From ddec111da6e2a822295b5a672f029b5bc2646c26 Mon Sep 17 00:00:00 2001 From: Leonardo Date: Thu, 8 Aug 2024 00:23:54 -0300 Subject: [PATCH 01/26] qt_gui: Added feature to toggle (show/hide) game list view "Show Game List" button originally didn't have any action assigned to it, so this PR is supposed to implement the change that would make sense to it (even though I don't think anyone would be using this too much.) --- src/qt_gui/main_window.cpp | 11 +++++++++++ src/qt_gui/main_window.h | 1 + 2 files changed, 12 insertions(+) diff --git a/src/qt_gui/main_window.cpp b/src/qt_gui/main_window.cpp index b3778be0..fe320bc6 100644 --- a/src/qt_gui/main_window.cpp +++ b/src/qt_gui/main_window.cpp @@ -162,6 +162,7 @@ void MainWindow::CreateConnects() { connect(ui->mw_searchbar, &QLineEdit::textChanged, this, &MainWindow::SearchGameTable); connect(ui->exitAct, &QAction::triggered, this, &QWidget::close); connect(ui->refreshGameListAct, &QAction::triggered, this, &MainWindow::RefreshGameTable); + connect(ui->showGameListAct, &QAction::triggered, this, &MainWindow::ShowGameList); connect(this, &MainWindow::ExtractionFinished, this, &MainWindow::RefreshGameTable); connect(ui->sizeSlider, &QSlider::valueChanged, this, [this](int value) { @@ -406,6 +407,16 @@ void MainWindow::SearchGameTable(const QString& text) { } } +void MainWindow::ShowGameList() { + if (ui->showGameListAct->isChecked()){ + RefreshGameTable(); + } + else { + m_game_grid_frame->clearContents(); + m_game_list_frame->clearContents(); + } +}; + void MainWindow::RefreshGameTable() { // m_game_info->m_games.clear(); m_game_info->GetGameInfo(this); diff --git a/src/qt_gui/main_window.h b/src/qt_gui/main_window.h index d1ef48dc..8bfcda5e 100644 --- a/src/qt_gui/main_window.h +++ b/src/qt_gui/main_window.h @@ -44,6 +44,7 @@ private Q_SLOTS: void ConfigureGuiFromSettings(); void SaveWindowState() const; void SearchGameTable(const QString& text); + void ShowGameList(); void RefreshGameTable(); void HandleResize(QResizeEvent* event); From 4375e6fa3afc4109ce3cfce1d176634082cd07ed Mon Sep 17 00:00:00 2001 From: Leonardo Date: Thu, 8 Aug 2024 18:30:58 -0300 Subject: [PATCH 02/26] Fixed if else formatting --- src/qt_gui/main_window.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qt_gui/main_window.cpp b/src/qt_gui/main_window.cpp index fe320bc6..6b732968 100644 --- a/src/qt_gui/main_window.cpp +++ b/src/qt_gui/main_window.cpp @@ -410,7 +410,7 @@ void MainWindow::SearchGameTable(const QString& text) { void MainWindow::ShowGameList() { if (ui->showGameListAct->isChecked()){ RefreshGameTable(); - } + } else { else { m_game_grid_frame->clearContents(); m_game_list_frame->clearContents(); From 0d56be629b8ed1fa5ee11eb06b7af018f3b8c429 Mon Sep 17 00:00:00 2001 From: Leonardo Date: Thu, 8 Aug 2024 18:33:06 -0300 Subject: [PATCH 03/26] Removed else that duplicated while commiting the typo fixes --- src/qt_gui/main_window.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/qt_gui/main_window.cpp b/src/qt_gui/main_window.cpp index 6b732968..1273780f 100644 --- a/src/qt_gui/main_window.cpp +++ b/src/qt_gui/main_window.cpp @@ -411,7 +411,6 @@ void MainWindow::ShowGameList() { if (ui->showGameListAct->isChecked()){ RefreshGameTable(); } else { - else { m_game_grid_frame->clearContents(); m_game_list_frame->clearContents(); } From 14947232a76d88760536494d2ea5d41d774b1515 Mon Sep 17 00:00:00 2001 From: Leonardo <58894431+notgonnaleo@users.noreply.github.com> Date: Fri, 9 Aug 2024 09:24:14 -0300 Subject: [PATCH 04/26] Fixed coding style again on the if brackets --- src/qt_gui/main_window.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qt_gui/main_window.cpp b/src/qt_gui/main_window.cpp index 1273780f..94800828 100644 --- a/src/qt_gui/main_window.cpp +++ b/src/qt_gui/main_window.cpp @@ -408,7 +408,7 @@ void MainWindow::SearchGameTable(const QString& text) { } void MainWindow::ShowGameList() { - if (ui->showGameListAct->isChecked()){ + if (ui->showGameListAct->isChecked()) { RefreshGameTable(); } else { m_game_grid_frame->clearContents(); From 516a3e71049c5df38d052489b402e9012e6069fa Mon Sep 17 00:00:00 2001 From: bax-cz Date: Mon, 19 Aug 2024 13:14:14 +0200 Subject: [PATCH 05/26] PlayGo: basic implementation, credits to red-prig --- src/core/file_format/playgo_chunk.cpp | 73 ++++++- src/core/file_format/playgo_chunk.h | 117 ++++++++++- src/core/libraries/playgo/playgo.cpp | 288 +++++++++++++++++++++++--- src/core/libraries/playgo/playgo.h | 15 +- src/emulator.cpp | 7 +- 5 files changed, 446 insertions(+), 54 deletions(-) diff --git a/src/core/file_format/playgo_chunk.cpp b/src/core/file_format/playgo_chunk.cpp index 43d8a4de..a9bea4a7 100644 --- a/src/core/file_format/playgo_chunk.cpp +++ b/src/core/file_format/playgo_chunk.cpp @@ -1,16 +1,75 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include "common/io_file.h" - #include "playgo_chunk.h" -bool PlaygoChunk::Open(const std::filesystem::path& filepath) { +bool PlaygoFile::Open(const std::filesystem::path& filepath) { Common::FS::IOFile file(filepath, Common::FS::FileAccessMode::Read); - if (!file.IsOpen()) { - return false; + if (file.IsOpen()) { + file.Read(playgoHeader); + if (LoadChunks(file)) { + return true; + } } - file.Read(playgoHeader); + return false; +} - return true; +bool PlaygoFile::LoadChunks(const Common::FS::IOFile& file) { + if (file.IsOpen()) { + if (playgoHeader.magic == PLAYGO_MAGIC) { + bool ret = true; + + std::string chunk_attrs_data, chunk_mchunks_data, chunk_labels_data, mchunk_attrs_data; + ret = ret && load_chunk_data(file, playgoHeader.chunk_attrs, chunk_attrs_data); + ret = ret && load_chunk_data(file, playgoHeader.chunk_mchunks, chunk_mchunks_data); + ret = ret && load_chunk_data(file, playgoHeader.chunk_labels, chunk_labels_data); + ret = ret && load_chunk_data(file, playgoHeader.mchunk_attrs, mchunk_attrs_data); + + if (ret) { + chunks.resize(playgoHeader.chunk_count); + + auto chunk_attrs = + reinterpret_cast(&chunk_attrs_data[0]); + auto chunk_mchunks = reinterpret_cast(&chunk_mchunks_data[0]); + auto chunk_labels = reinterpret_cast(&chunk_labels_data[0]); + auto mchunk_attrs = + reinterpret_cast(&mchunk_attrs_data[0]); + + for (u16 i = 0; i < playgoHeader.chunk_count; i++) { + chunks[i].req_locus = chunk_attrs[i].req_locus; + chunks[i].language_mask = chunk_attrs[i].language_mask; + chunks[i].label_name = std::string(chunk_labels + chunk_attrs[i].label_offset); + + u64 total_size = 0; + u16 mchunk_count = chunk_attrs[i].mchunk_count; + if (mchunk_count != 0) { + auto mchunks = reinterpret_cast( + ((u8*)chunk_mchunks + chunk_attrs[i].mchunks_offset)); + for (u16 j = 0; j < mchunk_count; j++) { + u16 mchunk_id = mchunks[j]; + total_size += mchunk_attrs[mchunk_id].size.size; + } + } + chunks[i].total_size = total_size; + } + } + + return ret; + } + } + return false; +} + +bool PlaygoFile::load_chunk_data(const Common::FS::IOFile& file, const chunk_t& chunk, + std::string& data) { + if (file.IsOpen()) { + if (file.Seek(chunk.offset)) { + data.resize(chunk.length); + if (data.size() == chunk.length) { + file.ReadRaw(&data[0], chunk.length); + return true; + } + } + } + return false; } \ No newline at end of file diff --git a/src/core/file_format/playgo_chunk.h b/src/core/file_format/playgo_chunk.h index d17d24bf..275312b1 100644 --- a/src/core/file_format/playgo_chunk.h +++ b/src/core/file_format/playgo_chunk.h @@ -3,29 +3,128 @@ #pragma once #include -#include "common/types.h" +#include +#include "common/io_file.h" +#include "core/libraries/playgo/playgo_types.h" + +constexpr u32 PLAYGO_MAGIC = 0x6F676C70; + +struct chunk_t { + u32 offset; + u32 length; +} __attribute__((packed)); struct PlaygoHeader { u32 magic; u16 version_major; u16 version_minor; - u16 image_count; + u16 image_count; // [0;1] + u16 chunk_count; // [0;1000] + u16 mchunk_count; // [0;8000] + u16 scenario_count; // [0;32] + + u32 file_size; + u16 default_scenario_id; + u16 attrib; + u32 sdk_version; + u16 disc_count; // [0;2] (if equals to 0 then disc count = 1) + u16 layer_bmp; + + u8 reserved[32]; + char content_id[128]; + + chunk_t chunk_attrs; // [0;32000] + chunk_t chunk_mchunks; + chunk_t chunk_labels; // [0;16000] + chunk_t mchunk_attrs; // [0;12800] + chunk_t scenario_attrs; // [0;1024] + chunk_t scenario_chunks; + chunk_t scenario_labels; + chunk_t inner_mchunk_attrs; // [0;12800] +} __attribute__((packed)); + +struct playgo_scenario_attr_entry_t { + u8 _type; + u8 _unk[19]; + u16 initial_chunk_count; u16 chunk_count; + u32 chunks_offset; //<-scenario_chunks + u32 label_offset; //<-scenario_labels +} __attribute__((packed)); + +struct image_disc_layer_no_t { + u8 layer_no : 2; + u8 disc_no : 2; + u8 image_no : 4; +} __attribute__((packed)); + +struct playgo_chunk_attr_entry_t { + u8 flag; + image_disc_layer_no_t image_disc_layer_no; + u8 req_locus; + u8 unk[11]; u16 mchunk_count; - u16 scenario_count; - // TODO fill the rest -}; -class PlaygoChunk { + u64 language_mask; + u32 mchunks_offset; //<-chunk_mchunks + u32 label_offset; //<-chunk_labels +} __attribute__((packed)); + +struct playgo_chunk_loc_t { + u64 offset : 48; + u64 _align1 : 8; + u64 image_no : 4; + u64 _align2 : 4; +} __attribute__((packed)); + +struct playgo_chunk_size_t { + u64 size : 48; + u64 _align : 16; +} __attribute__((packed)); + +struct playgo_mchunk_attr_entry_t { + playgo_chunk_loc_t loc; + playgo_chunk_size_t size; +} __attribute__((packed)); + +struct PlaygoChunk { + u64 req_locus; + u64 language_mask; + u64 total_size; + std::string label_name; +} __attribute__((packed)); + +class PlaygoFile { public: - PlaygoChunk() = default; - ~PlaygoChunk() = default; + bool initialized; + OrbisPlayGoHandle handle; + OrbisPlayGoChunkId id; + OrbisPlayGoLocus locus; + OrbisPlayGoInstallSpeed speed; + s64 speed_tick; + OrbisPlayGoEta eta; + OrbisPlayGoLanguageMask langMask; + std::vector chunks; + +public: + PlaygoFile() + : initialized(false), handle(0), id(0), locus(0), speed(ORBIS_PLAYGO_INSTALL_SPEED_TRICKLE), + speed_tick(0), eta(0), langMask(0), playgoHeader{0} {} + ~PlaygoFile() = default; bool Open(const std::filesystem::path& filepath); - PlaygoHeader GetPlaygoHeader() { + bool LoadChunks(const Common::FS::IOFile& file); + PlaygoHeader& GetPlaygoHeader() { return playgoHeader; } + std::mutex& GetSpeedMutex() { + return speed_mutex; + } + +private: + bool load_chunk_data(const Common::FS::IOFile& file, const chunk_t& chunk, std::string& data); private: PlaygoHeader playgoHeader; + std::mutex speed_mutex; }; \ No newline at end of file diff --git a/src/core/libraries/playgo/playgo.cpp b/src/core/libraries/playgo/playgo.cpp index af98253f..fbd4ca06 100644 --- a/src/core/libraries/playgo/playgo.cpp +++ b/src/core/libraries/playgo/playgo.cpp @@ -6,6 +6,7 @@ #include "common/singleton.h" #include "core/libraries/error_codes.h" #include "core/libraries/libs.h" +#include "core/libraries/system/systemservice.h" #include "playgo.h" namespace Libraries::PlayGo { @@ -20,42 +21,129 @@ s32 PS4_SYSV_ABI sceDbgPlayGoSnapshot() { return ORBIS_OK; } -s32 PS4_SYSV_ABI scePlayGoClose() { - LOG_ERROR(Lib_PlayGo, "(STUBBED)called"); +s32 PS4_SYSV_ABI scePlayGoClose(OrbisPlayGoHandle handle) { + LOG_INFO(Lib_PlayGo, "called"); + + auto* playgo = Common::Singleton::Instance(); + + if (handle != 1) + return ORBIS_PLAYGO_ERROR_BAD_HANDLE; + if (!playgo->initialized) + return ORBIS_PLAYGO_ERROR_NOT_INITIALIZED; return ORBIS_OK; } -s32 PS4_SYSV_ABI scePlayGoGetChunkId() { - LOG_ERROR(Lib_PlayGo, "(STUBBED)called"); +s32 PS4_SYSV_ABI scePlayGoGetChunkId(OrbisPlayGoHandle handle, OrbisPlayGoChunkId* outChunkIdList, + u32 numberOfEntries, u32* outEntries) { + LOG_INFO(Lib_PlayGo, "called"); + auto* playgo = Common::Singleton::Instance(); + + if (handle != 1) + return ORBIS_PLAYGO_ERROR_BAD_HANDLE; + if (outEntries == nullptr) + return ORBIS_PLAYGO_ERROR_BAD_POINTER; + if (outChunkIdList != nullptr && numberOfEntries == 0) + return ORBIS_PLAYGO_ERROR_BAD_SIZE; + + if (playgo->GetPlaygoHeader().file_size == 0) { + *outEntries = 0; + } else { + if (outChunkIdList == nullptr) { + *outEntries = playgo->chunks.size(); + } else { + if (numberOfEntries > playgo->chunks.size()) { + numberOfEntries = playgo->chunks.size(); + } + + if (numberOfEntries != 0) { + for (u32 i = 0; i < numberOfEntries; i++) { + outChunkIdList[i] = i; + } + *outEntries = numberOfEntries; + } + } + } return ORBIS_OK; } -s32 PS4_SYSV_ABI scePlayGoGetEta() { - LOG_ERROR(Lib_PlayGo, "(STUBBED)called"); +s32 PS4_SYSV_ABI scePlayGoGetEta(OrbisPlayGoHandle handle, const OrbisPlayGoChunkId* chunkIds, + u32 numberOfEntries, OrbisPlayGoEta* outEta) { + LOG_INFO(Lib_PlayGo, "called"); + if (handle != 1) + return ORBIS_PLAYGO_ERROR_BAD_HANDLE; + if (chunkIds == nullptr || outEta == nullptr) + return ORBIS_PLAYGO_ERROR_BAD_POINTER; + if (numberOfEntries == 0) + return ORBIS_PLAYGO_ERROR_BAD_SIZE; + + *outEta = 0; // all is loaded return ORBIS_OK; } -s32 PS4_SYSV_ABI scePlayGoGetInstallSpeed() { - LOG_ERROR(Lib_PlayGo, "(STUBBED)called"); +s32 PS4_SYSV_ABI scePlayGoGetInstallSpeed(OrbisPlayGoHandle handle, + OrbisPlayGoInstallSpeed* outSpeed) { + LOG_INFO(Lib_PlayGo, "called"); + + auto* playgo = Common::Singleton::Instance(); + + if (handle != 1) + return ORBIS_PLAYGO_ERROR_BAD_HANDLE; + if (outSpeed == nullptr) + return ORBIS_PLAYGO_ERROR_BAD_POINTER; + if (!playgo->initialized) + return ORBIS_PLAYGO_ERROR_NOT_INITIALIZED; + + std::scoped_lock lk{playgo->GetSpeedMutex()}; + + if (playgo->speed == 0) { + using namespace std::chrono; + if ((duration_cast(steady_clock::now().time_since_epoch()).count() - + playgo->speed_tick) > 30 * 1000) { // 30sec + playgo->speed = ORBIS_PLAYGO_INSTALL_SPEED_TRICKLE; + } + } + *outSpeed = playgo->speed; + return ORBIS_OK; } s32 PS4_SYSV_ABI scePlayGoGetLanguageMask(OrbisPlayGoHandle handle, - OrbisPlayGoLanguageMask* languageMask) { - LOG_ERROR(Lib_PlayGo, "(STUBBED)called"); - *languageMask = 1; // En, todo; + OrbisPlayGoLanguageMask* outLanguageMask) { + LOG_INFO(Lib_PlayGo, "called"); + + auto* playgo = Common::Singleton::Instance(); + + if (handle != 1) + return ORBIS_PLAYGO_ERROR_BAD_HANDLE; + if (outLanguageMask == nullptr) + return ORBIS_PLAYGO_ERROR_BAD_POINTER; + if (!playgo->initialized) + return ORBIS_PLAYGO_ERROR_NOT_INITIALIZED; + + *outLanguageMask = playgo->langMask; return ORBIS_OK; } s32 PS4_SYSV_ABI scePlayGoGetLocus(OrbisPlayGoHandle handle, const OrbisPlayGoChunkId* chunkIds, uint32_t numberOfEntries, OrbisPlayGoLocus* outLoci) { - LOG_ERROR(Lib_PlayGo, "(STUBBED)called handle = {}, chunkIds = {}, numberOfEntries = {}", + LOG_INFO(Lib_PlayGo, "called handle = {}, chunkIds = {}, numberOfEntries = {}", handle, *chunkIds, numberOfEntries); - auto* playgo = Common::Singleton::Instance(); + auto* playgo = Common::Singleton::Instance(); - for (uint32_t i = 0; i < numberOfEntries; i++) { - if (chunkIds[i] <= playgo->GetPlaygoHeader().mchunk_count) { + if (handle != 1) + return ORBIS_PLAYGO_ERROR_BAD_HANDLE; + if (chunkIds == nullptr || outLoci == nullptr) + return ORBIS_PLAYGO_ERROR_BAD_POINTER; + if (numberOfEntries == 0) + return ORBIS_PLAYGO_ERROR_BAD_SIZE; + if (!playgo->initialized) + return ORBIS_PLAYGO_ERROR_NOT_INITIALIZED; + if (playgo->GetPlaygoHeader().file_size == 0) + return ORBIS_PLAYGO_ERROR_NOT_SUPPORT_PLAYGO; + + for (uint32_t i = 0; i < numberOfEntries; i++) { + if (chunkIds[i] <= playgo->chunks.size()) { outLoci[i] = OrbisPlayGoLocusValue::ORBIS_PLAYGO_LOCUS_LOCAL_FAST; } else { outLoci[i] = ORBIS_PLAYGO_LOCUS_NOT_DOWNLOADED; @@ -67,64 +155,204 @@ s32 PS4_SYSV_ABI scePlayGoGetLocus(OrbisPlayGoHandle handle, const OrbisPlayGoCh s32 PS4_SYSV_ABI scePlayGoGetProgress(OrbisPlayGoHandle handle, const OrbisPlayGoChunkId* chunkIds, uint32_t numberOfEntries, OrbisPlayGoProgress* outProgress) { - LOG_ERROR(Lib_PlayGo, "(STUBBED)called handle = {}, chunkIds = {}, numberOfEntries = {}", + LOG_INFO(Lib_PlayGo, "called handle = {}, chunkIds = {}, numberOfEntries = {}", handle, *chunkIds, numberOfEntries); - outProgress->progressSize = 0x10000; // todo? - outProgress->totalSize = 0x10000; + + auto* playgo = Common::Singleton::Instance(); + + if (handle != 1) + return ORBIS_PLAYGO_ERROR_BAD_HANDLE; + if (chunkIds == nullptr || outProgress == nullptr) + return ORBIS_PLAYGO_ERROR_BAD_POINTER; + if (numberOfEntries == 0) + return ORBIS_PLAYGO_ERROR_BAD_SIZE; + if (!playgo->initialized) + return ORBIS_PLAYGO_ERROR_NOT_INITIALIZED; + if (playgo->GetPlaygoHeader().file_size == 0) + return ORBIS_PLAYGO_ERROR_BAD_CHUNK_ID; + + outProgress->progressSize = 0; + outProgress->totalSize = 0; + + u64 total_size = 0; + for (u32 i = 0; i < numberOfEntries; i++) { + u32 chunk_id = chunkIds[i]; + if (chunk_id < playgo->chunks.size()) { + total_size += playgo->chunks[chunk_id].total_size; + } else { + return ORBIS_PLAYGO_ERROR_BAD_CHUNK_ID; + } + } + + outProgress->progressSize = total_size; + outProgress->totalSize = total_size; + return ORBIS_OK; } s32 PS4_SYSV_ABI scePlayGoGetToDoList(OrbisPlayGoHandle handle, OrbisPlayGoToDo* outTodoList, u32 numberOfEntries, u32* outEntries) { - LOG_ERROR(Lib_PlayGo, "(STUBBED)called handle = {} numberOfEntries = {}", handle, + LOG_INFO(Lib_PlayGo, "called handle = {} numberOfEntries = {}", handle, numberOfEntries); + + auto* playgo = Common::Singleton::Instance(); + if (handle != 1) return ORBIS_PLAYGO_ERROR_BAD_HANDLE; - if (outTodoList == nullptr) + if (outTodoList == nullptr || outEntries == nullptr) return ORBIS_PLAYGO_ERROR_BAD_POINTER; + if (numberOfEntries == 0) + return ORBIS_PLAYGO_ERROR_BAD_SIZE; + if (!playgo->initialized) + return ORBIS_PLAYGO_ERROR_NOT_INITIALIZED; *outEntries = 0; // nothing to do return ORBIS_OK; } +int scePlayGoConvertLanguage(int systemLang) { + if (systemLang >= 0 && systemLang < 48) { + return (1 << (64 - systemLang - 1)); + } else { + return 0; + } +} + s32 PS4_SYSV_ABI scePlayGoInitialize(OrbisPlayGoInitParams* param) { + LOG_INFO(Lib_PlayGo, "called, bufSize = {}", param->bufSize); if (param->bufAddr == nullptr) return ORBIS_PLAYGO_ERROR_BAD_POINTER; if (param->bufSize < 0x200000) return ORBIS_PLAYGO_ERROR_BAD_SIZE; - LOG_INFO(Lib_PlayGo, "(STUBBED)called, bufSize = {}", param->bufSize); + + auto* playgo = Common::Singleton::Instance(); + + if (!playgo->initialized) { + using namespace SystemService; + // get system lang + int systemLang = 0; + sceSystemServiceParamGetInt(ORBIS_SYSTEM_SERVICE_PARAM_ID_LANG, &systemLang); + playgo->langMask = scePlayGoConvertLanguage(systemLang); + playgo->initialized = true; + } + else { + return ORBIS_PLAYGO_ERROR_ALREADY_INITIALIZED; + } return ORBIS_OK; } s32 PS4_SYSV_ABI scePlayGoOpen(OrbisPlayGoHandle* outHandle, const void* param) { - *outHandle = 1; - LOG_INFO(Lib_PlayGo, "(STUBBED)called"); + LOG_INFO(Lib_PlayGo, "called"); + + auto* playgo = Common::Singleton::Instance(); + + if (outHandle == nullptr) + return ORBIS_PLAYGO_ERROR_BAD_POINTER; + if (param) + return ORBIS_PLAYGO_ERROR_INVALID_ARGUMENT; + if (!playgo->initialized) + return ORBIS_PLAYGO_ERROR_NOT_INITIALIZED; + if (playgo->GetPlaygoHeader().file_size == 0) + return ORBIS_PLAYGO_ERROR_NOT_SUPPORT_PLAYGO; + + playgo->handle = *outHandle = 1; return ORBIS_OK; } -s32 PS4_SYSV_ABI scePlayGoPrefetch() { - LOG_ERROR(Lib_PlayGo, "(STUBBED)called"); +s32 PS4_SYSV_ABI scePlayGoPrefetch(OrbisPlayGoHandle handle, const OrbisPlayGoChunkId* chunkIds, + u32 numberOfEntries, OrbisPlayGoLocus minimumLocus) { + LOG_INFO(Lib_PlayGo, "called"); + + auto* playgo = Common::Singleton::Instance(); + + if (handle != 1) + return ORBIS_PLAYGO_ERROR_BAD_HANDLE; + if (chunkIds == nullptr) + return ORBIS_PLAYGO_ERROR_BAD_POINTER; + if (numberOfEntries == 0) + return ORBIS_PLAYGO_ERROR_BAD_SIZE; + if (!playgo->initialized) + return ORBIS_PLAYGO_ERROR_NOT_INITIALIZED; + + switch (minimumLocus) { + case ORBIS_PLAYGO_LOCUS_NOT_DOWNLOADED: + case ORBIS_PLAYGO_LOCUS_LOCAL_SLOW: + case ORBIS_PLAYGO_LOCUS_LOCAL_FAST: + break; + default: + return ORBIS_PLAYGO_ERROR_BAD_LOCUS; + } return ORBIS_OK; } -s32 PS4_SYSV_ABI scePlayGoSetInstallSpeed() { - LOG_ERROR(Lib_PlayGo, "(STUBBED)called"); +s32 PS4_SYSV_ABI scePlayGoSetInstallSpeed(OrbisPlayGoHandle handle, OrbisPlayGoInstallSpeed speed) { + LOG_INFO(Lib_PlayGo, "called"); + + auto* playgo = Common::Singleton::Instance(); + + if (handle != 1) + return ORBIS_PLAYGO_ERROR_BAD_HANDLE; + if (!playgo->initialized) + return ORBIS_PLAYGO_ERROR_NOT_INITIALIZED; + + switch (speed) { + case ORBIS_PLAYGO_INSTALL_SPEED_SUSPENDED: + case ORBIS_PLAYGO_INSTALL_SPEED_TRICKLE: + case ORBIS_PLAYGO_INSTALL_SPEED_FULL: + break; + default: + return ORBIS_PLAYGO_ERROR_INVALID_ARGUMENT; + } + + std::scoped_lock lk{playgo->GetSpeedMutex()}; + + using namespace std::chrono; + playgo->speed = speed; + playgo->speed_tick = + duration_cast(steady_clock::now().time_since_epoch()).count(); + return ORBIS_OK; } s32 PS4_SYSV_ABI scePlayGoSetLanguageMask(OrbisPlayGoHandle handle, OrbisPlayGoLanguageMask languageMask) { - LOG_ERROR(Lib_PlayGo, "(STUBBED)called"); + LOG_INFO(Lib_PlayGo, "called"); + auto* playgo = Common::Singleton::Instance(); + + if (handle != 1) + return ORBIS_PLAYGO_ERROR_BAD_HANDLE; + if (!playgo->initialized) + return ORBIS_PLAYGO_ERROR_NOT_INITIALIZED; + + playgo->langMask = languageMask; return ORBIS_OK; } s32 PS4_SYSV_ABI scePlayGoSetToDoList(OrbisPlayGoHandle handle, const OrbisPlayGoToDo* todoList, uint32_t numberOfEntries) { - LOG_ERROR(Lib_PlayGo, "(STUBBED)called"); + LOG_INFO(Lib_PlayGo, "called"); + + auto* playgo = Common::Singleton::Instance(); + + if (handle != 1) + return ORBIS_PLAYGO_ERROR_BAD_HANDLE; + if (todoList == nullptr) + return ORBIS_PLAYGO_ERROR_BAD_POINTER; + if (numberOfEntries == 0) + return ORBIS_PLAYGO_ERROR_BAD_SIZE; + if (!playgo->initialized) + return ORBIS_PLAYGO_ERROR_NOT_INITIALIZED; return ORBIS_OK; } s32 PS4_SYSV_ABI scePlayGoTerminate() { - LOG_ERROR(Lib_PlayGo, "(STUBBED)called"); + LOG_INFO(Lib_PlayGo, "called"); + + auto* playgo = Common::Singleton::Instance(); + if (playgo->initialized) { + playgo->initialized = false; + } else { + return ORBIS_PLAYGO_ERROR_NOT_INITIALIZED; + } return ORBIS_OK; } diff --git a/src/core/libraries/playgo/playgo.h b/src/core/libraries/playgo/playgo.h index a6b8c91d..9b77bd7a 100644 --- a/src/core/libraries/playgo/playgo.h +++ b/src/core/libraries/playgo/playgo.h @@ -14,10 +14,12 @@ constexpr int shadMagic = 0x53484144; s32 PS4_SYSV_ABI sceDbgPlayGoRequestNextChunk(); s32 PS4_SYSV_ABI sceDbgPlayGoSnapshot(); -s32 PS4_SYSV_ABI scePlayGoClose(); -s32 PS4_SYSV_ABI scePlayGoGetChunkId(); -s32 PS4_SYSV_ABI scePlayGoGetEta(); -s32 PS4_SYSV_ABI scePlayGoGetInstallSpeed(); +s32 PS4_SYSV_ABI scePlayGoClose(OrbisPlayGoHandle handle); +s32 PS4_SYSV_ABI scePlayGoGetChunkId(OrbisPlayGoHandle handle, OrbisPlayGoChunkId* outChunkIdList, + u32 numberOfEntries, u32* outEntries); +s32 PS4_SYSV_ABI scePlayGoGetEta(OrbisPlayGoHandle handle, const OrbisPlayGoChunkId* chunkIds, + u32 numberOfEntries, OrbisPlayGoEta* outEta); +s32 PS4_SYSV_ABI scePlayGoGetInstallSpeed(OrbisPlayGoHandle handle, OrbisPlayGoInstallSpeed* outSpeed); s32 PS4_SYSV_ABI scePlayGoGetLanguageMask(OrbisPlayGoHandle handle, OrbisPlayGoLanguageMask* outLanguageMask); s32 PS4_SYSV_ABI scePlayGoGetLocus(OrbisPlayGoHandle handle, const OrbisPlayGoChunkId* chunkIds, @@ -28,8 +30,9 @@ s32 PS4_SYSV_ABI scePlayGoGetToDoList(OrbisPlayGoHandle handle, OrbisPlayGoToDo* u32 numberOfEntries, u32* outEntries); s32 PS4_SYSV_ABI scePlayGoInitialize(OrbisPlayGoInitParams* param); s32 PS4_SYSV_ABI scePlayGoOpen(OrbisPlayGoHandle* outHandle, const void* param); -s32 PS4_SYSV_ABI scePlayGoPrefetch(); -s32 PS4_SYSV_ABI scePlayGoSetInstallSpeed(); +s32 PS4_SYSV_ABI scePlayGoPrefetch(OrbisPlayGoHandle handle, const OrbisPlayGoChunkId* chunkIds, + u32 numberOfEntries, OrbisPlayGoLocus minimumLocus); +s32 PS4_SYSV_ABI scePlayGoSetInstallSpeed(OrbisPlayGoHandle handle, OrbisPlayGoInstallSpeed speed); s32 PS4_SYSV_ABI scePlayGoSetLanguageMask(OrbisPlayGoHandle handle, OrbisPlayGoLanguageMask languageMask); s32 PS4_SYSV_ABI scePlayGoSetToDoList(OrbisPlayGoHandle handle, const OrbisPlayGoToDo* todoList, diff --git a/src/emulator.cpp b/src/emulator.cpp index 12e89349..9cc7a130 100644 --- a/src/emulator.cpp +++ b/src/emulator.cpp @@ -91,8 +91,11 @@ void Emulator::Run(const std::filesystem::path& file) { app_version = param_sfo->GetString("APP_VER"); LOG_INFO(Loader, "Fw: {:#x} App Version: {}", fw_version, app_version); } else if (entry.path().filename() == "playgo-chunk.dat") { - auto* playgo = Common::Singleton::Instance(); - playgo->Open(sce_sys_folder.string() + "/playgo-chunk.dat"); + auto* playgo = Common::Singleton::Instance(); + auto filepath = sce_sys_folder / "playgo-chunk.dat"; + if (!playgo->Open(filepath)) { + LOG_ERROR(Loader, "PlayGo: unable to open file"); + } } else if (entry.path().filename() == "pic0.png" || entry.path().filename() == "pic1.png") { auto* splash = Common::Singleton::Instance(); From 9d45b991712a491cfef3fcea709411876eb468fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C2=A5IGA?= <164882787+Xphalnos@users.noreply.github.com> Date: Mon, 19 Aug 2024 17:42:23 +0200 Subject: [PATCH 06/26] Adding icons to categories (#479) --- .reuse/dep5 | 1 + src/images/about_icon.png | Bin 0 -> 8462 bytes src/qt_gui/main_window.cpp | 4 ++++ src/qt_gui/main_window_ui.h | 4 ++++ src/shadps4.qrc | 1 + 5 files changed, 10 insertions(+) create mode 100644 src/images/about_icon.png diff --git a/.reuse/dep5 b/.reuse/dep5 index a80001f8..0140c0c0 100644 --- a/.reuse/dep5 +++ b/.reuse/dep5 @@ -15,6 +15,7 @@ Files: CMakeSettings.json documents/Screenshots/Undertale.png documents/Screenshots/We are DOOMED.png scripts/ps4_names.txt + src/images/about_icon.png src/images/controller_icon.png src/images/exit_icon.png src/images/file_icon.png diff --git a/src/images/about_icon.png b/src/images/about_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..bbb517982e77db1b4ca6ee72633932c2d8530e80 GIT binary patch literal 8462 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 literal 0 HcmV?d00001 diff --git a/src/qt_gui/main_window.cpp b/src/qt_gui/main_window.cpp index 653987b5..d00a6ef6 100644 --- a/src/qt_gui/main_window.cpp +++ b/src/qt_gui/main_window.cpp @@ -702,7 +702,9 @@ QIcon MainWindow::RecolorIcon(const QIcon& icon, bool isWhite) { void MainWindow::SetUiIcons(bool isWhite) { ui->bootInstallPkgAct->setIcon(RecolorIcon(ui->bootInstallPkgAct->icon(), isWhite)); + ui->bootGameAct->setIcon(RecolorIcon(ui->bootGameAct->icon(), isWhite)); ui->exitAct->setIcon(RecolorIcon(ui->exitAct->icon(), isWhite)); + ui->aboutAct->setIcon(RecolorIcon(ui->aboutAct->icon(), isWhite)); ui->setlistModeListAct->setIcon(RecolorIcon(ui->setlistModeListAct->icon(), isWhite)); ui->setlistModeGridAct->setIcon(RecolorIcon(ui->setlistModeGridAct->icon(), isWhite)); ui->gameInstallPathAct->setIcon(RecolorIcon(ui->gameInstallPathAct->icon(), isWhite)); @@ -716,6 +718,8 @@ void MainWindow::SetUiIcons(bool isWhite) { ui->refreshGameListAct->setIcon(RecolorIcon(ui->refreshGameListAct->icon(), isWhite)); ui->menuGame_List_Mode->setIcon(RecolorIcon(ui->menuGame_List_Mode->icon(), isWhite)); ui->pkgViewerAct->setIcon(RecolorIcon(ui->pkgViewerAct->icon(), isWhite)); + ui->configureAct->setIcon(RecolorIcon(ui->configureAct->icon(), isWhite)); + ui->addElfFolderAct->setIcon(RecolorIcon(ui->addElfFolderAct->icon(), isWhite)); } void MainWindow::resizeEvent(QResizeEvent* event) { diff --git a/src/qt_gui/main_window_ui.h b/src/qt_gui/main_window_ui.h index f8de3076..7d0c58dd 100644 --- a/src/qt_gui/main_window_ui.h +++ b/src/qt_gui/main_window_ui.h @@ -98,8 +98,10 @@ public: bootInstallPkgAct->setIcon(QIcon(":images/file_icon.png")); bootGameAct = new QAction(MainWindow); bootGameAct->setObjectName("bootGameAct"); + bootGameAct->setIcon(QIcon(":images/play_icon.png")); addElfFolderAct = new QAction(MainWindow); addElfFolderAct->setObjectName("addElfFolderAct"); + addElfFolderAct->setIcon(QIcon(":images/folder_icon.png")); exitAct = new QAction(MainWindow); exitAct->setObjectName("exitAct"); exitAct->setIcon(QIcon(":images/exit_icon.png")); @@ -144,8 +146,10 @@ public: pkgViewerAct->setIcon(QIcon(":images/file_icon.png")); aboutAct = new QAction(MainWindow); aboutAct->setObjectName("aboutAct"); + aboutAct->setIcon(QIcon(":images/about_icon.png")); configureAct = new QAction(MainWindow); configureAct->setObjectName("configureAct"); + configureAct->setIcon(QIcon(":images/settings_icon.png")); setThemeDark = new QAction(MainWindow); setThemeDark->setObjectName("setThemeDark"); setThemeDark->setCheckable(true); diff --git a/src/shadps4.qrc b/src/shadps4.qrc index cdbae786..c22b837b 100644 --- a/src/shadps4.qrc +++ b/src/shadps4.qrc @@ -1,6 +1,7 @@ images/shadps4.ico + images/about_icon.png images/play_icon.png images/pause_icon.png images/stop_icon.png From 09da94b7b28a64c17a229324bc9a87aac5964b16 Mon Sep 17 00:00:00 2001 From: Random <28494085+Random06457@users.noreply.github.com> Date: Mon, 19 Aug 2024 17:45:42 +0200 Subject: [PATCH 07/26] fix gcc compilation error in vk_graphics_pipeline.cpp (#477) gcc fails to infer the type of the two parts of a ternary expression whose types are different but both contain an implicit cast operator to the same type --- .../renderer_vulkan/vk_graphics_pipeline.cpp | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp index 2d502737..04486290 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp @@ -173,16 +173,17 @@ GraphicsPipeline::GraphicsPipeline(const Instance& instance_, Scheduler& schedul }, .back{ .failOp = LiverpoolToVK::StencilOp(key.depth.backface_enable - ? key.stencil.stencil_fail_back - : key.stencil.stencil_fail_front), + ? key.stencil.stencil_fail_back.Value() + : key.stencil.stencil_fail_front.Value()), .passOp = LiverpoolToVK::StencilOp(key.depth.backface_enable - ? key.stencil.stencil_zpass_back - : key.stencil.stencil_zpass_front), + ? key.stencil.stencil_zpass_back.Value() + : key.stencil.stencil_zpass_front.Value()), .depthFailOp = LiverpoolToVK::StencilOp(key.depth.backface_enable - ? key.stencil.stencil_zfail_back - : key.stencil.stencil_zfail_front), - .compareOp = LiverpoolToVK::CompareOp( - key.depth.backface_enable ? key.depth.stencil_bf_func : key.depth.stencil_ref_func), + ? key.stencil.stencil_zfail_back.Value() + : key.stencil.stencil_zfail_front.Value()), + .compareOp = LiverpoolToVK::CompareOp(key.depth.backface_enable + ? key.depth.stencil_bf_func.Value() + : key.depth.stencil_ref_func.Value()), .compareMask = key.stencil_ref_back.stencil_mask, .writeMask = key.stencil_ref_back.stencil_write_mask, .reference = key.stencil_ref_back.stencil_test_val, From 8f7b3c2e8c0e7514b3a84f6e6e29dee0c4ae8e20 Mon Sep 17 00:00:00 2001 From: bax-cz Date: Mon, 19 Aug 2024 20:40:23 +0200 Subject: [PATCH 08/26] clang: fixed formatting --- src/core/file_format/playgo_chunk.cpp | 6 +++--- src/core/file_format/playgo_chunk.h | 6 +++--- src/core/libraries/playgo/playgo.cpp | 18 ++++++++---------- src/core/libraries/playgo/playgo.h | 3 ++- 4 files changed, 16 insertions(+), 17 deletions(-) diff --git a/src/core/file_format/playgo_chunk.cpp b/src/core/file_format/playgo_chunk.cpp index a9bea4a7..a430b1ac 100644 --- a/src/core/file_format/playgo_chunk.cpp +++ b/src/core/file_format/playgo_chunk.cpp @@ -27,7 +27,7 @@ bool PlaygoFile::LoadChunks(const Common::FS::IOFile& file) { if (ret) { chunks.resize(playgoHeader.chunk_count); - + auto chunk_attrs = reinterpret_cast(&chunk_attrs_data[0]); auto chunk_mchunks = reinterpret_cast(&chunk_mchunks_data[0]); @@ -61,7 +61,7 @@ bool PlaygoFile::LoadChunks(const Common::FS::IOFile& file) { } bool PlaygoFile::load_chunk_data(const Common::FS::IOFile& file, const chunk_t& chunk, - std::string& data) { + std::string& data) { if (file.IsOpen()) { if (file.Seek(chunk.offset)) { data.resize(chunk.length); @@ -70,6 +70,6 @@ bool PlaygoFile::load_chunk_data(const Common::FS::IOFile& file, const chunk_t& return true; } } - } + } return false; } \ No newline at end of file diff --git a/src/core/file_format/playgo_chunk.h b/src/core/file_format/playgo_chunk.h index 275312b1..4409d2d1 100644 --- a/src/core/file_format/playgo_chunk.h +++ b/src/core/file_format/playgo_chunk.h @@ -36,8 +36,8 @@ struct PlaygoHeader { chunk_t chunk_attrs; // [0;32000] chunk_t chunk_mchunks; - chunk_t chunk_labels; // [0;16000] - chunk_t mchunk_attrs; // [0;12800] + chunk_t chunk_labels; // [0;16000] + chunk_t mchunk_attrs; // [0;12800] chunk_t scenario_attrs; // [0;1024] chunk_t scenario_chunks; chunk_t scenario_labels; @@ -67,7 +67,7 @@ struct playgo_chunk_attr_entry_t { u16 mchunk_count; u64 language_mask; u32 mchunks_offset; //<-chunk_mchunks - u32 label_offset; //<-chunk_labels + u32 label_offset; //<-chunk_labels } __attribute__((packed)); struct playgo_chunk_loc_t { diff --git a/src/core/libraries/playgo/playgo.cpp b/src/core/libraries/playgo/playgo.cpp index fbd4ca06..66422dc2 100644 --- a/src/core/libraries/playgo/playgo.cpp +++ b/src/core/libraries/playgo/playgo.cpp @@ -103,7 +103,7 @@ s32 PS4_SYSV_ABI scePlayGoGetInstallSpeed(OrbisPlayGoHandle handle, } } *outSpeed = playgo->speed; - + return ORBIS_OK; } @@ -126,8 +126,8 @@ s32 PS4_SYSV_ABI scePlayGoGetLanguageMask(OrbisPlayGoHandle handle, s32 PS4_SYSV_ABI scePlayGoGetLocus(OrbisPlayGoHandle handle, const OrbisPlayGoChunkId* chunkIds, uint32_t numberOfEntries, OrbisPlayGoLocus* outLoci) { - LOG_INFO(Lib_PlayGo, "called handle = {}, chunkIds = {}, numberOfEntries = {}", - handle, *chunkIds, numberOfEntries); + LOG_INFO(Lib_PlayGo, "called handle = {}, chunkIds = {}, numberOfEntries = {}", handle, + *chunkIds, numberOfEntries); auto* playgo = Common::Singleton::Instance(); @@ -142,7 +142,7 @@ s32 PS4_SYSV_ABI scePlayGoGetLocus(OrbisPlayGoHandle handle, const OrbisPlayGoCh if (playgo->GetPlaygoHeader().file_size == 0) return ORBIS_PLAYGO_ERROR_NOT_SUPPORT_PLAYGO; - for (uint32_t i = 0; i < numberOfEntries; i++) { + for (uint32_t i = 0; i < numberOfEntries; i++) { if (chunkIds[i] <= playgo->chunks.size()) { outLoci[i] = OrbisPlayGoLocusValue::ORBIS_PLAYGO_LOCUS_LOCAL_FAST; } else { @@ -155,8 +155,8 @@ s32 PS4_SYSV_ABI scePlayGoGetLocus(OrbisPlayGoHandle handle, const OrbisPlayGoCh s32 PS4_SYSV_ABI scePlayGoGetProgress(OrbisPlayGoHandle handle, const OrbisPlayGoChunkId* chunkIds, uint32_t numberOfEntries, OrbisPlayGoProgress* outProgress) { - LOG_INFO(Lib_PlayGo, "called handle = {}, chunkIds = {}, numberOfEntries = {}", - handle, *chunkIds, numberOfEntries); + LOG_INFO(Lib_PlayGo, "called handle = {}, chunkIds = {}, numberOfEntries = {}", handle, + *chunkIds, numberOfEntries); auto* playgo = Common::Singleton::Instance(); @@ -192,8 +192,7 @@ s32 PS4_SYSV_ABI scePlayGoGetProgress(OrbisPlayGoHandle handle, const OrbisPlayG s32 PS4_SYSV_ABI scePlayGoGetToDoList(OrbisPlayGoHandle handle, OrbisPlayGoToDo* outTodoList, u32 numberOfEntries, u32* outEntries) { - LOG_INFO(Lib_PlayGo, "called handle = {} numberOfEntries = {}", handle, - numberOfEntries); + LOG_INFO(Lib_PlayGo, "called handle = {} numberOfEntries = {}", handle, numberOfEntries); auto* playgo = Common::Singleton::Instance(); @@ -233,8 +232,7 @@ s32 PS4_SYSV_ABI scePlayGoInitialize(OrbisPlayGoInitParams* param) { sceSystemServiceParamGetInt(ORBIS_SYSTEM_SERVICE_PARAM_ID_LANG, &systemLang); playgo->langMask = scePlayGoConvertLanguage(systemLang); playgo->initialized = true; - } - else { + } else { return ORBIS_PLAYGO_ERROR_ALREADY_INITIALIZED; } return ORBIS_OK; diff --git a/src/core/libraries/playgo/playgo.h b/src/core/libraries/playgo/playgo.h index 9b77bd7a..f5ae1baa 100644 --- a/src/core/libraries/playgo/playgo.h +++ b/src/core/libraries/playgo/playgo.h @@ -19,7 +19,8 @@ s32 PS4_SYSV_ABI scePlayGoGetChunkId(OrbisPlayGoHandle handle, OrbisPlayGoChunkI u32 numberOfEntries, u32* outEntries); s32 PS4_SYSV_ABI scePlayGoGetEta(OrbisPlayGoHandle handle, const OrbisPlayGoChunkId* chunkIds, u32 numberOfEntries, OrbisPlayGoEta* outEta); -s32 PS4_SYSV_ABI scePlayGoGetInstallSpeed(OrbisPlayGoHandle handle, OrbisPlayGoInstallSpeed* outSpeed); +s32 PS4_SYSV_ABI scePlayGoGetInstallSpeed(OrbisPlayGoHandle handle, + OrbisPlayGoInstallSpeed* outSpeed); s32 PS4_SYSV_ABI scePlayGoGetLanguageMask(OrbisPlayGoHandle handle, OrbisPlayGoLanguageMask* outLanguageMask); s32 PS4_SYSV_ABI scePlayGoGetLocus(OrbisPlayGoHandle handle, const OrbisPlayGoChunkId* chunkIds, From 0b3356bd1a9175632459cacee4dcace1b861a961 Mon Sep 17 00:00:00 2001 From: bax-cz Date: Tue, 20 Aug 2024 09:28:07 +0200 Subject: [PATCH 09/26] linux build fix --- src/core/file_format/playgo_chunk.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/core/file_format/playgo_chunk.h b/src/core/file_format/playgo_chunk.h index 4409d2d1..439e27d0 100644 --- a/src/core/file_format/playgo_chunk.h +++ b/src/core/file_format/playgo_chunk.h @@ -4,6 +4,7 @@ #pragma once #include #include +#include #include "common/io_file.h" #include "core/libraries/playgo/playgo_types.h" @@ -92,7 +93,7 @@ struct PlaygoChunk { u64 language_mask; u64 total_size; std::string label_name; -} __attribute__((packed)); +}; class PlaygoFile { public: @@ -127,4 +128,4 @@ private: private: PlaygoHeader playgoHeader; std::mutex speed_mutex; -}; \ No newline at end of file +}; From c60bfbe2a598ae852a8bd73036f60b057bd923a0 Mon Sep 17 00:00:00 2001 From: kotn3l <32578937+kotn3l@users.noreply.github.com> Date: Tue, 20 Aug 2024 14:39:56 +0200 Subject: [PATCH 10/26] Set game window size based on the config (in windowed mode) (#481) * Set windowed mode size based on config * fix formatting oops * emulator.cpp clang format fix (hopefully?) * formatting fix for real --- src/emulator.cpp | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/emulator.cpp b/src/emulator.cpp index 9cc7a130..37e227dd 100644 --- a/src/emulator.cpp +++ b/src/emulator.cpp @@ -34,9 +34,6 @@ Frontend::WindowSDL* g_window = nullptr; namespace Core { -static constexpr s32 WindowWidth = 1280; -static constexpr s32 WindowHeight = 720; - Emulator::Emulator() { // Read configuration file. const auto config_dir = Common::FS::GetUserPath(Common::FS::PathType::UserDir); @@ -117,8 +114,8 @@ void Emulator::Run(const std::filesystem::path& file) { window_title = fmt::format("shadPS4 v{} {} | {}", Common::VERSION, Common::g_scm_desc, game_title); } - window = - std::make_unique(WindowWidth, WindowHeight, controller, window_title); + window = std::make_unique( + Config::getScreenWidth(), Config::getScreenHeight(), controller, window_title); g_window = window.get(); From e070dab2f0b93cabec383d0b7aa1b789a9b5e52f Mon Sep 17 00:00:00 2001 From: jdp_ <42700985+jdpatdiscord@users.noreply.github.com> Date: Tue, 20 Aug 2024 12:26:35 -0400 Subject: [PATCH 11/26] Allow builds on MSYS2 & improve Windows build steps documentation --- CMakeLists.txt | 25 ++++++++++-- documents/building-windows.md | 74 ++++++++++++++++++++++++++++++----- 2 files changed, 85 insertions(+), 14 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 679325ec..7337d18b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -643,23 +643,40 @@ if (ENABLE_QT_GUI) endif() if (WIN32) - target_link_libraries(shadps4 PRIVATE mincore winpthreads clang_rt.builtins-x86_64.lib) - add_definitions(-D_CRT_SECURE_NO_WARNINGS -D_CRT_NONSTDC_NO_DEPRECATE -D_SCL_SECURE_NO_WARNINGS) + target_link_libraries(shadps4 PRIVATE mincore winpthreads) + + if (MSVC) + # MSVC likes putting opinions on what people can use, disable: + add_definitions(-D_CRT_SECURE_NO_WARNINGS -D_CRT_NONSTDC_NO_DEPRECATE -D_SCL_SECURE_NO_WARNINGS) + endif() + add_definitions(-DNOMINMAX -DWIN32_LEAN_AND_MEAN) + if (MSVC) # Needed for conflicts with time.h of windows.h add_definitions(-D_TIMESPEC_DEFINED) endif() + # Target Windows 10 RS5 add_definitions(-DNTDDI_VERSION=0x0A000006 -D_WIN32_WINNT=0x0A00 -DWINVER=0x0A00) - # Increase stack commit area - target_link_options(shadps4 PRIVATE /STACK:0x200000,0x200000) + + if (MSVC) + target_link_libraries(shadps4 PRIVATE clang_rt.builtins-x86_64.lib) + endif() + # Disable ASLR so we can reserve the user area if (MSVC) target_link_options(shadps4 PRIVATE /DYNAMICBASE:NO) else() target_link_options(shadps4 PRIVATE -Wl,--disable-dynamicbase) endif() + + # Increase stack commit area (Needed, otherwise there are crashes) + if (MSVC) + target_link_options(shadps4 PRIVATE /STACK:0x200000,0x200000) + else() + target_link_options(shadps4 PRIVATE -Wl,--stack,2097152) + endif() endif() if (WIN32) diff --git a/documents/building-windows.md b/documents/building-windows.md index e00ed90d..21c0c12b 100644 --- a/documents/building-windows.md +++ b/documents/building-windows.md @@ -5,21 +5,75 @@ SPDX-License-Identifier: GPL-2.0-or-later # Build shadPS4 for Windows -## Download Visual Studio Community 2022 +This tutorial reads as if you have none of the prerequisites already installed. If you do, just ignore the steps regarding installation. +If you are building to contribute to the project, please omit `--depth 1` from the git invokations. -Download link: [**Visual Studio 2022**](https://visualstudio.microsoft.com/vs/) +## Option 1: Visual Studio 2022 -## Requirements +### (Prerequisite) Download the Community edition from [**Visual Studio 2022**](https://visualstudio.microsoft.com/vs/) -### From Visual Studio Community +Once you are within the installer: +1. Select `Desktop development with C++` +2. Go to "Individual Components" tab +3. Make sure `C++ Clang Compiler for Windows`, `MSBuild support for LLVM` and `C++ CMake Tools for Windows` are selected +4. Continue the installation -- Desktop development with C++ +### (Prerequisite) Download [**Qt**](https://doc.qt.io/qt-6/get-and-install-qt.html) -### From individual components tab install +Beware, this requires you to create a Qt account. If you do not want to do this, please follow the MSYS2/MinGW compilation method instead. -- C++ Clang Compiler for Windows (17.0.3) -- MSBuild support for LLVM (Clang-cl) toolset +1. Select Qt for Visual Studio plugin +2. Select `msvc2019_64` option or similar. If you are on Windows on ARM / Qualcomm Snapdragon Elite X, select `msvc2019_arm64` -- ## Compiling +Go through the installation normally. If you do not know what components to select, just select the newest Qt version it gives you. +If you know what you are doing, you may unselect individual components that eat up too much disk space. -- Open Visual Studio Community and select the **x64-Clang-Release**, **x64-Clang-Debug** or **x64-Clang-RelWithDebInfo**. It should compile just fine. +Once you are finished, you will have to configure Qt within Visual Studio: +1. Tools -> Options -> Qt -> Versions +2. Add a new Qt version and navigate it to the correct folder. Should look like so: `C:\Qt\6.7.1\msvc2019_64` +3. Enable the default checkmark on the new version you just created. + +### (Prerequisite) Download [**Git for Windows**](https://git-scm.com/download/win) + +Go through the Git for Windows installation as normal + +### Compiling with Visual Studio GUI + +1. Open Git for Windows, navigate to a place where you want to store the shadPS4 source code folder +2. Run `git clone --depth 1 --recursive https://github.com/shadps4-emu/shadPS4` +3. Open up Visual Studio, select `Open a local folder` and select the folder with the shadPS4 source code. The folder should contain `CMakeLists.txt` +4. Build -> Build All + +## Option 2: MSYS2/MinGW + +### (Prerequisite) Download [**MSYS2**](https://www.msys2.org/) + +Go through the MSYS2 installation as normal + +If you are building to distribute, please omit `-DCMAKE_CXX_FLAGS="-O2 -march=native"` within the build configuration step. + +Normal x86-based computers, follow: +1. Open "MSYS2 MINGW64" from your new applications +2. Run `pacman -Syu`, let it complete; +3. Run `pacman -S --needed git mingw-w64-x86_64-binutils mingw-w64-x86_64-clang mingw-w64-x86_64-cmake mingw-w64-x86_64-ninja mingw-w64-x86_64-qt6-base` +4. Run `git clone --depth 1 --recursive https://github.com/shadps4-emu/shadPS4` +5. Run `cd shadPS4` +6. Run `cmake -S . -B build -DCMAKE_CXX_COMPILER="clang++.exe" -DCMAKE_C_COMPILER="clang.exe" -DCMAKE_CXX_FLAGS="-O2 -march=native"` +7. Run `cmake --build build` +8. To run the finished product, run `./build/shadPS4.exe` + +ARM64-based computers, follow: +1. Open "MSYS2 CLANGARM64" from your new applications +2. Run `pacman -Syu`, let it complete; +3. Run `pacman -S --needed git mingw-w64-clang-aarch64-binutils mingw-w64-clang-aarch64-clang mingw-w64-clang-aarch64-cmake mingw-w64-clang-aarch64-ninja mingw-w64-clang-aarch64-qt6-base` +4. Run `git clone --depth 1 --recursive https://github.com/shadps4-emu/shadPS4` +5. Run `cd shadPS4` +6. Run `cmake -S . -B build -DCMAKE_CXX_COMPILER="clang++.exe" -DCMAKE_C_COMPILER="clang.exe" -DCMAKE_CXX_FLAGS="-O2 -march=native"` +7. Run `cmake --build build` +8. To run the finished product, run `./build/shadPS4.exe` + +## Note on MSYS2 builds + +These builds may not be easily copyable to people who do not also have a MSYS2 installation. +If you want to distribute these builds, you need to copy over the correct DLLs into a distribution folder. +In order to run them, you must be within the MSYS2 shell environment. \ No newline at end of file From 34a1339a2ba2e42e02d183a4374906c9ff1295ff Mon Sep 17 00:00:00 2001 From: jdp_ <42700985+jdpatdiscord@users.noreply.github.com> Date: Tue, 20 Aug 2024 12:26:35 -0400 Subject: [PATCH 12/26] Allow builds on MSYS2 & improve Windows build steps documentation --- CMakeLists.txt | 25 ++++++++++-- documents/building-windows.md | 76 ++++++++++++++++++++++++++++++----- 2 files changed, 87 insertions(+), 14 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 679325ec..7337d18b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -643,23 +643,40 @@ if (ENABLE_QT_GUI) endif() if (WIN32) - target_link_libraries(shadps4 PRIVATE mincore winpthreads clang_rt.builtins-x86_64.lib) - add_definitions(-D_CRT_SECURE_NO_WARNINGS -D_CRT_NONSTDC_NO_DEPRECATE -D_SCL_SECURE_NO_WARNINGS) + target_link_libraries(shadps4 PRIVATE mincore winpthreads) + + if (MSVC) + # MSVC likes putting opinions on what people can use, disable: + add_definitions(-D_CRT_SECURE_NO_WARNINGS -D_CRT_NONSTDC_NO_DEPRECATE -D_SCL_SECURE_NO_WARNINGS) + endif() + add_definitions(-DNOMINMAX -DWIN32_LEAN_AND_MEAN) + if (MSVC) # Needed for conflicts with time.h of windows.h add_definitions(-D_TIMESPEC_DEFINED) endif() + # Target Windows 10 RS5 add_definitions(-DNTDDI_VERSION=0x0A000006 -D_WIN32_WINNT=0x0A00 -DWINVER=0x0A00) - # Increase stack commit area - target_link_options(shadps4 PRIVATE /STACK:0x200000,0x200000) + + if (MSVC) + target_link_libraries(shadps4 PRIVATE clang_rt.builtins-x86_64.lib) + endif() + # Disable ASLR so we can reserve the user area if (MSVC) target_link_options(shadps4 PRIVATE /DYNAMICBASE:NO) else() target_link_options(shadps4 PRIVATE -Wl,--disable-dynamicbase) endif() + + # Increase stack commit area (Needed, otherwise there are crashes) + if (MSVC) + target_link_options(shadps4 PRIVATE /STACK:0x200000,0x200000) + else() + target_link_options(shadps4 PRIVATE -Wl,--stack,2097152) + endif() endif() if (WIN32) diff --git a/documents/building-windows.md b/documents/building-windows.md index e00ed90d..684e5fe9 100644 --- a/documents/building-windows.md +++ b/documents/building-windows.md @@ -5,21 +5,77 @@ SPDX-License-Identifier: GPL-2.0-or-later # Build shadPS4 for Windows -## Download Visual Studio Community 2022 +This tutorial reads as if you have none of the prerequisites already installed. If you do, just ignore the steps regarding installation. +If you are building to contribute to the project, please omit `--depth 1` from the git invokations. -Download link: [**Visual Studio 2022**](https://visualstudio.microsoft.com/vs/) +Note: **ARM64 is not supported!** As of writing, it will not build nor run. The instructions with respect to ARM64 are for developers only. -## Requirements +## Option 1: Visual Studio 2022 -### From Visual Studio Community +### (Prerequisite) Download the Community edition from [**Visual Studio 2022**](https://visualstudio.microsoft.com/vs/) -- Desktop development with C++ +Once you are within the installer: +1. Select `Desktop development with C++` +2. Go to "Individual Components" tab +3. Make sure `C++ Clang Compiler for Windows`, `MSBuild support for LLVM` and `C++ CMake Tools for Windows` are selected +4. Continue the installation -### From individual components tab install +### (Prerequisite) Download [**Qt**](https://doc.qt.io/qt-6/get-and-install-qt.html) -- C++ Clang Compiler for Windows (17.0.3) -- MSBuild support for LLVM (Clang-cl) toolset +Beware, this requires you to create a Qt account. If you do not want to do this, please follow the MSYS2/MinGW compilation method instead. -- ## Compiling +1. Select Qt for Visual Studio plugin +2. Select `msvc2019_64` option or similar. If you are on Windows on ARM / Qualcomm Snapdragon Elite X, select `msvc2019_arm64` -- Open Visual Studio Community and select the **x64-Clang-Release**, **x64-Clang-Debug** or **x64-Clang-RelWithDebInfo**. It should compile just fine. +Go through the installation normally. If you do not know what components to select, just select the newest Qt version it gives you. +If you know what you are doing, you may unselect individual components that eat up too much disk space. + +Once you are finished, you will have to configure Qt within Visual Studio: +1. Tools -> Options -> Qt -> Versions +2. Add a new Qt version and navigate it to the correct folder. Should look like so: `C:\Qt\6.7.1\msvc2019_64` +3. Enable the default checkmark on the new version you just created. + +### (Prerequisite) Download [**Git for Windows**](https://git-scm.com/download/win) + +Go through the Git for Windows installation as normal + +### Compiling with Visual Studio GUI + +1. Open Git for Windows, navigate to a place where you want to store the shadPS4 source code folder +2. Run `git clone --depth 1 --recursive https://github.com/shadps4-emu/shadPS4` +3. Open up Visual Studio, select `Open a local folder` and select the folder with the shadPS4 source code. The folder should contain `CMakeLists.txt` +4. Build -> Build All + +## Option 2: MSYS2/MinGW + +### (Prerequisite) Download [**MSYS2**](https://www.msys2.org/) + +Go through the MSYS2 installation as normal + +If you are building to distribute, please omit `-DCMAKE_CXX_FLAGS="-O2 -march=native"` within the build configuration step. + +Normal x86-based computers, follow: +1. Open "MSYS2 MINGW64" from your new applications +2. Run `pacman -Syu`, let it complete; +3. Run `pacman -S --needed git mingw-w64-x86_64-binutils mingw-w64-x86_64-clang mingw-w64-x86_64-cmake mingw-w64-x86_64-ninja mingw-w64-x86_64-qt6-base` +4. Run `git clone --depth 1 --recursive https://github.com/shadps4-emu/shadPS4` +5. Run `cd shadPS4` +6. Run `cmake -S . -B build -DCMAKE_CXX_COMPILER="clang++.exe" -DCMAKE_C_COMPILER="clang.exe" -DCMAKE_CXX_FLAGS="-O2 -march=native"` +7. Run `cmake --build build` +8. To run the finished product, run `./build/shadPS4.exe` + +ARM64-based computers, follow: +1. Open "MSYS2 CLANGARM64" from your new applications +2. Run `pacman -Syu`, let it complete; +3. Run `pacman -S --needed git mingw-w64-clang-aarch64-binutils mingw-w64-clang-aarch64-clang mingw-w64-clang-aarch64-cmake mingw-w64-clang-aarch64-ninja mingw-w64-clang-aarch64-qt6-base` +4. Run `git clone --depth 1 --recursive https://github.com/shadps4-emu/shadPS4` +5. Run `cd shadPS4` +6. Run `cmake -S . -B build -DCMAKE_CXX_COMPILER="clang++.exe" -DCMAKE_C_COMPILER="clang.exe" -DCMAKE_CXX_FLAGS="-O2 -march=native"` +7. Run `cmake --build build` +8. To run the finished product, run `./build/shadPS4.exe` + +## Note on MSYS2 builds + +These builds may not be easily copyable to people who do not also have a MSYS2 installation. +If you want to distribute these builds, you need to copy over the correct DLLs into a distribution folder. +In order to run them, you must be within the MSYS2 shell environment. \ No newline at end of file From 32cb3649d31a1eceb4e8b871c205b05aa138c63b Mon Sep 17 00:00:00 2001 From: Lizardy <6063922+lzardy@users.noreply.github.com> Date: Tue, 20 Aug 2024 21:47:17 +0000 Subject: [PATCH 13/26] rtc errors (#485) * rtc errors * add system libs to cmakelists * this.[func] * fix errors * declaration * log handle addr * missed --------- Co-authored-by: microsoftv <6063922+microsoftv@users.noreply.github.com> --- CMakeLists.txt | 6 ++++++ src/core/libraries/ngs2/ngs2_impl.cpp | 23 ++++++++++++----------- src/core/libraries/ngs2/ngs2_impl.h | 2 +- src/core/libraries/rtc/rtc.cpp | 4 ++-- src/core/libraries/rtc/rtc_error.h | 17 +++++++++++++++++ 5 files changed, 38 insertions(+), 14 deletions(-) create mode 100644 src/core/libraries/rtc/rtc_error.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 679325ec..84a863e6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -193,6 +193,7 @@ set(SYSTEM_LIBS src/core/libraries/system/commondialog.cpp src/core/libraries/app_content/app_content.h src/core/libraries/rtc/rtc.cpp src/core/libraries/rtc/rtc.h + src/core/libraries/rtc/rtc_error.h src/core/libraries/disc_map/disc_map.cpp src/core/libraries/disc_map/disc_map.h src/core/libraries/disc_map/disc_map_codes.h @@ -208,6 +209,11 @@ set(SYSTEM_LIBS src/core/libraries/system/commondialog.cpp src/core/libraries/avplayer/avplayer_state.h src/core/libraries/avplayer/avplayer.cpp src/core/libraries/avplayer/avplayer.h + src/core/libraries/ngs2/ngs2.cpp + src/core/libraries/ngs2/ngs2.h + src/core/libraries/ngs2/ngs2_error.h + src/core/libraries/ngs2/ngs2_impl.cpp + src/core/libraries/ngs2/ngs2_impl.h ) set(VIDEOOUT_LIB src/core/libraries/videoout/buffer.h diff --git a/src/core/libraries/ngs2/ngs2_impl.cpp b/src/core/libraries/ngs2/ngs2_impl.cpp index 185be94d..50c62f5e 100644 --- a/src/core/libraries/ngs2/ngs2_impl.cpp +++ b/src/core/libraries/ngs2/ngs2_impl.cpp @@ -12,22 +12,23 @@ using namespace Libraries::Kernel; namespace Libraries::Ngs2 { -s32 Ngs2::ReportInvalid(u32 handle_type) const { +s32 Ngs2::ReportInvalid(Ngs2Handle* handle, u32 handle_type) const { + uintptr_t hAddress = reinterpret_cast(handle); switch (handle_type) { case 1: - LOG_ERROR(Lib_Ngs2, "Invalid system handle {}", this); + LOG_ERROR(Lib_Ngs2, "Invalid system handle {}", hAddress); return ORBIS_NGS2_ERROR_INVALID_SYSTEM_HANDLE; case 2: - LOG_ERROR(Lib_Ngs2, "Invalid rack handle {}", this); + LOG_ERROR(Lib_Ngs2, "Invalid rack handle {}", hAddress); return ORBIS_NGS2_ERROR_INVALID_RACK_HANDLE; case 4: - LOG_ERROR(Lib_Ngs2, "Invalid voice handle {}", this); + LOG_ERROR(Lib_Ngs2, "Invalid voice handle {}", hAddress); return ORBIS_NGS2_ERROR_INVALID_VOICE_HANDLE; case 8: - LOG_ERROR(Lib_Ngs2, "Invalid report handle {}", this); + LOG_ERROR(Lib_Ngs2, "Invalid report handle {}", hAddress); return ORBIS_NGS2_ERROR_INVALID_REPORT_HANDLE; default: - LOG_ERROR(Lib_Ngs2, "Invalid handle {}", this); + LOG_ERROR(Lib_Ngs2, "Invalid handle {}", hAddress); return ORBIS_NGS2_ERROR_INVALID_HANDLE; } } @@ -58,24 +59,24 @@ s32 Ngs2::HandleCleanup(Ngs2Handle* handle, u32 hType, void* dataOut) { } } } - return HandleReportInvalid(handle, hType); + return this->ReportInvalid(handle, hType); } s32 Ngs2::HandleEnter(Ngs2Handle* handle, u32 hType, Ngs2Handle* handleOut) { if (!handle) { - return HandleReportInvalid(handle, 0); + return this->ReportInvalid(handle, 0); } if (handle->selfPointer != handle || !handle->atomicPtr || !handle->dataPointer || (~hType & handle->handleType)) { - return HandleReportInvalid(handle, handle->handleType); + return this->ReportInvalid(handle, handle->handleType); } std::atomic* atomic = handle->atomicPtr; while (true) { u32 i = atomic->load(); if (i == 0) { - return HandleReportInvalid(handle, handle->handleType); + return this->ReportInvalid(handle, handle->handleType); } if (atomic->compare_exchange_strong(i, i + 1)) { break; @@ -83,7 +84,7 @@ s32 Ngs2::HandleEnter(Ngs2Handle* handle, u32 hType, Ngs2Handle* handleOut) { } if (handleOut) { - *handleOut = handle; + handleOut = handle; } return ORBIS_OK; } diff --git a/src/core/libraries/ngs2/ngs2_impl.h b/src/core/libraries/ngs2/ngs2_impl.h index 36001779..fea87c51 100644 --- a/src/core/libraries/ngs2/ngs2_impl.h +++ b/src/core/libraries/ngs2/ngs2_impl.h @@ -9,7 +9,7 @@ namespace Libraries::Ngs2 { class Ngs2 { public: - s32 ReportInvalid(u32 handle_type) const; + s32 ReportInvalid(Ngs2Handle* handle, u32 handle_type) const; s32 HandleSetup(Ngs2Handle* handle, void* data, std::atomic* atomic, u32 type, u32 flags); s32 HandleCleanup(Ngs2Handle* handle, u32 hType, void* dataOut); s32 HandleEnter(Ngs2Handle* handle, u32 hType, Ngs2Handle* handleOut); diff --git a/src/core/libraries/rtc/rtc.cpp b/src/core/libraries/rtc/rtc.cpp index 82e6db67..f6faa538 100644 --- a/src/core/libraries/rtc/rtc.cpp +++ b/src/core/libraries/rtc/rtc.cpp @@ -7,6 +7,7 @@ #include "core/libraries/error_codes.h" #include "core/libraries/libs.h" #include "rtc.h" +#include "rtc_error.h" namespace Libraries::Rtc { @@ -123,8 +124,7 @@ int PS4_SYSV_ABI sceRtcGetTick() { } int PS4_SYSV_ABI sceRtcGetTickResolution() { - LOG_ERROR(Lib_Rtc, "(STUBBED) called"); - return ORBIS_OK; + return 1000000; } int PS4_SYSV_ABI sceRtcGetTime_t() { diff --git a/src/core/libraries/rtc/rtc_error.h b/src/core/libraries/rtc/rtc_error.h new file mode 100644 index 00000000..04eecbbd --- /dev/null +++ b/src/core/libraries/rtc/rtc_error.h @@ -0,0 +1,17 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +constexpr int ORBIS_RTC_ERROR_INVALID_PARAMETER = 0x80010602; +constexpr int ORBIS_RTC_ERROR_INVALID_TICK_PARAMETER = 0x80010603; +constexpr int ORBIS_RTC_ERROR_INVALID_DATE_PARAMETER = 0x80010604; +constexpr int ORBIS_RTC_ERROR_NOT_IMPLEMENTED = 0x80010605; +constexpr int ORBIS_RTC_ERROR_INVALID_TIMEZONE_FORMAT = 0x80010607; +constexpr int ORBIS_RTC_ERROR_INVALID_YEARS_PARAMETER = 0x80010621; +constexpr int ORBIS_RTC_ERROR_INVALID_MONTHS_PARAMETER = 0x80010622; +constexpr int ORBIS_RTC_ERROR_INVALID_DAYS_PARAMETER = 0x80010623; +constexpr int ORBIS_RTC_ERROR_INVALID_HOURS_PARAMETER = 0x80010624; +constexpr int ORBIS_RTC_ERROR_INVALID_MINUTES_PARAMETER = 0x80010625; +constexpr int ORBIS_RTC_ERROR_INVALID_SECONDS_PARAMETER = 0x80010626; +constexpr int ORBIS_RTC_ERROR_INVALID_MILLISECONDS_PARAMETER = 0x80010627; \ No newline at end of file From 3f9c86ad33428b29a81ddcb218fbb34de072698b Mon Sep 17 00:00:00 2001 From: TheTurtle <47210458+raphaelthegreat@users.noreply.github.com> Date: Wed, 21 Aug 2024 02:00:24 +0300 Subject: [PATCH 14/26] vk_pipeline_cache: Avoid recompiling new shaders on each new PL (#480) * cfg: Add one more divergence case * Seen in RDR shaders * renderer_vulkan: Reduce number of compiled shaders * vk_pipeline_cache: Remove some unnecessary checks --- src/core/libraries/kernel/time_management.cpp | 7 ++ .../frontend/control_flow_graph.cpp | 5 +- .../renderer_vulkan/vk_compute_pipeline.cpp | 27 +++---- .../renderer_vulkan/vk_compute_pipeline.h | 14 +++- .../renderer_vulkan/vk_graphics_pipeline.cpp | 61 ++++++++------- .../renderer_vulkan/vk_graphics_pipeline.h | 9 +-- .../renderer_vulkan/vk_pipeline_cache.cpp | 77 +++++++++++-------- .../renderer_vulkan/vk_pipeline_cache.h | 5 +- .../renderer_vulkan/vk_rasterizer.cpp | 2 +- src/video_core/texture_cache/tile_manager.cpp | 4 +- 10 files changed, 125 insertions(+), 86 deletions(-) diff --git a/src/core/libraries/kernel/time_management.cpp b/src/core/libraries/kernel/time_management.cpp index c4854937..214f039b 100644 --- a/src/core/libraries/kernel/time_management.cpp +++ b/src/core/libraries/kernel/time_management.cpp @@ -143,6 +143,7 @@ int PS4_SYSV_ABI sceKernelGettimeofday(OrbisKernelTimeval* tp) { return ORBIS_KERNEL_ERROR_EFAULT; } +#ifdef _WIN64 auto now = std::chrono::system_clock::now(); auto duration = now.time_since_epoch(); auto seconds = std::chrono::duration_cast(duration); @@ -150,6 +151,12 @@ int PS4_SYSV_ABI sceKernelGettimeofday(OrbisKernelTimeval* tp) { tp->tv_sec = seconds.count(); tp->tv_usec = microsecs.count(); +#else + timeval tv; + gettimeofday(&tv, nullptr); + tp->tv_sec = tv.tv_sec; + tp->tv_usec = tv.tv_usec; +#endif return ORBIS_OK; } diff --git a/src/shader_recompiler/frontend/control_flow_graph.cpp b/src/shader_recompiler/frontend/control_flow_graph.cpp index 4f3ab86e..3faf8665 100644 --- a/src/shader_recompiler/frontend/control_flow_graph.cpp +++ b/src/shader_recompiler/frontend/control_flow_graph.cpp @@ -37,6 +37,7 @@ static IR::Condition MakeCondition(Opcode opcode) { return IR::Condition::Execnz; case Opcode::S_AND_SAVEEXEC_B64: case Opcode::S_ANDN2_B64: + case Opcode::V_CMPX_NE_U32: return IR::Condition::Execnz; default: return IR::Condition::True; @@ -93,7 +94,7 @@ void CFG::EmitDivergenceLabels() { // While this instruction does not save EXEC it is often used paired // with SAVEEXEC to mask the threads that didn't pass the condition // of initial branch. - inst.opcode == Opcode::S_ANDN2_B64; + inst.opcode == Opcode::S_ANDN2_B64 || inst.opcode == Opcode::V_CMPX_NE_U32; }; const auto is_close_scope = [](const GcnInst& inst) { // Closing an EXEC scope can be either a branch instruction @@ -187,7 +188,7 @@ void CFG::LinkBlocks() { const auto end_inst{block.end_inst}; // Handle divergence block inserted here. if (end_inst.opcode == Opcode::S_AND_SAVEEXEC_B64 || - end_inst.opcode == Opcode::S_ANDN2_B64) { + end_inst.opcode == Opcode::S_ANDN2_B64 || end_inst.opcode == Opcode::V_CMPX_NE_U32) { // Blocks are stored ordered by address in the set auto next_it = std::next(it); auto* target_block = &(*next_it); diff --git a/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp b/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp index 62b50eeb..81cf9c02 100644 --- a/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp +++ b/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp @@ -12,18 +12,19 @@ namespace Vulkan { ComputePipeline::ComputePipeline(const Instance& instance_, Scheduler& scheduler_, - vk::PipelineCache pipeline_cache, const Shader::Info* info_, - u64 compute_key_, vk::ShaderModule module) - : instance{instance_}, scheduler{scheduler_}, compute_key{compute_key_}, info{*info_} { + vk::PipelineCache pipeline_cache, u64 compute_key_, + const Program* program) + : instance{instance_}, scheduler{scheduler_}, compute_key{compute_key_}, + info{&program->pgm.info} { const vk::PipelineShaderStageCreateInfo shader_ci = { .stage = vk::ShaderStageFlagBits::eCompute, - .module = module, + .module = program->module, .pName = "main", }; u32 binding{}; boost::container::small_vector bindings; - for (const auto& buffer : info.buffers) { + for (const auto& buffer : info->buffers) { bindings.push_back({ .binding = binding++, .descriptorType = buffer.is_storage ? vk::DescriptorType::eStorageBuffer @@ -32,7 +33,7 @@ ComputePipeline::ComputePipeline(const Instance& instance_, Scheduler& scheduler .stageFlags = vk::ShaderStageFlagBits::eCompute, }); } - for (const auto& image : info.images) { + for (const auto& image : info->images) { bindings.push_back({ .binding = binding++, .descriptorType = image.is_storage ? vk::DescriptorType::eStorageImage @@ -41,7 +42,7 @@ ComputePipeline::ComputePipeline(const Instance& instance_, Scheduler& scheduler .stageFlags = vk::ShaderStageFlagBits::eCompute, }); } - for (const auto& sampler : info.samplers) { + for (const auto& sampler : info->samplers) { bindings.push_back({ .binding = binding++, .descriptorType = vk::DescriptorType::eSampler, @@ -96,8 +97,8 @@ bool ComputePipeline::BindResources(VideoCore::BufferCache& buffer_cache, Shader::PushData push_data{}; u32 binding{}; - for (const auto& buffer : info.buffers) { - const auto vsharp = buffer.GetVsharp(info); + for (const auto& buffer : info->buffers) { + const auto vsharp = buffer.GetVsharp(*info); const VAddr address = vsharp.base_address; // Most of the time when a metadata is updated with a shader it gets cleared. It means we // can skip the whole dispatch and update the tracked state instead. Also, it is not @@ -139,9 +140,9 @@ bool ComputePipeline::BindResources(VideoCore::BufferCache& buffer_cache, }); } - for (const auto& image_desc : info.images) { + for (const auto& image_desc : info->images) { const auto tsharp = - info.ReadUd(image_desc.sgpr_base, image_desc.dword_offset); + info->ReadUd(image_desc.sgpr_base, image_desc.dword_offset); VideoCore::ImageInfo image_info{tsharp}; VideoCore::ImageViewInfo view_info{tsharp, image_desc.is_storage}; const auto& image_view = texture_cache.FindTexture(image_info, view_info); @@ -161,8 +162,8 @@ bool ComputePipeline::BindResources(VideoCore::BufferCache& buffer_cache, LOG_WARNING(Render_Vulkan, "Unexpected metadata read by a CS shader (texture)"); } } - for (const auto& sampler : info.samplers) { - const auto ssharp = sampler.GetSsharp(info); + for (const auto& sampler : info->samplers) { + const auto ssharp = sampler.GetSsharp(*info); const auto vk_sampler = texture_cache.GetSampler(ssharp); image_infos.emplace_back(vk_sampler, VK_NULL_HANDLE, vk::ImageLayout::eGeneral); set_writes.push_back({ diff --git a/src/video_core/renderer_vulkan/vk_compute_pipeline.h b/src/video_core/renderer_vulkan/vk_compute_pipeline.h index 16de5635..5da9dc7e 100644 --- a/src/video_core/renderer_vulkan/vk_compute_pipeline.h +++ b/src/video_core/renderer_vulkan/vk_compute_pipeline.h @@ -3,6 +3,7 @@ #pragma once +#include "shader_recompiler/ir/program.h" #include "shader_recompiler/runtime_info.h" #include "video_core/renderer_vulkan/vk_common.h" @@ -16,11 +17,18 @@ namespace Vulkan { class Instance; class Scheduler; +struct Program { + Shader::IR::Program pgm; + std::vector spv; + vk::ShaderModule module; + u32 end_binding; +}; + class ComputePipeline { public: explicit ComputePipeline(const Instance& instance, Scheduler& scheduler, - vk::PipelineCache pipeline_cache, const Shader::Info* info, - u64 compute_key, vk::ShaderModule module); + vk::PipelineCache pipeline_cache, u64 compute_key, + const Program* program); ~ComputePipeline(); [[nodiscard]] vk::Pipeline Handle() const noexcept { @@ -37,7 +45,7 @@ private: vk::UniquePipelineLayout pipeline_layout; vk::UniqueDescriptorSetLayout desc_layout; u64 compute_key; - Shader::Info info{}; + const Shader::Info* info; }; } // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp index 04486290..887a6d87 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp @@ -19,15 +19,14 @@ namespace Vulkan { GraphicsPipeline::GraphicsPipeline(const Instance& instance_, Scheduler& scheduler_, const GraphicsPipelineKey& key_, vk::PipelineCache pipeline_cache, - std::span infos, - std::array modules) + std::span programs) : instance{instance_}, scheduler{scheduler_}, key{key_} { const vk::Device device = instance.GetDevice(); for (u32 i = 0; i < MaxShaderStages; i++) { - if (!infos[i]) { + if (!programs[i]) { continue; } - stages[i] = *infos[i]; + stages[i] = &programs[i]->pgm.info; } BuildDescSetLayout(); @@ -49,14 +48,14 @@ GraphicsPipeline::GraphicsPipeline(const Instance& instance_, Scheduler& schedul boost::container::static_vector bindings; boost::container::static_vector attributes; const auto& vs_info = stages[u32(Shader::Stage::Vertex)]; - for (const auto& input : vs_info.vs_inputs) { + for (const auto& input : vs_info->vs_inputs) { if (input.instance_step_rate == Shader::Info::VsInput::InstanceIdType::OverStepRate0 || input.instance_step_rate == Shader::Info::VsInput::InstanceIdType::OverStepRate1) { // Skip attribute binding as the data will be pulled by shader continue; } - const auto buffer = vs_info.ReadUd(input.sgpr_base, input.dword_offset); + const auto buffer = vs_info->ReadUd(input.sgpr_base, input.dword_offset); attributes.push_back({ .location = input.binding, .binding = input.binding, @@ -192,21 +191,21 @@ GraphicsPipeline::GraphicsPipeline(const Instance& instance_, Scheduler& schedul .maxDepthBounds = key.depth_bounds_max, }; - u32 shader_count{}; auto stage = u32(Shader::Stage::Vertex); - std::array shader_stages; - shader_stages[shader_count++] = vk::PipelineShaderStageCreateInfo{ + boost::container::static_vector + shader_stages; + shader_stages.emplace_back(vk::PipelineShaderStageCreateInfo{ .stage = vk::ShaderStageFlagBits::eVertex, - .module = modules[stage], + .module = programs[stage]->module, .pName = "main", - }; + }); stage = u32(Shader::Stage::Fragment); - if (modules[stage]) { - shader_stages[shader_count++] = vk::PipelineShaderStageCreateInfo{ + if (programs[stage]) { + shader_stages.emplace_back(vk::PipelineShaderStageCreateInfo{ .stage = vk::ShaderStageFlagBits::eFragment, - .module = modules[stage], + .module = programs[stage]->module, .pName = "main", - }; + }); } const auto it = std::ranges::find(key.color_formats, vk::Format::eUndefined); @@ -280,7 +279,7 @@ GraphicsPipeline::GraphicsPipeline(const Instance& instance_, Scheduler& schedul const vk::GraphicsPipelineCreateInfo pipeline_info = { .pNext = &pipeline_rendering_ci, - .stageCount = shader_count, + .stageCount = static_cast(shader_stages.size()), .pStages = shader_stages.data(), .pVertexInputState = &vertex_input_info, .pInputAssemblyState = &input_assembly, @@ -306,8 +305,11 @@ GraphicsPipeline::~GraphicsPipeline() = default; void GraphicsPipeline::BuildDescSetLayout() { u32 binding{}; boost::container::small_vector bindings; - for (const auto& stage : stages) { - for (const auto& buffer : stage.buffers) { + for (const auto* stage : stages) { + if (!stage) { + continue; + } + for (const auto& buffer : stage->buffers) { bindings.push_back({ .binding = binding++, .descriptorType = buffer.is_storage ? vk::DescriptorType::eStorageBuffer @@ -316,7 +318,7 @@ void GraphicsPipeline::BuildDescSetLayout() { .stageFlags = vk::ShaderStageFlagBits::eVertex | vk::ShaderStageFlagBits::eFragment, }); } - for (const auto& image : stage.images) { + for (const auto& image : stage->images) { bindings.push_back({ .binding = binding++, .descriptorType = image.is_storage ? vk::DescriptorType::eStorageImage @@ -325,7 +327,7 @@ void GraphicsPipeline::BuildDescSetLayout() { .stageFlags = vk::ShaderStageFlagBits::eVertex | vk::ShaderStageFlagBits::eFragment, }); } - for (const auto& sampler : stage.samplers) { + for (const auto& sampler : stage->samplers) { bindings.push_back({ .binding = binding++, .descriptorType = vk::DescriptorType::eSampler, @@ -352,13 +354,16 @@ void GraphicsPipeline::BindResources(const Liverpool::Regs& regs, Shader::PushData push_data{}; u32 binding{}; - for (const auto& stage : stages) { - if (stage.uses_step_rates) { + for (const auto* stage : stages) { + if (!stage) { + continue; + } + if (stage->uses_step_rates) { push_data.step0 = regs.vgt_instance_step_rate_0; push_data.step1 = regs.vgt_instance_step_rate_1; } - for (const auto& buffer : stage.buffers) { - const auto vsharp = buffer.GetVsharp(stage); + for (const auto& buffer : stage->buffers) { + const auto vsharp = buffer.GetVsharp(*stage); if (vsharp) { const VAddr address = vsharp.base_address; if (texture_cache.IsMeta(address)) { @@ -391,9 +396,9 @@ void GraphicsPipeline::BindResources(const Liverpool::Regs& regs, } boost::container::static_vector tsharps; - for (const auto& image_desc : stage.images) { + for (const auto& image_desc : stage->images) { const auto& tsharp = tsharps.emplace_back( - stage.ReadUd(image_desc.sgpr_base, image_desc.dword_offset)); + stage->ReadUd(image_desc.sgpr_base, image_desc.dword_offset)); VideoCore::ImageInfo image_info{tsharp}; VideoCore::ImageViewInfo view_info{tsharp, image_desc.is_storage}; const auto& image_view = texture_cache.FindTexture(image_info, view_info); @@ -413,8 +418,8 @@ void GraphicsPipeline::BindResources(const Liverpool::Regs& regs, LOG_WARNING(Render_Vulkan, "Unexpected metadata read by a PS shader (texture)"); } } - for (const auto& sampler : stage.samplers) { - auto ssharp = sampler.GetSsharp(stage); + for (const auto& sampler : stage->samplers) { + auto ssharp = sampler.GetSsharp(*stage); if (sampler.disable_aniso) { const auto& tsharp = tsharps[sampler.associated_image]; if (tsharp.base_level == 0 && tsharp.last_level == 0) { diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.h b/src/video_core/renderer_vulkan/vk_graphics_pipeline.h index bc8e9913..f7ea32d9 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.h +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.h @@ -3,9 +3,9 @@ #include #include "common/types.h" -#include "shader_recompiler/runtime_info.h" #include "video_core/renderer_vulkan/liverpool_to_vk.h" #include "video_core/renderer_vulkan/vk_common.h" +#include "video_core/renderer_vulkan/vk_compute_pipeline.h" namespace VideoCore { class BufferCache; @@ -58,8 +58,7 @@ class GraphicsPipeline { public: explicit GraphicsPipeline(const Instance& instance, Scheduler& scheduler, const GraphicsPipelineKey& key, vk::PipelineCache pipeline_cache, - std::span infos, - std::array modules); + std::span programs); ~GraphicsPipeline(); void BindResources(const Liverpool::Regs& regs, VideoCore::BufferCache& buffer_cache, @@ -74,7 +73,7 @@ public: } const Shader::Info& GetStage(Shader::Stage stage) const noexcept { - return stages[u32(stage)]; + return *stages[u32(stage)]; } bool IsEmbeddedVs() const noexcept { @@ -99,7 +98,7 @@ private: vk::UniquePipeline pipeline; vk::UniquePipelineLayout pipeline_layout; vk::UniqueDescriptorSetLayout desc_layout; - std::array stages{}; + std::array stages{}; GraphicsPipelineKey key; }; diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index c11705e7..617a7812 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -20,6 +20,10 @@ namespace Vulkan { using Shader::VsOutput; +[[nodiscard]] inline u64 HashCombine(const u64 seed, const u64 hash) { + return seed ^ (hash + 0x9e3779b9 + (seed << 6) + (seed >> 2)); +} + void BuildVsOutputs(Shader::Info& info, const AmdGpu::Liverpool::VsOutputControl& ctl) { const auto add_output = [&](VsOutput x, VsOutput y, VsOutput z, VsOutput w) { if (x != VsOutput::None || y != VsOutput::None || z != VsOutput::None || @@ -246,23 +250,14 @@ std::unique_ptr PipelineCache::CreateGraphicsPipeline() { } u32 binding{}; - std::array programs; - std::array infos{}; - for (u32 i = 0; i < MaxShaderStages; i++) { if (!graphics_key.stage_hashes[i]) { - stages[i] = VK_NULL_HANDLE; + programs[i] = nullptr; continue; } auto* pgm = regs.ProgramForStage(i); const auto code = pgm->Code(); - const auto it = module_map.find(graphics_key.stage_hashes[i]); - if (it != module_map.end()) { - stages[i] = *it->second; - continue; - } - // Dump shader code if requested. const auto stage = Shader::Stage{i}; const u64 hash = graphics_key.stage_hashes[i]; @@ -273,39 +268,56 @@ std::unique_ptr PipelineCache::CreateGraphicsPipeline() { block_pool.ReleaseContents(); inst_pool.ReleaseContents(); - if (stage != Shader::Stage::Compute && stage != Shader::Stage::Fragment && - stage != Shader::Stage::Vertex) { + if (stage != Shader::Stage::Fragment && stage != Shader::Stage::Vertex) { LOG_ERROR(Render_Vulkan, "Unsupported shader stage {}. PL creation skipped.", stage); return {}; } + const u64 lookup_hash = HashCombine(hash, binding); + auto it = program_cache.find(lookup_hash); + if (it != program_cache.end()) { + const Program* program = it.value().get(); + ASSERT(program->pgm.info.stage == stage); + programs[i] = program; + binding = program->end_binding; + continue; + } + // Recompile shader to IR. try { + auto program = std::make_unique(); + block_pool.ReleaseContents(); + inst_pool.ReleaseContents(); + LOG_INFO(Render_Vulkan, "Compiling {} shader {:#x}", stage, hash); Shader::Info info = MakeShaderInfo(stage, pgm->user_data, regs); info.pgm_base = pgm->Address(); info.pgm_hash = hash; - programs[i] = + program->pgm = Shader::TranslateProgram(inst_pool, block_pool, code, std::move(info), profile); // Compile IR to SPIR-V - auto spv_code = Shader::Backend::SPIRV::EmitSPIRV(profile, programs[i], binding); + program->spv = Shader::Backend::SPIRV::EmitSPIRV(profile, program->pgm, binding); if (Config::dumpShaders()) { - DumpShader(spv_code, hash, stage, "spv"); + DumpShader(program->spv, hash, stage, "spv"); } - stages[i] = CompileSPV(spv_code, instance.GetDevice()); - infos[i] = &programs[i].info; + + // Compile module and set name to hash in renderdoc + program->end_binding = binding; + program->module = CompileSPV(program->spv, instance.GetDevice()); + const auto name = fmt::format("{}_{:#x}", stage, hash); + Vulkan::SetObjectName(instance.GetDevice(), program->module, name); + + // Cache program + const auto [it, _] = program_cache.emplace(lookup_hash, std::move(program)); + programs[i] = it.value().get(); } catch (const Shader::Exception& e) { UNREACHABLE_MSG("{}", e.what()); } - - // Set module name to hash in renderdoc - const auto name = fmt::format("{}_{:#x}", stage, hash); - Vulkan::SetObjectName(instance.GetDevice(), stages[i], name); } return std::make_unique(instance, scheduler, graphics_key, *pipeline_cache, - infos, stages); + programs); } std::unique_ptr PipelineCache::CreateComputePipeline() { @@ -322,26 +334,31 @@ std::unique_ptr PipelineCache::CreateComputePipeline() { // Recompile shader to IR. try { + auto program = std::make_unique(); LOG_INFO(Render_Vulkan, "Compiling cs shader {:#x}", compute_key); Shader::Info info = MakeShaderInfo(Shader::Stage::Compute, cs_pgm.user_data, liverpool->regs); info.pgm_base = cs_pgm.Address(); info.pgm_hash = compute_key; - auto program = + program->pgm = Shader::TranslateProgram(inst_pool, block_pool, code, std::move(info), profile); // Compile IR to SPIR-V u32 binding{}; - const auto spv_code = Shader::Backend::SPIRV::EmitSPIRV(profile, program, binding); + program->spv = Shader::Backend::SPIRV::EmitSPIRV(profile, program->pgm, binding); if (Config::dumpShaders()) { - DumpShader(spv_code, compute_key, Shader::Stage::Compute, "spv"); + DumpShader(program->spv, compute_key, Shader::Stage::Compute, "spv"); } - const auto module = CompileSPV(spv_code, instance.GetDevice()); - // Set module name to hash in renderdoc + + // Compile module and set name to hash in renderdoc + program->module = CompileSPV(program->spv, instance.GetDevice()); const auto name = fmt::format("cs_{:#x}", compute_key); - Vulkan::SetObjectName(instance.GetDevice(), module, name); - return std::make_unique(instance, scheduler, *pipeline_cache, - &program.info, compute_key, module); + Vulkan::SetObjectName(instance.GetDevice(), program->module, name); + + // Cache program + const auto [it, _] = program_cache.emplace(compute_key, std::move(program)); + return std::make_unique(instance, scheduler, *pipeline_cache, compute_key, + it.value().get()); } catch (const Shader::Exception& e) { UNREACHABLE_MSG("{}", e.what()); return nullptr; diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.h b/src/video_core/renderer_vulkan/vk_pipeline_cache.h index d41723ec..8f3b806c 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.h +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.h @@ -5,6 +5,7 @@ #include #include "shader_recompiler/ir/basic_block.h" +#include "shader_recompiler/ir/program.h" #include "shader_recompiler/profile.h" #include "video_core/renderer_vulkan/vk_compute_pipeline.h" #include "video_core/renderer_vulkan/vk_graphics_pipeline.h" @@ -43,10 +44,10 @@ private: AmdGpu::Liverpool* liverpool; vk::UniquePipelineCache pipeline_cache; vk::UniquePipelineLayout pipeline_layout; - tsl::robin_map module_map; - std::array stages{}; + tsl::robin_map> program_cache; tsl::robin_map> compute_pipelines; tsl::robin_map> graphics_pipelines; + std::array programs{}; Shader::Profile profile{}; GraphicsPipelineKey graphics_key{}; u64 compute_key{}; diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index 542624a0..9ec8fe21 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -153,7 +153,7 @@ void Rasterizer::BeginRendering() { }; texture_cache.TouchMeta(htile_address, false); state.has_depth = true; - state.has_stencil = image.info.usage.stencil; + state.has_stencil = regs.depth_control.stencil_enable; } scheduler.BeginRendering(state); } diff --git a/src/video_core/texture_cache/tile_manager.cpp b/src/video_core/texture_cache/tile_manager.cpp index f08f2094..6bb104a6 100644 --- a/src/video_core/texture_cache/tile_manager.cpp +++ b/src/video_core/texture_cache/tile_manager.cpp @@ -249,11 +249,11 @@ struct DetilerParams { u32 sizes[14]; }; -static constexpr size_t StreamBufferSize = 128_MB; +static constexpr size_t StreamBufferSize = 1_GB; TileManager::TileManager(const Vulkan::Instance& instance, Vulkan::Scheduler& scheduler) : instance{instance}, scheduler{scheduler}, - stream_buffer{instance, scheduler, MemoryUsage::Stream, StreamBufferSize} { + stream_buffer{instance, scheduler, MemoryUsage::Upload, StreamBufferSize} { static const std::array detiler_shaders{ HostShaders::DETILE_M8X1_COMP, HostShaders::DETILE_M8X2_COMP, HostShaders::DETILE_M32X1_COMP, HostShaders::DETILE_M32X2_COMP, From 6596fe091c3e69e206b277b4adfd5c177eec5f56 Mon Sep 17 00:00:00 2001 From: Borchev <4501931+Borchev@users.noreply.github.com> Date: Tue, 20 Aug 2024 16:10:38 -0700 Subject: [PATCH 15/26] Workaround for readonly memory mapping of files issue --- src/common/io_file.cpp | 5 ++--- src/core/address_space.cpp | 28 +++++++++++++++++++++------- src/core/address_space.h | 2 +- src/core/memory.cpp | 3 ++- 4 files changed, 26 insertions(+), 12 deletions(-) diff --git a/src/common/io_file.cpp b/src/common/io_file.cpp index e1c22d2a..fbc37a10 100644 --- a/src/common/io_file.cpp +++ b/src/common/io_file.cpp @@ -217,7 +217,7 @@ void IOFile::Close() { file = nullptr; #ifdef _WIN64 - if (file_mapping) { + if (file_mapping && file_access_mode == FileAccessMode::ReadWrite) { CloseHandle(std::bit_cast(file_mapping)); } #endif @@ -259,8 +259,7 @@ uintptr_t IOFile::GetFileMapping() { mapping = CreateFileMapping2(hfile, NULL, FILE_MAP_WRITE, PAGE_READWRITE, SEC_COMMIT, 0, NULL, NULL, 0); } else { - mapping = CreateFileMapping2(hfile, NULL, FILE_MAP_READ, PAGE_READONLY, SEC_COMMIT, 0, NULL, - NULL, 0); + mapping = hfile; } file_mapping = std::bit_cast(mapping); diff --git a/src/core/address_space.cpp b/src/core/address_space.cpp index 77d021a6..6444790f 100644 --- a/src/core/address_space.cpp +++ b/src/core/address_space.cpp @@ -2,6 +2,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include +#include "common/alignment.h" #include "common/assert.h" #include "common/error.h" #include "core/address_space.h" @@ -129,9 +130,10 @@ struct AddressSpace::Impl { } void* Map(VAddr virtual_addr, PAddr phys_addr, size_t size, ULONG prot, uintptr_t fd = 0) { + const size_t aligned_size = Common::AlignUp(size, 16_KB); const auto it = placeholders.find(virtual_addr); ASSERT_MSG(it != placeholders.end(), "Cannot map already mapped region"); - ASSERT_MSG(virtual_addr >= it->lower() && virtual_addr + size <= it->upper(), + ASSERT_MSG(virtual_addr >= it->lower() && virtual_addr + aligned_size <= it->upper(), "Map range must be fully contained in a placeholder"); // Windows only allows splitting a placeholder into two. @@ -140,7 +142,7 @@ struct AddressSpace::Impl { // one at the start and at the end. const VAddr placeholder_start = it->lower(); const VAddr placeholder_end = it->upper(); - const VAddr virtual_end = virtual_addr + size; + const VAddr virtual_end = virtual_addr + aligned_size; // If the placeholder doesn't exactly start at virtual_addr, split it at the start. if (placeholder_start != virtual_addr) { @@ -161,11 +163,23 @@ struct AddressSpace::Impl { void* ptr = nullptr; if (phys_addr != -1) { HANDLE backing = fd ? reinterpret_cast(fd) : backing_handle; - ptr = MapViewOfFile3(backing, process, reinterpret_cast(virtual_addr), phys_addr, - size, MEM_REPLACE_PLACEHOLDER, prot, nullptr, 0); + if (fd && prot == PAGE_READONLY) { + DWORD resultvar; + ptr = VirtualAlloc2(process, reinterpret_cast(virtual_addr), aligned_size, + MEM_RESERVE | MEM_COMMIT | MEM_REPLACE_PLACEHOLDER, + PAGE_READWRITE, nullptr, 0); + bool ret = ReadFile(backing, ptr, size, &resultvar, NULL); + ASSERT_MSG(ret, "ReadFile failed. {}", Common::GetLastErrorMsg()); + ret = VirtualProtect(ptr, size, prot, &resultvar); + ASSERT_MSG(ret, "VirtualProtect failed. {}", Common::GetLastErrorMsg()); + } else { + ptr = MapViewOfFile3(backing, process, reinterpret_cast(virtual_addr), + phys_addr, aligned_size, MEM_REPLACE_PLACEHOLDER, prot, + nullptr, 0); + } } else { ptr = - VirtualAlloc2(process, reinterpret_cast(virtual_addr), size, + VirtualAlloc2(process, reinterpret_cast(virtual_addr), aligned_size, MEM_RESERVE | MEM_COMMIT | MEM_REPLACE_PLACEHOLDER, prot, nullptr, 0); } ASSERT_MSG(ptr, "{}", Common::GetLastErrorMsg()); @@ -455,12 +469,12 @@ void* AddressSpace::MapFile(VAddr virtual_addr, size_t size, size_t offset, u32 } void AddressSpace::Unmap(VAddr virtual_addr, size_t size, VAddr start_in_vma, VAddr end_in_vma, - PAddr phys_base, bool is_exec, bool has_backing) { + PAddr phys_base, bool is_exec, bool has_backing, bool readonly) { #ifdef _WIN32 // There does not appear to be comparable support for partial unmapping on Windows. // Unfortunately, a least one title was found to require this. The workaround is to unmap // the entire allocation and remap the portions outside of the requested unmapping range. - impl->Unmap(virtual_addr, size, has_backing); + impl->Unmap(virtual_addr, size, has_backing && !readonly); // TODO: Determine if any titles require partial unmapping support for flexible allocations. ASSERT_MSG(has_backing || (start_in_vma == 0 && end_in_vma == size), diff --git a/src/core/address_space.h b/src/core/address_space.h index 53041bcc..dc38de4d 100644 --- a/src/core/address_space.h +++ b/src/core/address_space.h @@ -92,7 +92,7 @@ public: /// Unmaps specified virtual memory area. void Unmap(VAddr virtual_addr, size_t size, VAddr start_in_vma, VAddr end_in_vma, - PAddr phys_base, bool is_exec, bool has_backing); + PAddr phys_base, bool is_exec, bool has_backing, bool readonly); void Protect(VAddr virtual_addr, size_t size, MemoryPermission perms); diff --git a/src/core/memory.cpp b/src/core/memory.cpp index 6d0d581f..6c3b5005 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -242,10 +242,11 @@ void MemoryManager::UnmapMemory(VAddr virtual_addr, size_t size) { vma.disallow_merge = false; vma.name = ""; MergeAdjacent(vma_map, new_it); + bool readonly = vma.prot == MemoryProt::CpuRead; // Unmap the memory region. impl.Unmap(vma_base_addr, vma_base_size, start_in_vma, start_in_vma + size, phys_base, is_exec, - has_backing); + has_backing, readonly); TRACK_FREE(virtual_addr, "VMEM"); } From fc300b526561a2d57cb69a4442c24d4c532bb4cc Mon Sep 17 00:00:00 2001 From: Borchev <4501931+Borchev@users.noreply.github.com> Date: Tue, 20 Aug 2024 20:07:32 -0700 Subject: [PATCH 16/26] Fix unmapping bug --- src/core/address_space.cpp | 4 ++-- src/core/address_space.h | 2 +- src/core/memory.cpp | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/core/address_space.cpp b/src/core/address_space.cpp index 6444790f..23511370 100644 --- a/src/core/address_space.cpp +++ b/src/core/address_space.cpp @@ -469,12 +469,12 @@ void* AddressSpace::MapFile(VAddr virtual_addr, size_t size, size_t offset, u32 } void AddressSpace::Unmap(VAddr virtual_addr, size_t size, VAddr start_in_vma, VAddr end_in_vma, - PAddr phys_base, bool is_exec, bool has_backing, bool readonly) { + PAddr phys_base, bool is_exec, bool has_backing, bool readonly_file) { #ifdef _WIN32 // There does not appear to be comparable support for partial unmapping on Windows. // Unfortunately, a least one title was found to require this. The workaround is to unmap // the entire allocation and remap the portions outside of the requested unmapping range. - impl->Unmap(virtual_addr, size, has_backing && !readonly); + impl->Unmap(virtual_addr, size, has_backing && !readonly_file); // TODO: Determine if any titles require partial unmapping support for flexible allocations. ASSERT_MSG(has_backing || (start_in_vma == 0 && end_in_vma == size), diff --git a/src/core/address_space.h b/src/core/address_space.h index dc38de4d..2a3488d5 100644 --- a/src/core/address_space.h +++ b/src/core/address_space.h @@ -92,7 +92,7 @@ public: /// Unmaps specified virtual memory area. void Unmap(VAddr virtual_addr, size_t size, VAddr start_in_vma, VAddr end_in_vma, - PAddr phys_base, bool is_exec, bool has_backing, bool readonly); + PAddr phys_base, bool is_exec, bool has_backing, bool readonly_file); void Protect(VAddr virtual_addr, size_t size, MemoryPermission perms); diff --git a/src/core/memory.cpp b/src/core/memory.cpp index 6c3b5005..552c4039 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -242,11 +242,11 @@ void MemoryManager::UnmapMemory(VAddr virtual_addr, size_t size) { vma.disallow_merge = false; vma.name = ""; MergeAdjacent(vma_map, new_it); - bool readonly = vma.prot == MemoryProt::CpuRead; + bool readonly_file = vma.prot == MemoryProt::CpuRead && type == VMAType::File; // Unmap the memory region. impl.Unmap(vma_base_addr, vma_base_size, start_in_vma, start_in_vma + size, phys_base, is_exec, - has_backing, readonly); + has_backing, readonly_file); TRACK_FREE(virtual_addr, "VMEM"); } From 9275b0966e65c707cfc522d74eb1e36abd6c9af3 Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Wed, 21 Aug 2024 03:06:10 -0700 Subject: [PATCH 17/26] Untranslocate app bundle path if needed on macOS. --- src/common/path_util.cpp | 50 ++++++++++++++++++++++++++++++++-------- 1 file changed, 40 insertions(+), 10 deletions(-) diff --git a/src/common/path_util.cpp b/src/common/path_util.cpp index c1e8a5c0..d69f7216 100644 --- a/src/common/path_util.cpp +++ b/src/common/path_util.cpp @@ -8,6 +8,7 @@ #ifdef __APPLE__ #include +#include #include #endif @@ -26,23 +27,52 @@ namespace Common::FS { namespace fs = std::filesystem; #ifdef __APPLE__ +using IsTranslocatedURLFunc = Boolean (*)(CFURLRef path, bool* isTranslocated, + CFErrorRef* __nullable error); +using CreateOriginalPathForURLFunc = CFURLRef __nullable (*)(CFURLRef translocatedPath, + CFErrorRef* __nullable error); + +static CFURLRef UntranslocateBundlePath(const CFURLRef bundle_path) { + if (void* security_handle = + dlopen("/System/Library/Frameworks/Security.framework/Security", RTLD_LAZY)) { + SCOPE_EXIT { + dlclose(security_handle); + }; + + const auto IsTranslocatedURL = reinterpret_cast( + dlsym(security_handle, "SecTranslocateIsTranslocatedURL")); + const auto CreateOriginalPathForURL = reinterpret_cast( + dlsym(security_handle, "SecTranslocateCreateOriginalPathForURL")); + + bool is_translocated = false; + if (IsTranslocatedURL && CreateOriginalPathForURL && + IsTranslocatedURL(bundle_path, &is_translocated, nullptr) && is_translocated) { + return CreateOriginalPathForURL(bundle_path, nullptr); + } + } + return nullptr; +} + static std::filesystem::path GetBundleParentDirectory() { if (CFBundleRef bundle_ref = CFBundleGetMainBundle()) { if (CFURLRef bundle_url_ref = CFBundleCopyBundleURL(bundle_ref)) { SCOPE_EXIT { CFRelease(bundle_url_ref); }; - if (CFStringRef bundle_path_ref = - CFURLCopyFileSystemPath(bundle_url_ref, kCFURLPOSIXPathStyle)) { - SCOPE_EXIT { - CFRelease(bundle_path_ref); - }; - char app_bundle_path[MAXPATHLEN]; - if (CFStringGetFileSystemRepresentation(bundle_path_ref, app_bundle_path, - sizeof(app_bundle_path))) { - std::filesystem::path bundle_path{app_bundle_path}; - return bundle_path.parent_path(); + + CFURLRef untranslocated_url_ref = UntranslocateBundlePath(bundle_url_ref); + SCOPE_EXIT { + if (untranslocated_url_ref) { + CFRelease(untranslocated_url_ref); } + }; + + char app_bundle_path[MAXPATHLEN]; + if (CFURLGetFileSystemRepresentation( + untranslocated_url_ref ? untranslocated_url_ref : bundle_url_ref, true, + reinterpret_cast(app_bundle_path), sizeof(app_bundle_path))) { + std::filesystem::path bundle_path{app_bundle_path}; + return bundle_path.parent_path(); } } } From 6d0d2eaa59c7947044ee486cdd27358f61adebe1 Mon Sep 17 00:00:00 2001 From: Dzmitry Dubrova Date: Wed, 21 Aug 2024 14:37:34 +0300 Subject: [PATCH 18/26] avplayer: Fix sceAvPlayerGetAudioData --- src/core/libraries/avplayer/avplayer_source.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/core/libraries/avplayer/avplayer_source.cpp b/src/core/libraries/avplayer/avplayer_source.cpp index 776d389f..e05a2cdf 100644 --- a/src/core/libraries/avplayer/avplayer_source.cpp +++ b/src/core/libraries/avplayer/avplayer_source.cpp @@ -359,6 +359,7 @@ bool AvPlayerSource::GetAudioData(SceAvPlayerFrameInfo& audio_info) { audio_info = {}; audio_info.timestamp = frame->info.timestamp; audio_info.pData = reinterpret_cast(frame->info.pData); + audio_info.details.audio.sample_rate = frame->info.details.audio.sample_rate; audio_info.details.audio.size = frame->info.details.audio.size; audio_info.details.audio.channel_count = frame->info.details.audio.channel_count; return true; @@ -655,6 +656,7 @@ Frame AvPlayerSource::PrepareAudioFrame(FrameBuffer buffer, const AVFrame& frame .audio = { .channel_count = u16(frame.ch_layout.nb_channels), + .sample_rate = u32(frame.sample_rate), .size = u32(size), }, }, From ba0a6ab03818f14b10b2aa7e87c858a08de7c968 Mon Sep 17 00:00:00 2001 From: Sebastian Kassai <8061077+xezrunner@users.noreply.github.com> Date: Wed, 21 Aug 2024 21:13:06 +0200 Subject: [PATCH 19/26] Expand documentation on configuration and debugging (#513) * documents: more info on config.toml * documents: add Debugging * documents: link to Debugging in README * documents: link to main branch for Debugging Once it's merged, it should link properly. Quickstart already exists in main. * documents: remove "troubleshooting" from Debugging Not entirely relevant. * documents: elaborate on a few points with stack traces * documents: formatting, indentation fixup * documents: remove unnecessary indent * documents: fix some inline code blocks + misc * documents: move the technical jargon out of Quickstart * documents: improve configuration sections Simplified the Quickstart config listing, added more technical info to the Debugging listing. * documents: link quickstart config link to proper section * documents: something ate my hash symbol * documents: use *:Critical for log silencing example * documents: add note about Start Without Debugging * documents: use correct CUSA code in example --- README.md | 4 + documents/Debugging/Debugging.md | 151 +++++++++++++++++++++++++++++ documents/Quickstart/Quickstart.md | 22 ++++- 3 files changed, 176 insertions(+), 1 deletion(-) create mode 100644 documents/Debugging/Debugging.md diff --git a/README.md b/README.md index 7089b3c0..542acca9 100644 --- a/README.md +++ b/README.md @@ -91,6 +91,10 @@ Check the build instructions for [**Linux**](https://github.com/shadps4-emu/shad |macOS Qt Build|[![macOS-qt](https://github.com/shadps4-emu/shadPS4/actions/workflows/macos-qt.yml/badge.svg)](https://github.com/shadps4-emu/shadPS4/actions/workflows/macos-qt.yml) +# Debugging and reporting issues + +For more information on how to test, debug and report issues with the emulator or games, read the [Debugging documentation](https://github.com/shadps4-emu/shadPS4/blob/main/documents/Debugging/Debugging.md). + # Keyboard Mapping | Controller button | Keyboard | diff --git a/documents/Debugging/Debugging.md b/documents/Debugging/Debugging.md new file mode 100644 index 00000000..5e207b52 --- /dev/null +++ b/documents/Debugging/Debugging.md @@ -0,0 +1,151 @@ +# Debugging and reporting issues about shadPS4 and games + +This document covers information about debugging, troubleshooting and reporting developer-side issues related to shadPS4 and games. + +## Setup + +This section will guide you through setting up tools for debugging the emulator. This list will likely expand as more tools and platforms receive consistent setups. + +
+Windows and Visual Studio + +Make sure you have the project set up for building on Windows with Visual Studio and CMake: [Build shadPS4 for Windows +](https://github.com/shadps4-emu/shadPS4/blob/main/documents/building-windows.md) + +1. Open the project folder in Visual Studio **as a folder**. _Do not run `cmake ..` or other commands that set up the project._ + +2. In the Solution Explorer, click the **Switch between solutions and available views** button.\ + ![image](https://github.com/user-attachments/assets/4e2be2b1-ba5a-4451-9ab2-f4ecf246213d) + +3. Double-click on **CMake Targets View**.\ + ![image](https://github.com/user-attachments/assets/5ce7cf90-cd61-4cfa-bef5-645909827290) + +4. Under **shadPS4 Project**, right-click on the **shadps4 (executable)** solution and click **Set as Startup Item**. This will let you start and debug shadPS4 using the VS debug buttons, as well as the default F5 shortcut.\ + ![image](https://github.com/user-attachments/assets/34c7c047-28a3-499f-be8f-df781134d104) + +5. Right-click the **shadps4 (executable)** solution once more and click **Add debug configuration**. + +6. Add an `"args: []"` section into the first `configurations` entry.\ + List your game path as an argument, as if you were launching the non-GUI emulator from the command line. + ![image](https://github.com/user-attachments/assets/8c7c3e69-f38f-4d6b-bdfd-4f1c41c50be7) + +7. Set the appropriate CMake configuration for debugging or testing. + - For debugging the emulator and games within it, select `x64-Clang-Debug`. + - For testing the emulator with compiler optimizations as a release build, it is recommended to select `x64-Clang-RelWithDebInfo`, + as debug symbols will still be generated in case you encounter release configuration-exclusive bugs/errors. + ![image](https://github.com/user-attachments/assets/0d975f7a-7bea-4f89-87ef-5d685bea4381) + +Launch and debug the emulator through **Debug > Start Debugging** (F5 by default), or **Debug > Start Without Debugging** (Ctrl+F5 by default) when testing games for performance. + +
+ +## Configuration + +You can configure the emulator by editing the `config.toml` file found in the `user` folder created after starting the application. + +
+ Some configuration entries worth changing + +- `[General]` + + - `logType`: Configures logging synchronization (`sync`/`async`) + - By default, the emulator logs messages asynchronously for better performance. Some log messages may end up being received out-of-order. + - It can be beneficial to set this to `sync` in order for the log to accurately maintain message order, at the cost of performance. + - When communicating about issues with games and the log messages aren't clear due to potentially confusing order, set this to `sync` and send that log as well. + - `logFilter`: Sets the logging category for various logging classes. + - Format: `: ...` + - Multiple classes can be set by separating them with a space. (example: `Render:Warning Debug:Critical Lib.Pad:Error`) + - Sub-classes can be specified in the same format as seen in the console/log (such as `Core.Linker`). + - All classes and sub-classes can be set by specifying a `*` symbol. (example: `Kernel.*:Critical`) + - Valid log levels: `Trace, Debug, Info, Warning, Error, Critical` - in this order, setting a level silences all levels preceding it and logs every level after it. + - Examples: + - If the log is being spammed with messages coming from Lib.Pad, you can use `Lib.Pad:Critical` to only log critical-level messages. + - If you'd like to mute everything, but still want to receive messages from Vulkan rendering: `*:Critical Render.Vulkan:Info` + + - `Fullscreen`: Display the game in a full screen borderless window. + +- `[GPU]` + - `dumpShaders`: Dump shaders that are loaded by the emulator. Dump path: `../user/shader/dumps` + - `nullGpu`: Disables rendering. + - `screenWidth` and `screenHeight`: Configures the game window width and height. + +- `[Vulkan]` + - `validation`-related settings: Use when debugging Vulkan. + - `rdocEnable`: Automatically hook RenderDoc when installed. Useful for debugging shaders and game rendering. + - `rdocMarkersEnable`: Enable automatic RenderDoc event annotation + +- `[LLE]` + - `libc`: Use LLE with `libc`. + +
+ +## Quick analysis + +This section will provide some preliminary steps to take and tips on what to do when you encounter scenarios that require debugging. + +
+When a game crashes and breaks in the debugger + +1. Analyze the log + - A console will open by default when you launch the emulator. It shows the same log messages that go into the log file found at `/user/log/shad_log.txt`. + + - It is recommended that you start analyzing the log bottom-up first: + - Are there any critical or error-level messages at the end of the log that would point to a reason for the game crashing? + - Do any of the last few messages contain information about the game loading files? + - Did the game window draw anything on-screen? + + - Continue analyzing the log from the start to see other errors (such as with initialization, memory mapping, linker errors etc.) + +2. Analyze the stack trace + - When the emulator is launched through a debugger, it will **break** when an exception or violation is encountered.\ + _(**breaking** in this context means pausing execution of the program before it continues or stops altogether. + Breaks can be intentional as well - these are set with various kinds of **breakpoints**.)_ + + - Default setups of most debuggers include a **Stack trace** window/panel that lists the functions the program has called before breaking. + + - The stack trace entries can be navigated to and will show the relevant function, as well as switch to the state that the program was in at the time of execution.\ + Use the **Locals** and **Watch** windows to investigate variables and other code in these contexts. + + 3. Identify the reason for the crash + - **Logs aren't always accurate in determining the reason for a crash.**\ + Some log entries are reported as errors but may not be fatal for the execution to stop. `Critical` entries are most likely to be the cause for crashes. + + - Pinpoint the area of the emulator where the crash occured\ + If the stack trace ends with functions that are relevant to rendering, it is safe to assume that the issue is with **rendering**.\ + Similarly, if a crash is in a library responsible for playing videos, your issue can be narrowed down to the scope of video playback in the emulator. + + - **âš  Some crashes are intentional** + - If you identify **Access violations for writing operations** where the function is (or in cases of game libraries, _looks like_ it is) copying memory, + it most likely is an **intentional exception** meant to catch game data being written by the game. + This is used by the emulator developers to identify procedures that have to do with game data changing. + - Debugging tools usually include an option to not break on certain types of exceptions. **Exclude access violations and other intentional exceptions when debugging to skip these exceptions.** + - You can also identify such cases if the game works in Release builds of the emulator. These intentional exceptions are development-time only. + - Attempt to **Continue** and observe whether the stack trace and/or variables and registers change when you encounter exceptions. + +
+ +## Reporting and communicating about issues + +When communicating with the project about game-specific issues, specify an **uniquely identifable game name** along with its `CUSA-xxxxx` code that is specific to the region/variant of the game you're testing.\ +The version number is also important to add at least in the description, especially if you can verify that the game behaves differently across versions.\ +Accurately identifying games will help other developers that own that game recognize your issue by its title and jump in to help test and debug it. + +- Examples of good naming schemes: + - Amplitude (2016) `CUSA02480` + - Rock Band 4 (`CUSA02084`) v1.0 + - inFamous: Second Son \[`CUSA-00004`\] +- Examples of unideal naming schemes: + - _The Witness_ + - _GTA 5_ + - _Watch Dogs_ + +- If your issue is small or you aren't sure whether you have properly identified something, [join the Discord server](https://discord.gg/MyZRaBngxA) and use the #development channel + to concisely explain the issue, as well as any findings you currently have. + +- It is recommended that you check the [game compatibility issue tracker](https://github.com/shadps4-emu/shadps4-game-compatibility/issues) and post very short summaries of progress changes there, + (such as the game now booting into the menu or getting in-game) for organizational and status update purposes. + +- ⚠ **Do not post theoretical, unproven game-specific issues in the emulator issue tracker that you cannot verify and locate in the emulator source code as being a bug.**\ + Do, however, add information about the game you experienced the issue in, so that it can be tested in a reproducible environment. + - Good example: "_Crash in `Shader::Gcn::CFG::EmitBlocks()`, out of bounds list access_" -> _issue description shares stack trace, points to code in the repository and provides relevant information_ + - Bad example: "_Amplitude crashes on boot, access violation_" -> _issue description reiterates title, focuses on the game instead of the emulator and refuses to elaborate_ \ No newline at end of file diff --git a/documents/Quickstart/Quickstart.md b/documents/Quickstart/Quickstart.md index 4c51b288..4dd897d8 100644 --- a/documents/Quickstart/Quickstart.md +++ b/documents/Quickstart/Quickstart.md @@ -58,4 +58,24 @@ To install PKG files (game and updates), you will need the Qt application (with ## Configure the emulator -You can configure the emulator in the "user" folder (created after the first start of the application) then in the "config.toml" file. Here you can find lots of parameters to set with True or False. +You can configure the emulator by editing the `config.toml` file found in the `user` folder created after starting the application.\ +Some settings may be related to more technical development and debugging. For more information on those, see [Debugging](https://github.com/shadps4-emu/shadPS4/blob/main/documents/Debugging/Debugging.md#configuration). + +Here's a list of configuration entries that are worth changing: + +- `[General]` + + - `Fullscreen`: Display the game in a full screen borderless window. + + - `logType`: Configures logging synchronization (`sync`/`async`) + - It can be beneficial to set this to `sync` in order for the log to accurately maintain message order, at the cost of performance. + - Use when sending logs to developers. See more about [reporting issues](https://github.com/shadps4-emu/shadPS4/blob/main/documents/Debugging/Debugging.md#reporting-and-communicating-about-issues). + - `logFilter`: Sets the logging category for various logging classes. + - Format: `: ...`, `: <*:level> ...` + - Valid log levels: `Trace, Debug, Info, Warning, Error, Critical` - in this order, setting a level silences all levels preceding it and logs every level after it. + - Examples: + - If the log is being spammed with messages coming from Lib.Pad, you can use `Lib.Pad:Critical` to only log critical-level messages. + - If you'd like to mute everything, but still want to receive messages from Vulkan rendering: `*:Error Render.Vulkan:Info` + +- `[GPU]` + - `screenWidth` and `screenHeight`: Configures the game window width and height. \ No newline at end of file From dfd305ff77369adf737157921526f7bf99a83d1e Mon Sep 17 00:00:00 2001 From: georgemoralis Date: Wed, 21 Aug 2024 22:16:03 +0300 Subject: [PATCH 20/26] Update Debugging.md fixed reuse --- documents/Debugging/Debugging.md | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/documents/Debugging/Debugging.md b/documents/Debugging/Debugging.md index 5e207b52..ef9aab87 100644 --- a/documents/Debugging/Debugging.md +++ b/documents/Debugging/Debugging.md @@ -1,4 +1,9 @@ -# Debugging and reporting issues about shadPS4 and games + + + # Debugging and reporting issues about shadPS4 and games This document covers information about debugging, troubleshooting and reporting developer-side issues related to shadPS4 and games. @@ -148,4 +153,4 @@ Accurately identifying games will help other developers that own that game recog - ⚠ **Do not post theoretical, unproven game-specific issues in the emulator issue tracker that you cannot verify and locate in the emulator source code as being a bug.**\ Do, however, add information about the game you experienced the issue in, so that it can be tested in a reproducible environment. - Good example: "_Crash in `Shader::Gcn::CFG::EmitBlocks()`, out of bounds list access_" -> _issue description shares stack trace, points to code in the repository and provides relevant information_ - - Bad example: "_Amplitude crashes on boot, access violation_" -> _issue description reiterates title, focuses on the game instead of the emulator and refuses to elaborate_ \ No newline at end of file + - Bad example: "_Amplitude crashes on boot, access violation_" -> _issue description reiterates title, focuses on the game instead of the emulator and refuses to elaborate_ From 79680c50c085f45bd5a4ce01f52565d9a2c175d8 Mon Sep 17 00:00:00 2001 From: Vladislav Mikhalin Date: Wed, 21 Aug 2024 23:54:23 +0300 Subject: [PATCH 21/26] Misc fixes (#517) * Misc fixes * Removed the skip for draw calls without RTs * Remove Srgb image stores to rework later --- .../libraries/avplayer/avplayer_source.cpp | 3 ++ src/core/libraries/system/msgdialog.cpp | 21 ++++++++++++-- src/video_core/amdgpu/liverpool.h | 1 + .../renderer_vulkan/liverpool_to_vk.cpp | 28 +++++++++++-------- .../renderer_vulkan/vk_graphics_pipeline.cpp | 3 +- .../renderer_vulkan/vk_graphics_pipeline.h | 1 + .../renderer_vulkan/vk_pipeline_cache.cpp | 17 ++++++++++- .../renderer_vulkan/vk_rasterizer.cpp | 15 +++++++--- .../renderer_vulkan/vk_scheduler.cpp | 13 +++++++-- src/video_core/texture_cache/image.cpp | 14 ++++++++-- 10 files changed, 89 insertions(+), 27 deletions(-) diff --git a/src/core/libraries/avplayer/avplayer_source.cpp b/src/core/libraries/avplayer/avplayer_source.cpp index e05a2cdf..f5479870 100644 --- a/src/core/libraries/avplayer/avplayer_source.cpp +++ b/src/core/libraries/avplayer/avplayer_source.cpp @@ -366,6 +366,9 @@ bool AvPlayerSource::GetAudioData(SceAvPlayerFrameInfo& audio_info) { } u64 AvPlayerSource::CurrentTime() { + if (!IsActive()) { + return 0; + } using namespace std::chrono; return duration_cast(high_resolution_clock::now() - m_start_time).count(); } diff --git a/src/core/libraries/system/msgdialog.cpp b/src/core/libraries/system/msgdialog.cpp index 1c8653f5..452feec9 100644 --- a/src/core/libraries/system/msgdialog.cpp +++ b/src/core/libraries/system/msgdialog.cpp @@ -6,6 +6,8 @@ #include "core/libraries/libs.h" #include "core/libraries/system/msgdialog.h" +#include + namespace Libraries::MsgDialog { int PS4_SYSV_ABI sceMsgDialogClose() { @@ -30,9 +32,22 @@ int PS4_SYSV_ABI sceMsgDialogInitialize() { s32 PS4_SYSV_ABI sceMsgDialogOpen(const OrbisMsgDialogParam* param) { LOG_ERROR(Lib_MsgDlg, "(STUBBED) called"); - OrbisMsgDialogUserMessageParam* userMsgParam = param->userMsgParam; - const char* msg = userMsgParam->msg; - printf("sceMsgDialogOpen msg : %s", msg); + switch (param->mode) { + case ORBIS_MSG_DIALOG_MODE_USER_MSG: + LOG_INFO(Lib_MsgDlg, "sceMsgDialogOpen userMsg type = %s msg = %s", + magic_enum::enum_name(param->userMsgParam->buttonType), param->userMsgParam->msg); + break; + case ORBIS_MSG_DIALOG_MODE_PROGRESS_BAR: + LOG_INFO(Lib_MsgDlg, "sceMsgDialogOpen progressBar type = %s msg = %s", + magic_enum::enum_name(param->progBarParam->barType), param->progBarParam->msg); + break; + case ORBIS_MSG_DIALOG_MODE_SYSTEM_MSG: + LOG_INFO(Lib_MsgDlg, "sceMsgDialogOpen systemMsg type: %s", + magic_enum::enum_name(param->sysMsgParam->sysMsgType)); + break; + default: + break; + } return ORBIS_OK; } diff --git a/src/video_core/amdgpu/liverpool.h b/src/video_core/amdgpu/liverpool.h index 896927df..595c98f5 100644 --- a/src/video_core/amdgpu/liverpool.h +++ b/src/video_core/amdgpu/liverpool.h @@ -848,6 +848,7 @@ struct Liverpool { u32 raw; BitField<0, 1, u32> depth_clear_enable; BitField<1, 1, u32> stencil_clear_enable; + BitField<5, 1, u32> stencil_compress_disable; BitField<6, 1, u32> depth_compress_disable; }; diff --git a/src/video_core/renderer_vulkan/liverpool_to_vk.cpp b/src/video_core/renderer_vulkan/liverpool_to_vk.cpp index e86d0652..daf64a4d 100644 --- a/src/video_core/renderer_vulkan/liverpool_to_vk.cpp +++ b/src/video_core/renderer_vulkan/liverpool_to_vk.cpp @@ -5,6 +5,8 @@ #include "video_core/amdgpu/pixel_format.h" #include "video_core/renderer_vulkan/liverpool_to_vk.h" +#include + namespace Vulkan::LiverpoolToVK { using DepthBuffer = Liverpool::DepthBuffer; @@ -588,6 +590,8 @@ vk::Format AdjustColorBufferFormat(vk::Format base_format, return is_vo_surface ? vk::Format::eB8G8R8A8Unorm : vk::Format::eB8G8R8A8Srgb; case vk::Format::eB8G8R8A8Srgb: return is_vo_surface ? vk::Format::eR8G8B8A8Unorm : vk::Format::eR8G8B8A8Srgb; + default: + break; } } else { if (is_vo_surface && base_format == vk::Format::eR8G8B8A8Srgb) { @@ -601,27 +605,29 @@ vk::Format AdjustColorBufferFormat(vk::Format base_format, } vk::Format DepthFormat(DepthBuffer::ZFormat z_format, DepthBuffer::StencilFormat stencil_format) { - if (z_format == DepthBuffer::ZFormat::Z32Float && - stencil_format == DepthBuffer::StencilFormat::Stencil8) { + using ZFormat = DepthBuffer::ZFormat; + using StencilFormat = DepthBuffer::StencilFormat; + + if (z_format == ZFormat::Z32Float && stencil_format == StencilFormat::Stencil8) { return vk::Format::eD32SfloatS8Uint; } - if (z_format == DepthBuffer::ZFormat::Z32Float && - stencil_format == DepthBuffer::StencilFormat::Invalid) { + if (z_format == ZFormat::Z32Float && stencil_format == StencilFormat::Invalid) { return vk::Format::eD32Sfloat; } - if (z_format == DepthBuffer::ZFormat::Z16 && - stencil_format == DepthBuffer::StencilFormat::Invalid) { + if (z_format == ZFormat::Z16 && stencil_format == StencilFormat::Invalid) { return vk::Format::eD16Unorm; } - if (z_format == DepthBuffer::ZFormat::Z16 && - stencil_format == DepthBuffer::StencilFormat::Stencil8) { + if (z_format == ZFormat::Z16 && stencil_format == StencilFormat::Stencil8) { return vk::Format::eD16UnormS8Uint; } - if (z_format == DepthBuffer::ZFormat::Invalid && - stencil_format == DepthBuffer::StencilFormat::Invalid) { + if (z_format == ZFormat::Invalid && stencil_format == StencilFormat::Stencil8) { + return vk::Format::eD32SfloatS8Uint; + } + if (z_format == ZFormat::Invalid && stencil_format == StencilFormat::Invalid) { return vk::Format::eUndefined; } - UNREACHABLE(); + UNREACHABLE_MSG("Unsupported depth/stencil format. depth = {} stencil = {}", + magic_enum::enum_name(z_format), magic_enum::enum_name(stencil_format)); } void EmitQuadToTriangleListIndices(u8* out_ptr, u32 num_vertices) { diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp index 887a6d87..c2649b96 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp @@ -214,8 +214,7 @@ GraphicsPipeline::GraphicsPipeline(const Instance& instance_, Scheduler& schedul .colorAttachmentCount = num_color_formats, .pColorAttachmentFormats = key.color_formats.data(), .depthAttachmentFormat = key.depth_format, - .stencilAttachmentFormat = - key.depth.stencil_enable ? key.depth_format : vk::Format::eUndefined, + .stencilAttachmentFormat = key.stencil_format, }; std::array attachments; diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.h b/src/video_core/renderer_vulkan/vk_graphics_pipeline.h index f7ea32d9..548e7d45 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.h +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.h @@ -26,6 +26,7 @@ struct GraphicsPipelineKey { std::array stage_hashes; std::array color_formats; vk::Format depth_format; + vk::Format stencil_format; Liverpool::DepthControl depth; float depth_bounds_min; diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index 617a7812..55f04bac 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -180,11 +180,26 @@ void PipelineCache::RefreshGraphicsKey() { key.num_samples = regs.aa_config.NumSamples(); const auto& db = regs.depth_buffer; - key.depth_format = LiverpoolToVK::DepthFormat(db.z_info.format, db.stencil_info.format); + const auto ds_format = LiverpoolToVK::DepthFormat(db.z_info.format, db.stencil_info.format); + + if (db.z_info.format != AmdGpu::Liverpool::DepthBuffer::ZFormat::Invalid) { + key.depth_format = ds_format; + } else { + key.depth_format = vk::Format::eUndefined; + } if (key.depth.depth_enable) { key.depth.depth_enable.Assign(key.depth_format != vk::Format::eUndefined); } + if (db.stencil_info.format != AmdGpu::Liverpool::DepthBuffer::StencilFormat::Invalid) { + key.stencil_format = key.depth_format; + } else { + key.stencil_format = vk::Format::eUndefined; + } + if (key.depth.stencil_enable) { + key.depth.stencil_enable.Assign(key.stencil_format != vk::Format::eUndefined); + } + const auto skip_cb_binding = regs.color_control.mode == AmdGpu::Liverpool::ColorControl::OperationMode::Disable; diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index 9ec8fe21..6cd80393 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -10,6 +10,7 @@ #include "video_core/renderer_vulkan/vk_scheduler.h" #include "video_core/texture_cache/image_view.h" #include "video_core/texture_cache/texture_cache.h" +#include "vk_rasterizer.h" namespace Vulkan { @@ -129,8 +130,12 @@ void Rasterizer::BeginRendering() { texture_cache.TouchMeta(col_buf.CmaskAddress(), false); } - if (regs.depth_buffer.z_info.format != Liverpool::DepthBuffer::ZFormat::Invalid && - regs.depth_buffer.Address() != 0) { + using ZFormat = AmdGpu::Liverpool::DepthBuffer::ZFormat; + using StencilFormat = AmdGpu::Liverpool::DepthBuffer::StencilFormat; + if (regs.depth_buffer.Address() != 0 && + ((regs.depth_control.depth_enable && regs.depth_buffer.z_info.format != ZFormat::Invalid) || + regs.depth_control.stencil_enable && + regs.depth_buffer.stencil_info.format != StencilFormat::Invalid)) { const auto htile_address = regs.depth_htile_data_base.GetAddress(); const bool is_clear = regs.depth_render_control.depth_clear_enable || texture_cache.IsMetaCleared(htile_address); @@ -152,8 +157,10 @@ void Rasterizer::BeginRendering() { .stencil = regs.stencil_clear}}, }; texture_cache.TouchMeta(htile_address, false); - state.has_depth = true; - state.has_stencil = regs.depth_control.stencil_enable; + state.has_depth = + regs.depth_buffer.z_info.format != AmdGpu::Liverpool::DepthBuffer::ZFormat::Invalid; + state.has_stencil = regs.depth_buffer.stencil_info.format != + AmdGpu::Liverpool::DepthBuffer::StencilFormat::Invalid; } scheduler.BeginRendering(state); } diff --git a/src/video_core/renderer_vulkan/vk_scheduler.cpp b/src/video_core/renderer_vulkan/vk_scheduler.cpp index 10ee6ea6..ef0307ef 100644 --- a/src/video_core/renderer_vulkan/vk_scheduler.cpp +++ b/src/video_core/renderer_vulkan/vk_scheduler.cpp @@ -29,15 +29,22 @@ void Scheduler::BeginRendering(const RenderState& new_state) { is_rendering = true; render_state = new_state; + const auto witdh = + render_state.width != std::numeric_limits::max() ? render_state.width : 1; + const auto height = + render_state.height != std::numeric_limits::max() ? render_state.height : 1; + const vk::RenderingInfo rendering_info = { .renderArea = { .offset = {0, 0}, - .extent = {render_state.width, render_state.height}, + .extent = {witdh, height}, }, .layerCount = 1, .colorAttachmentCount = render_state.num_color_attachments, - .pColorAttachments = render_state.color_attachments.data(), + .pColorAttachments = render_state.num_color_attachments > 0 + ? render_state.color_attachments.data() + : nullptr, .pDepthAttachment = render_state.has_depth ? &render_state.depth_attachment : nullptr, .pStencilAttachment = render_state.has_stencil ? &render_state.depth_attachment : nullptr, }; @@ -72,7 +79,7 @@ void Scheduler::EndRendering() { }, }); } - if (render_state.has_depth) { + if (render_state.has_depth || render_state.has_stencil) { barriers.push_back(vk::ImageMemoryBarrier{ .srcAccessMask = vk::AccessFlagBits::eDepthStencilAttachmentWrite, .dstAccessMask = vk::AccessFlagBits::eShaderRead | vk::AccessFlagBits::eShaderWrite, diff --git a/src/video_core/texture_cache/image.cpp b/src/video_core/texture_cache/image.cpp index bae4b89d..528dda55 100644 --- a/src/video_core/texture_cache/image.cpp +++ b/src/video_core/texture_cache/image.cpp @@ -131,11 +131,19 @@ Image::Image(const Vulkan::Instance& instance_, Vulkan::Scheduler& scheduler_, usage = ImageUsageFlags(info); - if (info.pixel_format == vk::Format::eD32Sfloat) { + switch (info.pixel_format) { + case vk::Format::eD16Unorm: + case vk::Format::eD32Sfloat: + case vk::Format::eX8D24UnormPack32: aspect_mask = vk::ImageAspectFlagBits::eDepth; - } - if (info.pixel_format == vk::Format::eD32SfloatS8Uint) { + break; + case vk::Format::eD16UnormS8Uint: + case vk::Format::eD24UnormS8Uint: + case vk::Format::eD32SfloatS8Uint: aspect_mask = vk::ImageAspectFlagBits::eDepth | vk::ImageAspectFlagBits::eStencil; + break; + default: + break; } const vk::ImageCreateInfo image_ci = { From ca4b520272cfbbb2cd02629ad597bf53dbbc2d53 Mon Sep 17 00:00:00 2001 From: Aiden Turner Date: Wed, 21 Aug 2024 18:52:08 -0400 Subject: [PATCH 22/26] Added logging for debugging configs (#518) * added logging for config file * forgot a setting * fixed bloated settings logging. * fixed compile error --- src/emulator.cpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/emulator.cpp b/src/emulator.cpp index 37e227dd..99a0fa2a 100644 --- a/src/emulator.cpp +++ b/src/emulator.cpp @@ -52,6 +52,19 @@ Emulator::Emulator() { LOG_INFO(Loader, "Branch {}", Common::g_scm_branch); LOG_INFO(Loader, "Description {}", Common::g_scm_desc); + LOG_INFO(Config, "General isNeo: {}", Config::isNeoMode()); + LOG_INFO(Config, "GPU isNullGpu: {}", Config::nullGpu()); + LOG_INFO(Config, "GPU shouldDumpShaders: {}", Config::dumpShaders()); + LOG_INFO(Config, "GPU shouldDumpPM4: {}", Config::dumpPM4()); + LOG_INFO(Config, "GPU vblankDivider: {}", Config::vblankDiv()); + LOG_INFO(Config, "Vulkan gpuId: {}", Config::getGpuId()); + LOG_INFO(Config, "Vulkan vkValidation: {}", Config::vkValidationEnabled()); + LOG_INFO(Config, "Vulkan vkValidationSync: {}", Config::vkValidationSyncEnabled()); + LOG_INFO(Config, "Vulkan vkValidationGpu: {}", Config::vkValidationGpuEnabled()); + LOG_INFO(Config, "Vulkan rdocEnable: {}", Config::isRdocEnabled()); + LOG_INFO(Config, "Vulkan rdocMarkersEnable: {}", Config::isMarkersEnabled()); + LOG_INFO(Config, "LLE isLibc: {}", Config::isLleLibc()); + // Defer until after logging is initialized. memory = Core::Memory::Instance(); controller = Common::Singleton::Instance(); From aed9a737d6d72cecd8e2016bb8a64ddb3f619323 Mon Sep 17 00:00:00 2001 From: Herman Semenov Date: Thu, 22 Aug 2024 02:56:01 +0300 Subject: [PATCH 23/26] Added const reference params if possible, removed less 16 size --- src/common/config.cpp | 12 ++++++------ src/common/config.h | 12 ++++++------ src/common/uint128.h | 2 +- src/core/file_format/playgo_chunk.cpp | 2 +- src/core/file_format/playgo_chunk.h | 2 +- src/core/file_format/psf.cpp | 2 +- src/core/file_format/psf.h | 2 +- src/core/libraries/avplayer/avplayer_source.cpp | 2 +- src/shader_recompiler/ir/opcodes.h | 2 +- 9 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/common/config.cpp b/src/common/config.cpp index 24db6b03..6289e2af 100644 --- a/src/common/config.cpp +++ b/src/common/config.cpp @@ -189,15 +189,15 @@ void setNeoMode(bool enable) { isNeo = enable; } -void setLogType(std::string type) { +void setLogType(const std::string& type) { logType = type; } -void setLogFilter(std::string type) { +void setLogFilter(const std::string& type) { logFilter = type; } -void setUserName(std::string type) { +void setUserName(const std::string& type) { userName = type; } @@ -234,15 +234,15 @@ void setMainWindowWidth(u32 width) { void setMainWindowHeight(u32 height) { m_window_size_H = height; } -void setPkgViewer(std::vector pkgList) { +void setPkgViewer(const std::vector& pkgList) { m_pkg_viewer.resize(pkgList.size()); m_pkg_viewer = pkgList; } -void setElfViewer(std::vector elfList) { +void setElfViewer(const std::vector& elfList) { m_elf_viewer.resize(elfList.size()); m_elf_viewer = elfList; } -void setRecentFiles(std::vector recentFiles) { +void setRecentFiles(const std::vector& recentFiles) { m_recent_files.resize(recentFiles.size()); m_recent_files = recentFiles; } diff --git a/src/common/config.h b/src/common/config.h index 3006f2e2..f2b7fe09 100644 --- a/src/common/config.h +++ b/src/common/config.h @@ -43,10 +43,10 @@ void setScreenHeight(u32 height); void setFullscreenMode(bool enable); void setLanguage(u32 language); void setNeoMode(bool enable); -void setUserName(std::string type); +void setUserName(const std::string& type); -void setLogType(std::string type); -void setLogFilter(std::string type); +void setLogType(const std::string& type); +void setLogFilter(const std::string& type); void setVkValidation(bool enable); void setVkSyncValidation(bool enable); @@ -67,9 +67,9 @@ void setSliderPositonGrid(u32 pos); void setTableMode(u32 mode); void setMainWindowWidth(u32 width); void setMainWindowHeight(u32 height); -void setPkgViewer(std::vector pkgList); -void setElfViewer(std::vector elfList); -void setRecentFiles(std::vector recentFiles); +void setPkgViewer(const std::vector& pkgList); +void setElfViewer(const std::vector& elfList); +void setRecentFiles(const std::vector& recentFiles); u32 getMainWindowGeometryX(); u32 getMainWindowGeometryY(); diff --git a/src/common/uint128.h b/src/common/uint128.h index c4435791..77c4a271 100644 --- a/src/common/uint128.h +++ b/src/common/uint128.h @@ -94,7 +94,7 @@ namespace Common { // This function divides a u128 by a u32 value and produces two u64 values: // the result of division and the remainder -[[nodiscard]] static inline std::pair Divide128On32(u128 dividend, u32 divisor) { +[[nodiscard]] static inline std::pair Divide128On32(const u128& dividend, u32 divisor) { u64 remainder = dividend[0] % divisor; u64 accum = dividend[0] / divisor; if (dividend[1] == 0) diff --git a/src/core/file_format/playgo_chunk.cpp b/src/core/file_format/playgo_chunk.cpp index a430b1ac..4e2faea4 100644 --- a/src/core/file_format/playgo_chunk.cpp +++ b/src/core/file_format/playgo_chunk.cpp @@ -60,7 +60,7 @@ bool PlaygoFile::LoadChunks(const Common::FS::IOFile& file) { return false; } -bool PlaygoFile::load_chunk_data(const Common::FS::IOFile& file, const chunk_t& chunk, +bool PlaygoFile::load_chunk_data(const Common::FS::IOFile& file, const chunk_t chunk, std::string& data) { if (file.IsOpen()) { if (file.Seek(chunk.offset)) { diff --git a/src/core/file_format/playgo_chunk.h b/src/core/file_format/playgo_chunk.h index 439e27d0..b6d38e0e 100644 --- a/src/core/file_format/playgo_chunk.h +++ b/src/core/file_format/playgo_chunk.h @@ -123,7 +123,7 @@ public: } private: - bool load_chunk_data(const Common::FS::IOFile& file, const chunk_t& chunk, std::string& data); + bool load_chunk_data(const Common::FS::IOFile& file, const chunk_t chunk, std::string& data); private: PlaygoHeader playgoHeader; diff --git a/src/core/file_format/psf.cpp b/src/core/file_format/psf.cpp index c11dc05c..3d076acd 100644 --- a/src/core/file_format/psf.cpp +++ b/src/core/file_format/psf.cpp @@ -9,7 +9,7 @@ PSF::PSF() = default; PSF::~PSF() = default; -bool PSF::open(const std::string& filepath, std::vector psfBuffer) { +bool PSF::open(const std::string& filepath, const std::vector& psfBuffer) { if (!psfBuffer.empty()) { psf.resize(psfBuffer.size()); psf = psfBuffer; diff --git a/src/core/file_format/psf.h b/src/core/file_format/psf.h index 17e515de..9a162b1d 100644 --- a/src/core/file_format/psf.h +++ b/src/core/file_format/psf.h @@ -35,7 +35,7 @@ public: PSF(); ~PSF(); - bool open(const std::string& filepath, std::vector psfBuffer); + bool open(const std::string& filepath, const std::vector& psfBuffer); std::string GetString(const std::string& key); u32 GetInteger(const std::string& key); diff --git a/src/core/libraries/avplayer/avplayer_source.cpp b/src/core/libraries/avplayer/avplayer_source.cpp index f5479870..2fc360a0 100644 --- a/src/core/libraries/avplayer/avplayer_source.cpp +++ b/src/core/libraries/avplayer/avplayer_source.cpp @@ -82,7 +82,7 @@ static s32 CodecTypeToStreamType(AVMediaType codec_type) { } } -static f32 AVRationalToF32(const AVRational& rational) { +static f32 AVRationalToF32(const AVRational rational) { return f32(rational.num) / rational.den; } diff --git a/src/shader_recompiler/ir/opcodes.h b/src/shader_recompiler/ir/opcodes.h index 66b60221..06f1a611 100644 --- a/src/shader_recompiler/ir/opcodes.h +++ b/src/shader_recompiler/ir/opcodes.h @@ -101,7 +101,7 @@ struct fmt::formatter { return ctx.begin(); } template - auto format(const Shader::IR::Opcode& op, FormatContext& ctx) const { + auto format(const Shader::IR::Opcode op, FormatContext& ctx) const { return fmt::format_to(ctx.out(), "{}", Shader::IR::NameOf(op)); } }; From 12a65e3fb87473f1e15d3868b378325725bedd60 Mon Sep 17 00:00:00 2001 From: georgemoralis Date: Thu, 22 Aug 2024 11:24:31 +0300 Subject: [PATCH 24/26] LLE libc removal --- CMakeLists.txt | 16 +- src/common/config.cpp | 12 - src/common/config.h | 1 - src/core/libraries/libc/libc.cpp | 496 ---------------- src/core/libraries/libc/libc.h | 14 - src/core/libraries/libc/libc_cxa.cpp | 161 ----- src/core/libraries/libc/libc_cxa.h | 15 - src/core/libraries/libc/libc_math.cpp | 55 -- src/core/libraries/libc/libc_math.h | 21 - src/core/libraries/libc/libc_stdio.cpp | 78 --- src/core/libraries/libc/libc_stdio.h | 23 - src/core/libraries/libc/libc_stdlib.cpp | 45 -- src/core/libraries/libc/libc_stdlib.h | 19 - src/core/libraries/libc/libc_string.cpp | 61 -- src/core/libraries/libc/libc_string.h | 24 - src/core/libraries/libc/printf.h | 753 ------------------------ src/core/libraries/libc/va_ctx.h | 110 ---- src/core/libraries/libs.cpp | 4 - src/emulator.cpp | 23 +- 19 files changed, 7 insertions(+), 1924 deletions(-) delete mode 100644 src/core/libraries/libc/libc.cpp delete mode 100644 src/core/libraries/libc/libc.h delete mode 100644 src/core/libraries/libc/libc_cxa.cpp delete mode 100644 src/core/libraries/libc/libc_cxa.h delete mode 100644 src/core/libraries/libc/libc_math.cpp delete mode 100644 src/core/libraries/libc/libc_math.h delete mode 100644 src/core/libraries/libc/libc_stdio.cpp delete mode 100644 src/core/libraries/libc/libc_stdio.h delete mode 100644 src/core/libraries/libc/libc_stdlib.cpp delete mode 100644 src/core/libraries/libc/libc_stdlib.h delete mode 100644 src/core/libraries/libc/libc_string.cpp delete mode 100644 src/core/libraries/libc/libc_string.h delete mode 100644 src/core/libraries/libc/printf.h delete mode 100644 src/core/libraries/libc/va_ctx.h diff --git a/CMakeLists.txt b/CMakeLists.txt index edd67015..478fb43d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -223,21 +223,7 @@ set(VIDEOOUT_LIB src/core/libraries/videoout/buffer.h src/core/libraries/videoout/video_out.h ) -set(LIBC_SOURCES src/core/libraries/libc/libc.cpp - src/core/libraries/libc/libc.h - src/core/libraries/libc/printf.h - src/core/libraries/libc/va_ctx.h - src/core/libraries/libc/libc_cxa.cpp - src/core/libraries/libc/libc_cxa.h - src/core/libraries/libc/libc_stdio.cpp - src/core/libraries/libc/libc_stdio.h - src/core/libraries/libc/libc_math.cpp - src/core/libraries/libc/libc_math.h - src/core/libraries/libc/libc_string.cpp - src/core/libraries/libc/libc_string.h - src/core/libraries/libc/libc_stdlib.cpp - src/core/libraries/libc/libc_stdlib.h - src/core/libraries/libc_internal/libc_internal.cpp +set(LIBC_SOURCES src/core/libraries/libc_internal/libc_internal.cpp src/core/libraries/libc_internal/libc_internal.h ) diff --git a/src/common/config.cpp b/src/common/config.cpp index 6289e2af..ddd1d325 100644 --- a/src/common/config.cpp +++ b/src/common/config.cpp @@ -18,7 +18,6 @@ static std::string logFilter; static std::string logType = "async"; static std::string userName = "shadPS4"; static bool isDebugDump = false; -static bool isLibc = true; static bool isShowSplash = false; static bool isNullGpu = false; static bool shouldDumpShaders = false; @@ -49,10 +48,6 @@ std::vector m_recent_files; // Settings u32 m_language = 1; // english -bool isLleLibc() { - return isLibc; -} - bool isNeoMode() { return isNeo; } @@ -354,12 +349,6 @@ void load(const std::filesystem::path& path) { isDebugDump = toml::find_or(debug, "DebugDump", false); } - if (data.contains("LLE")) { - const toml::value& lle = data.at("LLE"); - - isLibc = toml::find_or(lle, "libc", true); - } - if (data.contains("GUI")) { const toml::value& gui = data.at("GUI"); @@ -425,7 +414,6 @@ void save(const std::filesystem::path& path) { data["Vulkan"]["rdocEnable"] = rdocEnable; data["Vulkan"]["rdocMarkersEnable"] = rdocMarkersEnable; data["Debug"]["DebugDump"] = isDebugDump; - data["LLE"]["libc"] = isLibc; data["GUI"]["theme"] = mw_themes; data["GUI"]["iconSize"] = m_icon_size; data["GUI"]["sliderPos"] = m_slider_pos; diff --git a/src/common/config.h b/src/common/config.h index f2b7fe09..9bf91e69 100644 --- a/src/common/config.h +++ b/src/common/config.h @@ -22,7 +22,6 @@ u32 getScreenHeight(); s32 getGpuId(); bool debugDump(); -bool isLleLibc(); bool showSplash(); bool nullGpu(); bool dumpShaders(); diff --git a/src/core/libraries/libc/libc.cpp b/src/core/libraries/libc/libc.cpp deleted file mode 100644 index dafb16be..00000000 --- a/src/core/libraries/libc/libc.cpp +++ /dev/null @@ -1,496 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include -#include "common/debug.h" -#include "common/logging/log.h" -#include "common/singleton.h" -#include "core/libraries/libc/libc.h" -#include "core/libraries/libc/libc_cxa.h" -#include "core/libraries/libc/libc_math.h" -#include "core/libraries/libc/libc_stdio.h" -#include "core/libraries/libc/libc_stdlib.h" -#include "core/libraries/libc/libc_string.h" -#include "core/libraries/libs.h" - -namespace Libraries::LibC { - -constexpr bool log_file_libc = true; // disable it to disable logging -static u32 g_need_sceLibc = 1; - -using cxa_destructor_func_t = void (*)(void*); - -struct CxaDestructor { - cxa_destructor_func_t destructor_func; - void* destructor_object; - void* module_id; -}; - -struct CContext { - std::vector cxa; -}; - -static PS4_SYSV_ABI int ps4___cxa_atexit(void (*func)(void*), void* arg, void* dso_handle) { - auto* cc = Common::Singleton::Instance(); - CxaDestructor c{}; - c.destructor_func = func; - c.destructor_object = arg; - c.module_id = dso_handle; - cc->cxa.push_back(c); - return 0; -} - -void PS4_SYSV_ABI ps4___cxa_finalize(void* d) { - BREAKPOINT(); -} - -void PS4_SYSV_ABI ps4___cxa_pure_virtual() { - BREAKPOINT(); -} - -static PS4_SYSV_ABI void ps4_init_env() { - LOG_INFO(Lib_LibC, "called"); -} - -static PS4_SYSV_ABI void ps4_catchReturnFromMain(int status) { - LOG_INFO(Lib_LibC, "returned = {}", status); -} - -static PS4_SYSV_ABI void ps4__Assert() { - LOG_INFO(Lib_LibC, "called"); - BREAKPOINT(); -} - -PS4_SYSV_ABI void ps4__ZdlPv(void* ptr) { - std::free(ptr); -} - -PS4_SYSV_ABI void ps4__ZSt11_Xbad_allocv() { - LOG_INFO(Lib_LibC, "called"); - BREAKPOINT(); -} - -PS4_SYSV_ABI void ps4__ZSt14_Xlength_errorPKc() { - LOG_INFO(Lib_LibC, "called"); - BREAKPOINT(); -} - -PS4_SYSV_ABI void* ps4__Znwm(u64 count) { - if (count == 0) { - LOG_INFO(Lib_LibC, "_Znwm count ={}", count); - BREAKPOINT(); - } - void* ptr = std::malloc(count); - return ptr; -} - -static constexpr u16 lowercaseTable[256] = { - 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000A, 0x000B, - 0x000C, 0x000D, 0x000E, 0x000F, 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, - 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F, 0x0020, 0x0021, 0x0022, 0x0023, - 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F, - 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003A, 0x003B, - 0x003C, 0x003D, 0x003E, 0x003F, 0x0040, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, - 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, 0x0070, 0x0071, 0x0072, 0x0073, - 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F, - 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006A, 0x006B, - 0x006C, 0x006D, 0x006E, 0x006F, 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, - 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F, 0x0080, 0x0081, 0x0082, 0x0083, - 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x008D, 0x008E, 0x008F, - 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, 0x0098, 0x0099, 0x009A, 0x009B, - 0x009C, 0x009D, 0x009E, 0x009F, 0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7, - 0x00A8, 0x00A9, 0x00AA, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF, 0x00B0, 0x00B1, 0x00B2, 0x00B3, - 0x00B4, 0x00B5, 0x00B6, 0x00B7, 0x00B8, 0x00B9, 0x00BA, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF, - 0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x00C7, 0x00C8, 0x00C9, 0x00CA, 0x00CB, - 0x00CC, 0x00CD, 0x00CE, 0x00CF, 0x00D0, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x00D7, - 0x00D8, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x00DD, 0x00DE, 0x00DF, 0x00E0, 0x00E1, 0x00E2, 0x00E3, - 0x00E4, 0x00E5, 0x00E6, 0x00E7, 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF, - 0x00F0, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x00F7, 0x00F8, 0x00F9, 0x00FA, 0x00FB, - 0x00FC, 0x00FD, 0x00FE, 0x00FF, -}; - -const PS4_SYSV_ABI u16* ps4__Getptolower() { - return &lowercaseTable[0]; -} - -static constexpr u16 uppercaseTable[256] = { - 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000A, 0x000B, - 0x000C, 0x000D, 0x000E, 0x000F, 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, - 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F, 0x0020, 0x0021, 0x0022, 0x0023, - 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F, - 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003A, 0x003B, - 0x003C, 0x003D, 0x003E, 0x003F, 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, - 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, 0x0050, 0x0051, 0x0052, 0x0053, - 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F, - 0x0060, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004A, 0x004B, - 0x004C, 0x004D, 0x004E, 0x004F, 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, - 0x0058, 0x0059, 0x005A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F, 0x0080, 0x0081, 0x0082, 0x0083, - 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x008D, 0x008E, 0x008F, - 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, 0x0098, 0x0099, 0x009A, 0x009B, - 0x009C, 0x009D, 0x009E, 0x009F, 0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7, - 0x00A8, 0x00A9, 0x00AA, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF, 0x00B0, 0x00B1, 0x00B2, 0x00B3, - 0x00B4, 0x00B5, 0x00B6, 0x00B7, 0x00B8, 0x00B9, 0x00BA, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF, - 0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x00C7, 0x00C8, 0x00C9, 0x00CA, 0x00CB, - 0x00CC, 0x00CD, 0x00CE, 0x00CF, 0x00D0, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x00D7, - 0x00D8, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x00DD, 0x00DE, 0x00DF, 0x00E0, 0x00E1, 0x00E2, 0x00E3, - 0x00E4, 0x00E5, 0x00E6, 0x00E7, 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF, - 0x00F0, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x00F7, 0x00F8, 0x00F9, 0x00FA, 0x00FB, - 0x00FC, 0x00FD, 0x00FE, 0x00FF, -}; - -const PS4_SYSV_ABI u16* ps4__Getptoupper() { - return &uppercaseTable[0]; -} - -namespace CharacterType { -enum : u16 { - HexDigit = 0x1, // '0'-'9', 'A'-'F', 'a'-'f' - Uppercase = 0x2, // 'A'-'Z' - Space = 0x4, - Punctuation = 0x08, - Lowercase = 0x10, // 'a'-'z' - DecimalDigit = 0x20, // '0'-'9' - Control = 0x40, // CR, FF, HT, NL, VT - Control2 = 0x80, // BEL, BS, etc - ExtraSpace = 0x100, - ExtraAlphabetic = 0x200, - ExtraBlank = 0x400 -}; -} - -static constexpr u16 characterTypeTable[256] = { - CharacterType::Control2, - CharacterType::Control2, - CharacterType::Control2, - CharacterType::Control2, - CharacterType::Control2, - CharacterType::Control2, - CharacterType::Control2, - CharacterType::Control2, - CharacterType::Control2, - CharacterType::Control | CharacterType::Control2 | CharacterType::ExtraBlank, - CharacterType::Control | CharacterType::Control2, - CharacterType::Control | CharacterType::Control2, - CharacterType::Control | CharacterType::Control2, - CharacterType::Control | CharacterType::Control2, - CharacterType::Control2, - CharacterType::Control2, - CharacterType::Control2, - CharacterType::Control2, - CharacterType::Control2, - CharacterType::Control2, - CharacterType::Control2, - CharacterType::Control2, - CharacterType::Control2, - CharacterType::Control2, - CharacterType::Control2, - CharacterType::Control2, - CharacterType::Control2, - CharacterType::Control2, - CharacterType::Control2, - CharacterType::Control2, - CharacterType::Control2, - CharacterType::Control2, - CharacterType::Space, // - CharacterType::Punctuation, // ! - CharacterType::Punctuation, // " - CharacterType::Punctuation, // # - CharacterType::Punctuation, // $ - CharacterType::Punctuation, // % - CharacterType::Punctuation, // & - CharacterType::Punctuation, // ' - CharacterType::Punctuation, // ( - CharacterType::Punctuation, // ) - CharacterType::Punctuation, // * - CharacterType::Punctuation, // + - CharacterType::Punctuation, // , - CharacterType::Punctuation, // - - CharacterType::Punctuation, // . - CharacterType::Punctuation, // / - CharacterType::HexDigit | CharacterType::DecimalDigit, // 0 - CharacterType::HexDigit | CharacterType::DecimalDigit, // 1 - CharacterType::HexDigit | CharacterType::DecimalDigit, // 2 - CharacterType::HexDigit | CharacterType::DecimalDigit, // 3 - CharacterType::HexDigit | CharacterType::DecimalDigit, // 4 - CharacterType::HexDigit | CharacterType::DecimalDigit, // 5 - CharacterType::HexDigit | CharacterType::DecimalDigit, // 6 - CharacterType::HexDigit | CharacterType::DecimalDigit, // 7 - CharacterType::HexDigit | CharacterType::DecimalDigit, // 8 - CharacterType::HexDigit | CharacterType::DecimalDigit, // 9 - CharacterType::Punctuation, // : - CharacterType::Punctuation, // ; - CharacterType::Punctuation, // < - CharacterType::Punctuation, // = - CharacterType::Punctuation, // > - CharacterType::Punctuation, // ? - CharacterType::Punctuation, // @ - CharacterType::HexDigit | CharacterType::Uppercase, // A - CharacterType::HexDigit | CharacterType::Uppercase, // B - CharacterType::HexDigit | CharacterType::Uppercase, // C - CharacterType::HexDigit | CharacterType::Uppercase, // D - CharacterType::HexDigit | CharacterType::Uppercase, // E - CharacterType::HexDigit | CharacterType::Uppercase, // F - CharacterType::Uppercase, // G - CharacterType::Uppercase, // H - CharacterType::Uppercase, // I - CharacterType::Uppercase, // J - CharacterType::Uppercase, // K - CharacterType::Uppercase, // L - CharacterType::Uppercase, // M - CharacterType::Uppercase, // N - CharacterType::Uppercase, // O - CharacterType::Uppercase, // P - CharacterType::Uppercase, // Q - CharacterType::Uppercase, // R - CharacterType::Uppercase, // S - CharacterType::Uppercase, // T - CharacterType::Uppercase, // U - CharacterType::Uppercase, // V - CharacterType::Uppercase, // W - CharacterType::Uppercase, // X - CharacterType::Uppercase, // Y - CharacterType::Uppercase, // Z - CharacterType::Punctuation, // [ - CharacterType::Punctuation, // - CharacterType::Punctuation, // ] - CharacterType::Punctuation, // ^ - CharacterType::Punctuation, // _ - CharacterType::Punctuation, // ` - CharacterType::HexDigit | CharacterType::Lowercase, // a - CharacterType::HexDigit | CharacterType::Lowercase, // b - CharacterType::HexDigit | CharacterType::Lowercase, // c - CharacterType::HexDigit | CharacterType::Lowercase, // d - CharacterType::HexDigit | CharacterType::Lowercase, // e - CharacterType::HexDigit | CharacterType::Lowercase, // f - CharacterType::Lowercase, // g - CharacterType::Lowercase, // h - CharacterType::Lowercase, // i - CharacterType::Lowercase, // j - CharacterType::Lowercase, // k - CharacterType::Lowercase, // l - CharacterType::Lowercase, // m - CharacterType::Lowercase, // n - CharacterType::Lowercase, // o - CharacterType::Lowercase, // p - CharacterType::Lowercase, // q - CharacterType::Lowercase, // r - CharacterType::Lowercase, // s - CharacterType::Lowercase, // t - CharacterType::Lowercase, // u - CharacterType::Lowercase, // v - CharacterType::Lowercase, // w - CharacterType::Lowercase, // x - CharacterType::Lowercase, // y - CharacterType::Lowercase, // z - CharacterType::Punctuation, // { - CharacterType::Punctuation, // | - CharacterType::Punctuation, // } - CharacterType::Punctuation, // ~ - CharacterType::Control2, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, -}; - -const PS4_SYSV_ABI u16* ps4__Getpctype() { - return &characterTypeTable[0]; -} - -void libcSymbolsRegister(Core::Loader::SymbolsResolver* sym) { - // cxa functions - LIB_FUNCTION("3GPpjQdAMTw", "libc", 1, "libc", 1, 1, ps4___cxa_guard_acquire); - LIB_FUNCTION("9rAeANT2tyE", "libc", 1, "libc", 1, 1, ps4___cxa_guard_release); - LIB_FUNCTION("2emaaluWzUw", "libc", 1, "libc", 1, 1, ps4___cxa_guard_abort); - - // stdlib functions - LIB_FUNCTION("uMei1W9uyNo", "libc", 1, "libc", 1, 1, ps4_exit); - LIB_FUNCTION("8G2LB+A3rzg", "libc", 1, "libc", 1, 1, ps4_atexit); - LIB_FUNCTION("gQX+4GDQjpM", "libc", 1, "libc", 1, 1, ps4_malloc); - LIB_FUNCTION("tIhsqj0qsFE", "libc", 1, "libc", 1, 1, ps4_free); - LIB_FUNCTION("cpCOXWMgha0", "libc", 1, "libc", 1, 1, ps4_rand); - LIB_FUNCTION("AEJdIVZTEmo", "libc", 1, "libc", 1, 1, ps4_qsort); - - // math functions - LIB_FUNCTION("EH-x713A99c", "libc", 1, "libc", 1, 1, ps4_atan2f); - LIB_FUNCTION("QI-x0SL8jhw", "libc", 1, "libc", 1, 1, ps4_acosf); - LIB_FUNCTION("ZE6RNL+eLbk", "libc", 1, "libc", 1, 1, ps4_tanf); - LIB_FUNCTION("GZWjF-YIFFk", "libc", 1, "libc", 1, 1, ps4_asinf); - LIB_FUNCTION("9LCjpWyQ5Zc", "libc", 1, "libc", 1, 1, ps4_pow); - LIB_FUNCTION("cCXjU72Z0Ow", "libc", 1, "libc", 1, 1, ps4__Sin); - LIB_FUNCTION("ZtjspkJQ+vw", "libc", 1, "libc", 1, 1, ps4__Fsin); - LIB_FUNCTION("dnaeGXbjP6E", "libc", 1, "libc", 1, 1, ps4_exp2); - LIB_FUNCTION("1D0H2KNjshE", "libc", 1, "libc", 1, 1, ps4_powf); - LIB_FUNCTION("DDHG1a6+3q0", "libc", 1, "libc", 1, 1, ps4_roundf); - - // string functions - LIB_FUNCTION("Ovb2dSJOAuE", "libc", 1, "libc", 1, 1, ps4_strcmp); - LIB_FUNCTION("j4ViWNHEgww", "libc", 1, "libc", 1, 1, ps4_strlen); - LIB_FUNCTION("6sJWiWSRuqk", "libc", 1, "libc", 1, 1, ps4_strncpy); - LIB_FUNCTION("+P6FRGH4LfA", "libc", 1, "libc", 1, 1, ps4_memmove); - LIB_FUNCTION("kiZSXIWd9vg", "libc", 1, "libc", 1, 1, ps4_strcpy); - LIB_FUNCTION("Ls4tzzhimqQ", "libc", 1, "libc", 1, 1, ps4_strcat); - LIB_FUNCTION("DfivPArhucg", "libc", 1, "libc", 1, 1, ps4_memcmp); - LIB_FUNCTION("Q3VBxCXhUHs", "libc", 1, "libc", 1, 1, ps4_memcpy); - LIB_FUNCTION("8zTFvBIAIN8", "libc", 1, "libc", 1, 1, ps4_memset); - LIB_FUNCTION("9yDWMxEFdJU", "libc", 1, "libc", 1, 1, ps4_strrchr); - LIB_FUNCTION("aesyjrHVWy4", "libc", 1, "libc", 1, 1, ps4_strncmp); - LIB_FUNCTION("g7zzzLDYGw0", "libc", 1, "libc", 1, 1, ps4_strdup); - - // stdio functions - LIB_FUNCTION("xeYO4u7uyJ0", "libc", 1, "libc", 1, 1, ps4_fopen); - // LIB_FUNCTION("hcuQgD53UxM", "libc", 1, "libc", 1, 1, ps4_printf); - LIB_FUNCTION("Q2V+iqvjgC0", "libc", 1, "libc", 1, 1, ps4_vsnprintf); - LIB_FUNCTION("YQ0navp+YIc", "libc", 1, "libc", 1, 1, ps4_puts); - // LIB_FUNCTION("fffwELXNVFA", "libc", 1, "libc", 1, 1, ps4_fprintf); - LIB_FUNCTION("QMFyLoqNxIg", "libc", 1, "libc", 1, 1, ps4_setvbuf); - LIB_FUNCTION("uodLYyUip20", "libc", 1, "libc", 1, 1, ps4_fclose); - LIB_FUNCTION("rQFVBXp-Cxg", "libc", 1, "libc", 1, 1, ps4_fseek); - LIB_FUNCTION("SHlt7EhOtqA", "libc", 1, "libc", 1, 1, ps4_fgetpos); - LIB_FUNCTION("lbB+UlZqVG0", "libc", 1, "libc", 1, 1, ps4_fread); - LIB_FUNCTION("Qazy8LmXTvw", "libc", 1, "libc", 1, 1, ps4_ftell); - - // misc - LIB_OBJ("P330P3dFF68", "libc", 1, "libc", 1, 1, &g_need_sceLibc); - LIB_OBJ("2sWzhYqFH4E", "libc", 1, "libc", 1, 1, stdout); - LIB_OBJ("H8AprKeZtNg", "libc", 1, "libc", 1, 1, stderr); - LIB_FUNCTION("bzQExy189ZI", "libc", 1, "libc", 1, 1, ps4_init_env); - LIB_FUNCTION("XKRegsFpEpk", "libc", 1, "libc", 1, 1, ps4_catchReturnFromMain); - LIB_FUNCTION("-QgqOT5u2Vk", "libc", 1, "libc", 1, 1, ps4__Assert); - LIB_FUNCTION("z+P+xCnWLBk", "libc", 1, "libc", 1, 1, ps4__ZdlPv); - LIB_FUNCTION("eT2UsmTewbU", "libc", 1, "libc", 1, 1, ps4__ZSt11_Xbad_allocv); - LIB_FUNCTION("tQIo+GIPklo", "libc", 1, "libc", 1, 1, ps4__ZSt14_Xlength_errorPKc); - LIB_FUNCTION("fJnpuVVBbKk", "libc", 1, "libc", 1, 1, ps4__Znwm); - LIB_FUNCTION("tsvEmnenz48", "libc", 1, "libc", 1, 1, ps4___cxa_atexit); - LIB_FUNCTION("H2e8t5ScQGc", "libc", 1, "libc", 1, 1, ps4___cxa_finalize); - LIB_FUNCTION("zr094EQ39Ww", "libc", 1, "libc", 1, 1, ps4___cxa_pure_virtual); - LIB_FUNCTION("1uJgoVq3bQU", "libc", 1, "libc", 1, 1, ps4__Getptolower); - LIB_FUNCTION("rcQCUr0EaRU", "libc", 1, "libc", 1, 1, ps4__Getptoupper); - LIB_FUNCTION("sUP1hBaouOw", "libc", 1, "libc", 1, 1, ps4__Getpctype); -} - -}; // namespace Libraries::LibC diff --git a/src/core/libraries/libc/libc.h b/src/core/libraries/libc/libc.h deleted file mode 100644 index 6504b8d7..00000000 --- a/src/core/libraries/libc/libc.h +++ /dev/null @@ -1,14 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -namespace Core::Loader { -class SymbolsResolver; -} - -namespace Libraries::LibC { - -void libcSymbolsRegister(Core::Loader::SymbolsResolver* sym); - -} // namespace Libraries::LibC diff --git a/src/core/libraries/libc/libc_cxa.cpp b/src/core/libraries/libc/libc_cxa.cpp deleted file mode 100644 index ff10d3f0..00000000 --- a/src/core/libraries/libc/libc_cxa.cpp +++ /dev/null @@ -1,161 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "common/logging/log.h" -#include "core/libraries/libc/libc_cxa.h" - -// adapted from -// https://opensource.apple.com/source/libcppabi/libcppabi-14/src/cxa_guard.cxx.auto.html - -namespace Libraries::LibC { - -// This file implements the __cxa_guard_* functions as defined at: -// http://www.codesourcery.com/public/cxx-abi/abi.html -// -// The goal of these functions is to support thread-safe, one-time -// initialization of function scope variables. The compiler will generate -// code like the following: -// -// if ( obj_guard.first_byte == 0 ) { -// if ( __cxa_guard_acquire(&obj_guard) ) { -// try { -// ... initialize the object ...; -// } -// catch (...) { -// __cxa_guard_abort(&obj_guard); -// throw; -// } -// ... queue object destructor with __cxa_atexit() ...; -// __cxa_guard_release(&obj_guard); -// } -// } -// -// Notes: -// ojb_guard is a 64-bytes in size and statically initialized to zero. -// -// Section 6.7 of the C++ Spec says "If control re-enters the declaration -// recursively while the object is being initialized, the behavior is -// undefined". This implementation calls abort(). -// - -// Note don't use function local statics to avoid use of cxa functions... -static pthread_mutex_t __guard_mutex; -static pthread_once_t __once_control = PTHREAD_ONCE_INIT; - -static void makeRecusiveMutex() { - pthread_mutexattr_t recursiveMutexAttr; - pthread_mutexattr_init(&recursiveMutexAttr); - pthread_mutexattr_settype(&recursiveMutexAttr, PTHREAD_MUTEX_RECURSIVE); - pthread_mutex_init(&__guard_mutex, &recursiveMutexAttr); -} - -__attribute__((noinline)) static pthread_mutex_t* guard_mutex() { - pthread_once(&__once_control, &makeRecusiveMutex); - return &__guard_mutex; -} - -// helper functions for getting/setting flags in guard_object -static bool initializerHasRun(u64* guard_object) { - return (*((u8*)guard_object) != 0); -} - -static void setInitializerHasRun(u64* guard_object) { - *((u8*)guard_object) = 1; -} - -static bool inUse(u64* guard_object) { - return (((u8*)guard_object)[1] != 0); -} - -static void setInUse(u64* guard_object) { - ((u8*)guard_object)[1] = 1; -} - -static void setNotInUse(u64* guard_object) { - ((u8*)guard_object)[1] = 0; -} - -// -// Returns 1 if the caller needs to run the initializer and then either -// call __cxa_guard_release() or __cxa_guard_abort(). If zero is returned, -// then the initializer has already been run. This function blocks -// if another thread is currently running the initializer. This function -// aborts if called again on the same guard object without an intervening -// call to __cxa_guard_release() or __cxa_guard_abort(). -// -int PS4_SYSV_ABI ps4___cxa_guard_acquire(u64* guard_object) { - // Double check that the initializer has not already been run - if (initializerHasRun(guard_object)) - return 0; - - // We now need to acquire a lock that allows only one thread - // to run the initializer. If a different thread calls - // __cxa_guard_acquire() with the same guard object, we want - // that thread to block until this thread is done running the - // initializer and calls __cxa_guard_release(). But if the same - // thread calls __cxa_guard_acquire() with the same guard object, - // we want to abort. - // To implement this we have one global pthread recursive mutex - // shared by all guard objects, but only one at a time. - - int result = ::pthread_mutex_lock(guard_mutex()); - if (result != 0) { - LOG_ERROR(Lib_LibC, "pthread_mutex_lock failed with {}", result); - } - // At this point all other threads will block in __cxa_guard_acquire() - - // Check if another thread has completed initializer run - if (initializerHasRun(guard_object)) { - int result = ::pthread_mutex_unlock(guard_mutex()); - if (result != 0) { - LOG_ERROR(Lib_LibC, "pthread_mutex_lock failed with {}", result); - } - return 0; - } - - // The pthread mutex is recursive to allow other lazy initialized - // function locals to be evaluated during evaluation of this one. - // But if the same thread can call __cxa_guard_acquire() on the - // *same* guard object again, we call abort(); - if (inUse(guard_object)) { - LOG_ERROR(Lib_LibC, - "initializer for function local static variable called enclosing function"); - } - - // mark this guard object as being in use - setInUse(guard_object); - - // return non-zero to tell caller to run initializer - return 1; -} - -// -// Sets the first byte of the guard_object to a non-zero value. -// Releases any locks acquired by __cxa_guard_acquire(). -// -void PS4_SYSV_ABI ps4___cxa_guard_release(u64* guard_object) { - // first mark initalizer as having been run, so - // other threads won't try to re-run it. - setInitializerHasRun(guard_object); - - // release global mutex - int result = ::pthread_mutex_unlock(guard_mutex()); - if (result != 0) { - LOG_ERROR(Lib_LibC, "pthread_mutex_unlock failed with {}", result); - } -} - -// -// Releases any locks acquired by __cxa_guard_acquire(). -// -void PS4_SYSV_ABI ps4___cxa_guard_abort(u64* guard_object) { - int result = ::pthread_mutex_unlock(guard_mutex()); - if (result != 0) { - LOG_ERROR(Lib_LibC, "pthread_mutex_unlock failed with {}", result); - } - - // now reset state, so possible to try to initialize again - setNotInUse(guard_object); -} - -} // namespace Libraries::LibC diff --git a/src/core/libraries/libc/libc_cxa.h b/src/core/libraries/libc/libc_cxa.h deleted file mode 100644 index 2594493e..00000000 --- a/src/core/libraries/libc/libc_cxa.h +++ /dev/null @@ -1,15 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include -#include "common/types.h" - -namespace Libraries::LibC { - -int PS4_SYSV_ABI ps4___cxa_guard_acquire(u64* guard_object); -void PS4_SYSV_ABI ps4___cxa_guard_release(u64* guard_object); -void PS4_SYSV_ABI ps4___cxa_guard_abort(u64* guard_object); - -} // namespace Libraries::LibC diff --git a/src/core/libraries/libc/libc_math.cpp b/src/core/libraries/libc/libc_math.cpp deleted file mode 100644 index 3b51c80e..00000000 --- a/src/core/libraries/libc/libc_math.cpp +++ /dev/null @@ -1,55 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include -#include "common/assert.h" -#include "core/libraries/libc/libc_math.h" - -namespace Libraries::LibC { - -float PS4_SYSV_ABI ps4_atan2f(float y, float x) { - return atan2f(y, x); -} - -float PS4_SYSV_ABI ps4_acosf(float num) { - return acosf(num); -} - -float PS4_SYSV_ABI ps4_tanf(float num) { - return tanf(num); -} - -float PS4_SYSV_ABI ps4_asinf(float num) { - return asinf(num); -} - -double PS4_SYSV_ABI ps4_pow(double base, double exponent) { - return pow(base, exponent); -} - -float PS4_SYSV_ABI ps4_powf(float x, float y) { - return powf(x, y); -} - -float PS4_SYSV_ABI ps4_roundf(float arg) { - return roundf(arg); -} - -double PS4_SYSV_ABI ps4__Sin(double x) { - return sin(x); -} - -float PS4_SYSV_ABI ps4__Fsin(float arg, unsigned int m, int n) { - ASSERT(n == 0); - if (m != 0) { - return cosf(arg); - } else { - return sinf(arg); - } -} - -double PS4_SYSV_ABI ps4_exp2(double arg) { - return exp2(arg); -} - -} // namespace Libraries::LibC diff --git a/src/core/libraries/libc/libc_math.h b/src/core/libraries/libc/libc_math.h deleted file mode 100644 index f73a33f1..00000000 --- a/src/core/libraries/libc/libc_math.h +++ /dev/null @@ -1,21 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include "common/types.h" - -namespace Libraries::LibC { - -float PS4_SYSV_ABI ps4_atan2f(float y, float x); -float PS4_SYSV_ABI ps4_acosf(float num); -float PS4_SYSV_ABI ps4_tanf(float num); -float PS4_SYSV_ABI ps4_asinf(float num); -double PS4_SYSV_ABI ps4_pow(double base, double exponent); -double PS4_SYSV_ABI ps4__Sin(double x); -float PS4_SYSV_ABI ps4__Fsin(float arg, unsigned int, int); -double PS4_SYSV_ABI ps4_exp2(double arg); -float PS4_SYSV_ABI ps4_powf(float x, float y); -float PS4_SYSV_ABI ps4_roundf(float arg); - -} // namespace Libraries::LibC diff --git a/src/core/libraries/libc/libc_stdio.cpp b/src/core/libraries/libc/libc_stdio.cpp deleted file mode 100644 index 2b15bd36..00000000 --- a/src/core/libraries/libc/libc_stdio.cpp +++ /dev/null @@ -1,78 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "common/logging/log.h" -#include "common/singleton.h" -#include "core/file_sys/fs.h" -#include "core/libraries/libc/libc_stdio.h" - -namespace Libraries::LibC { - -std::FILE* PS4_SYSV_ABI ps4_fopen(const char* filename, const char* mode) { - auto* mnt = Common::Singleton::Instance(); - const auto host_path = mnt->GetHostPath(filename).string(); - FILE* f = std::fopen(host_path.c_str(), mode); - if (f != nullptr) { - LOG_INFO(Lib_LibC, "fopen = {}", host_path); - } else { - LOG_INFO(Lib_LibC, "fopen can't open = {}", host_path); - } - return f; -} - -int PS4_SYSV_ABI ps4_fclose(FILE* stream) { - LOG_INFO(Lib_LibC, "callled"); - int ret = 0; - if (stream != nullptr) { - ret = fclose(stream); - } - return ret; -} - -int PS4_SYSV_ABI ps4_setvbuf(FILE* stream, char* buf, int mode, size_t size) { - return setvbuf(stream, buf, mode, size); -} - -int PS4_SYSV_ABI ps4_fseek(FILE* stream, long offset, int whence) { - return fseek(stream, offset, whence); -} - -int PS4_SYSV_ABI ps4_fgetpos(FILE* stream, fpos_t* pos) { - return fgetpos(stream, pos); -} - -std::size_t PS4_SYSV_ABI ps4_fread(void* ptr, size_t size, size_t nmemb, FILE* stream) { - return fread(ptr, size, nmemb, stream); -} - -int PS4_SYSV_ABI ps4_printf(VA_ARGS) { - VA_CTX(ctx); - return printf_ctx(&ctx); -} - -int PS4_SYSV_ABI ps4_fprintf(FILE* file, VA_ARGS) { - int fd = fileno(file); - if (fd == 1 || fd == 2) { // output stdout and stderr to console - VA_CTX(ctx); - return printf_ctx(&ctx); - } else { - VA_CTX(ctx); - char buf[256]; - fprintf_ctx(&ctx, buf); - return fprintf(file, "%s", buf); - } -} - -int PS4_SYSV_ABI ps4_vsnprintf(char* s, size_t n, const char* format, VaList* arg) { - return vsnprintf_ctx(s, n, format, arg); -} - -int PS4_SYSV_ABI ps4_puts(const char* s) { - return std::puts(s); -} - -long PS4_SYSV_ABI ps4_ftell(FILE* stream) { - return ftell(stream); -} - -} // namespace Libraries::LibC diff --git a/src/core/libraries/libc/libc_stdio.h b/src/core/libraries/libc/libc_stdio.h deleted file mode 100644 index 806c17c2..00000000 --- a/src/core/libraries/libc/libc_stdio.h +++ /dev/null @@ -1,23 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include "common/types.h" -#include "core/libraries/libc/printf.h" - -namespace Libraries::LibC { - -std::FILE* PS4_SYSV_ABI ps4_fopen(const char* filename, const char* mode); -int PS4_SYSV_ABI ps4_printf(VA_ARGS); -int PS4_SYSV_ABI ps4_vsnprintf(char* s, size_t n, const char* format, VaList* arg); -int PS4_SYSV_ABI ps4_puts(const char* s); -int PS4_SYSV_ABI ps4_fprintf(FILE* file, VA_ARGS); -int PS4_SYSV_ABI ps4_setvbuf(FILE* stream, char* buf, int mode, size_t size); -int PS4_SYSV_ABI ps4_fclose(FILE* stream); -int PS4_SYSV_ABI ps4_fseek(FILE* stream, long offset, int whence); -int PS4_SYSV_ABI ps4_fgetpos(FILE* stream, fpos_t* pos); -std::size_t PS4_SYSV_ABI ps4_fread(void* ptr, size_t size, size_t nmemb, FILE* stream); -long PS4_SYSV_ABI ps4_ftell(FILE* stream); - -} // namespace Libraries::LibC diff --git a/src/core/libraries/libc/libc_stdlib.cpp b/src/core/libraries/libc/libc_stdlib.cpp deleted file mode 100644 index e7183878..00000000 --- a/src/core/libraries/libc/libc_stdlib.cpp +++ /dev/null @@ -1,45 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include -#include "common/assert.h" -#include "core/libraries/libc/libc_stdlib.h" - -namespace Libraries::LibC { - -void PS4_SYSV_ABI ps4_exit(int code) { - std::exit(code); -} - -int PS4_SYSV_ABI ps4_atexit(void (*func)()) { - int rt = std::atexit(func); - ASSERT_MSG(rt == 0, "atexit returned {}", rt); - return rt; -} - -void* PS4_SYSV_ABI ps4_malloc(size_t size) { - return std::malloc(size); -} - -void PS4_SYSV_ABI ps4_free(void* ptr) { - std::free(ptr); -} - -typedef int(PS4_SYSV_ABI* pfunc_QsortCmp)(const void*, const void*); -thread_local static pfunc_QsortCmp compair_ps4; - -int qsort_compair(const void* arg1, const void* arg2) { - return compair_ps4(arg1, arg2); -} - -void PS4_SYSV_ABI ps4_qsort(void* ptr, size_t count, size_t size, - int(PS4_SYSV_ABI* comp)(const void*, const void*)) { - compair_ps4 = comp; - std::qsort(ptr, count, size, qsort_compair); -} - -int PS4_SYSV_ABI ps4_rand() { - return std::rand(); -} - -} // namespace Libraries::LibC diff --git a/src/core/libraries/libc/libc_stdlib.h b/src/core/libraries/libc/libc_stdlib.h deleted file mode 100644 index 4bb5f6c1..00000000 --- a/src/core/libraries/libc/libc_stdlib.h +++ /dev/null @@ -1,19 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include -#include "common/types.h" - -namespace Libraries::LibC { - -void PS4_SYSV_ABI ps4_exit(int code); -int PS4_SYSV_ABI ps4_atexit(void (*func)()); -void* PS4_SYSV_ABI ps4_malloc(size_t size); -void PS4_SYSV_ABI ps4_free(void* ptr); -void PS4_SYSV_ABI ps4_qsort(void* ptr, size_t count, size_t size, - int(PS4_SYSV_ABI* comp)(const void*, const void*)); -int PS4_SYSV_ABI ps4_rand(); - -} // namespace Libraries::LibC diff --git a/src/core/libraries/libc/libc_string.cpp b/src/core/libraries/libc/libc_string.cpp deleted file mode 100644 index af7524e6..00000000 --- a/src/core/libraries/libc/libc_string.cpp +++ /dev/null @@ -1,61 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include -#include -#include "core/libraries/libc/libc_string.h" - -namespace Libraries::LibC { - -int PS4_SYSV_ABI ps4_memcmp(const void* s1, const void* s2, size_t n) { - return std::memcmp(s1, s2, n); -} - -void* PS4_SYSV_ABI ps4_memcpy(void* dest, const void* src, size_t n) { - return std::memcpy(dest, src, n); -} - -void* PS4_SYSV_ABI ps4_memset(void* s, int c, size_t n) { - return std::memset(s, c, n); -} - -int PS4_SYSV_ABI ps4_strcmp(const char* str1, const char* str2) { - return std::strcmp(str1, str2); -} - -char* PS4_SYSV_ABI ps4_strncpy(char* dest, const char* src, size_t count) { - return std::strncpy(dest, src, count); -} - -void* PS4_SYSV_ABI ps4_memmove(void* dest, const void* src, std::size_t count) { - return std::memmove(dest, src, count); -} - -char* PS4_SYSV_ABI ps4_strcpy(char* dest, const char* src) { - return std::strcpy(dest, src); -} - -char* PS4_SYSV_ABI ps4_strcat(char* dest, const char* src) { - return std::strcat(dest, src); -} - -size_t PS4_SYSV_ABI ps4_strlen(const char* str) { - return std::strlen(str); -} - -char* PS4_SYSV_ABI ps4_strrchr(const char* s, int c) { - return (char*)strrchr(s, c); -} - -int PS4_SYSV_ABI ps4_strncmp(const char* s1, const char* s2, size_t n) { - return strncmp(s1, s2, n); -} - -char* PS4_SYSV_ABI ps4_strdup(const char* str1) { - char* dup = (char*)std::malloc(std::strlen(str1) + 1); - if (dup != NULL) - strcpy(dup, str1); - return dup; -} - -} // namespace Libraries::LibC diff --git a/src/core/libraries/libc/libc_string.h b/src/core/libraries/libc/libc_string.h deleted file mode 100644 index 3e575fa3..00000000 --- a/src/core/libraries/libc/libc_string.h +++ /dev/null @@ -1,24 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include -#include "common/types.h" - -namespace Libraries::LibC { - -int PS4_SYSV_ABI ps4_memcmp(const void* s1, const void* s2, size_t n); -void* PS4_SYSV_ABI ps4_memcpy(void* dest, const void* src, size_t n); -void* PS4_SYSV_ABI ps4_memset(void* s, int c, size_t n); -int PS4_SYSV_ABI ps4_strcmp(const char* str1, const char* str2); -char* PS4_SYSV_ABI ps4_strncpy(char* dest, const char* src, size_t count); -void* PS4_SYSV_ABI ps4_memmove(void* dest, const void* src, std::size_t count); -char* PS4_SYSV_ABI ps4_strcpy(char* destination, const char* source); -char* PS4_SYSV_ABI ps4_strcat(char* dest, const char* src); -size_t PS4_SYSV_ABI ps4_strlen(const char* str); -char* PS4_SYSV_ABI ps4_strrchr(const char* s, int c); -int PS4_SYSV_ABI ps4_strncmp(const char* s1, const char* s2, size_t n); -char* PS4_SYSV_ABI ps4_strdup(const char* str1); - -} // namespace Libraries::LibC diff --git a/src/core/libraries/libc/printf.h b/src/core/libraries/libc/printf.h deleted file mode 100644 index e8465427..00000000 --- a/src/core/libraries/libc/printf.h +++ /dev/null @@ -1,753 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2014-2018 Marco Paland (info@paland.com) -// SPDX-License-Identifier: MIT - -/////////////////////////////////////////////////////////////////////////////// -// \author (c) Marco Paland (info@paland.com) -// 2014-2018, PALANDesign Hannover, Germany -// -// \license The MIT License (MIT) -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// -// \brief Tiny printf, sprintf and snprintf implementation, optimized for speed on -// embedded systems with a very limited resources. -// Use this instead of bloated standard/newlib printf. -// These routines are thread safe and reentrant! -// -/////////////////////////////////////////////////////////////////////////////// -// Vita3K emulator project -// Copyright (C) 2023 Vita3K team -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation; either version 2 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License along -// with this program; if not, write to the Free Software Foundation, Inc., -// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - -// copied from Vita3k project at 6/10/2023 (latest update 30/06/2023) -// modifications for adapting va_args parameters - -#pragma once - -#include - -#include -#include -#include -#include -#include -#include - -#include "va_ctx.h" - -namespace Libraries::LibC { -// ntoa conversion buffer size, this must be big enough to hold -// one converted numeric number including padded zeros (dynamically created on stack) -// 32 byte is a good default -#define PRINTF_NTOA_BUFFER_SIZE 32U - -// ftoa conversion buffer size, this must be big enough to hold -// one converted float number including padded zeros (dynamically created on stack) -// 32 byte is a good default -#define PRINTF_FTOA_BUFFER_SIZE 32U - -// define this to support floating point (%f) -#define PRINTF_SUPPORT_FLOAT - -// define this to support long long types (%llu or %p) -#define PRINTF_SUPPORT_LONG_LONG - -// define this to support the ptrdiff_t type (%t) -// ptrdiff_t is normally defined in as long or long long type -#define PRINTF_SUPPORT_PTRDIFF_T - -/////////////////////////////////////////////////////////////////////////////// - -// internal flag definitions -#define FLAGS_ZEROPAD (1U << 0U) -#define FLAGS_LEFT (1U << 1U) -#define FLAGS_PLUS (1U << 2U) -#define FLAGS_SPACE (1U << 3U) -#define FLAGS_HASH (1U << 4U) -#define FLAGS_UPPERCASE (1U << 5U) -#define FLAGS_CHAR (1U << 6U) -#define FLAGS_SHORT (1U << 7U) -#define FLAGS_LONG (1U << 8U) -#define FLAGS_LONG_LONG (1U << 9U) -#define FLAGS_PRECISION (1U << 10U) -#define FLAGS_WIDTH (1U << 11U) - -// output function type -typedef void (*out_fct_type)(char character, void* buffer, size_t idx, size_t maxlen); - -// wrapper (used as buffer) for output function type -typedef struct { - void (*fct)(char character, void* arg); - void* arg; -} out_fct_wrap_type; - -// internal buffer output -static inline void _out_buffer(char character, void* buffer, size_t idx, size_t maxlen) { - if (idx < maxlen) { - ((char*)buffer)[idx] = character; - } -} - -// internal null output -static inline void _out_null(char character, void* buffer, size_t idx, size_t maxlen) { - (void)character; - (void)buffer; - (void)idx; - (void)maxlen; -} - -// internal output function wrapper -static inline void _out_fct(char character, void* buffer, size_t idx, size_t maxlen) { - (void)idx; - (void)maxlen; - // buffer is the output fct pointer - ((out_fct_wrap_type*)buffer)->fct(character, ((out_fct_wrap_type*)buffer)->arg); -} - -// internal strlen -// \return The length of the string (excluding the terminating 0) -static inline unsigned int _strlen(const char* str) { - const char* s; - for (s = str; *s; ++s) - ; - return (unsigned int)(s - str); -} - -// internal test if char is a digit (0-9) -// \return true if char is a digit -static inline bool _is_digit(char ch) { - return (ch >= '0') && (ch <= '9'); -} - -// internal ASCII string to unsigned int conversion -static inline unsigned int _atoi(const char** str) { - unsigned int i = 0U; - while (_is_digit(**str)) { - i = i * 10U + (unsigned int)(*((*str)++) - '0'); - } - return i; -} - -// internal itoa format -static inline size_t _ntoa_format(out_fct_type out, char* buffer, size_t idx, size_t maxlen, - char* buf, size_t len, bool negative, unsigned int base, - unsigned int prec, unsigned int width, unsigned int flags) { - const size_t start_idx = idx; - - // pad leading zeros - while (!(flags & FLAGS_LEFT) && (len < prec) && (len < PRINTF_NTOA_BUFFER_SIZE)) { - buf[len++] = '0'; - } - while (!(flags & FLAGS_LEFT) && (flags & FLAGS_ZEROPAD) && (len < width) && - (len < PRINTF_NTOA_BUFFER_SIZE)) { - buf[len++] = '0'; - } - - // handle hash - if (flags & FLAGS_HASH) { - if (((len == prec) || (len == width)) && (len > 0U)) { - len--; - if ((base == 16U) && (len > 0U)) { - len--; - } - } - if ((base == 16U) && !(flags & FLAGS_UPPERCASE) && (len < PRINTF_NTOA_BUFFER_SIZE)) { - buf[len++] = 'x'; - } - if ((base == 16U) && (flags & FLAGS_UPPERCASE) && (len < PRINTF_NTOA_BUFFER_SIZE)) { - buf[len++] = 'X'; - } - if (len < PRINTF_NTOA_BUFFER_SIZE) { - buf[len++] = '0'; - } - } - - // handle sign - if ((len == width) && (negative || (flags & FLAGS_PLUS) || (flags & FLAGS_SPACE))) { - len--; - } - if (len < PRINTF_NTOA_BUFFER_SIZE) { - if (negative) { - buf[len++] = '-'; - } else if (flags & FLAGS_PLUS) { - buf[len++] = '+'; // ignore the space if the '+' exists - } else if (flags & FLAGS_SPACE) { - buf[len++] = ' '; - } - } - - // pad spaces up to given width - if (!(flags & FLAGS_LEFT) && !(flags & FLAGS_ZEROPAD)) { - for (size_t i = len; i < width; i++) { - out(' ', buffer, idx++, maxlen); - } - } - - // reverse string - for (size_t i = 0U; i < len; i++) { - out(buf[len - i - 1U], buffer, idx++, maxlen); - } - - // append pad spaces up to given width - if (flags & FLAGS_LEFT) { - while (idx - start_idx < width) { - out(' ', buffer, idx++, maxlen); - } - } - - return idx; -} - -// internal itoa for 'long' type -static inline size_t _ntoa_long(out_fct_type out, char* buffer, size_t idx, size_t maxlen, - unsigned long value, bool negative, unsigned long base, - unsigned int prec, unsigned int width, unsigned int flags) { - char buf[PRINTF_NTOA_BUFFER_SIZE]; - size_t len = 0U; - - // write if precision != 0 and value is != 0 - if (!(flags & FLAGS_PRECISION) || value) { - do { - const char digit = (char)(value % base); - buf[len++] = - digit < 10 ? '0' + digit : (flags & FLAGS_UPPERCASE ? 'A' : 'a') + digit - 10; - value /= base; - } while (value && (len < PRINTF_NTOA_BUFFER_SIZE)); - } - - return _ntoa_format(out, buffer, idx, maxlen, buf, len, negative, (unsigned int)base, prec, - width, flags); -} - -// internal itoa for 'long long' type -#if defined(PRINTF_SUPPORT_LONG_LONG) -static inline size_t _ntoa_long_long(out_fct_type out, char* buffer, size_t idx, size_t maxlen, - unsigned long long value, bool negative, - unsigned long long base, unsigned int prec, unsigned int width, - unsigned int flags) { - char buf[PRINTF_NTOA_BUFFER_SIZE]; - size_t len = 0U; - - // write if precision != 0 and value is != 0 - if (!(flags & FLAGS_PRECISION) || value) { - do { - const char digit = (char)(value % base); - buf[len++] = - digit < 10 ? '0' + digit : (flags & FLAGS_UPPERCASE ? 'A' : 'a') + digit - 10; - value /= base; - } while (value && (len < PRINTF_NTOA_BUFFER_SIZE)); - } - - return _ntoa_format(out, buffer, idx, maxlen, buf, len, negative, (unsigned int)base, prec, - width, flags); -} -#endif // PRINTF_SUPPORT_LONG_LONG - -#if defined(PRINTF_SUPPORT_FLOAT) -static inline size_t _ftoa(out_fct_type out, char* buffer, size_t idx, size_t maxlen, double value, - unsigned int prec, unsigned int width, unsigned int flags) { - char buf[PRINTF_FTOA_BUFFER_SIZE]; - size_t len = 0U; - double diff = 0.0; - - // if input is larger than thres_max, revert to exponential - const double thres_max = (double)0x7FFFFFFF; - - // powers of 10 - static const double pow10[] = {1, 10, 100, 1000, 10000, - 100000, 1000000, 10000000, 100000000, 1000000000}; - - // test for negative - bool negative = false; - if (value < 0) { - negative = true; - value = 0 - value; - } - - // set default precision to 6, if not set explicitly - if (!(flags & FLAGS_PRECISION)) { - prec = 6U; - } - // limit precision to 9, cause a prec >= 10 can lead to overflow errors - while ((len < PRINTF_FTOA_BUFFER_SIZE) && (prec > 9U)) { - buf[len++] = '0'; - prec--; - } - - int whole = (int)value; - double tmp = (value - whole) * pow10[prec]; - unsigned long frac = (unsigned long)tmp; - diff = tmp - frac; - - if (diff > 0.5) { - ++frac; - // handle rollover, e.g. case 0.99 with prec 1 is 1.0 - if (frac >= pow10[prec]) { - frac = 0; - ++whole; - } - } else if ((diff == 0.5) && ((frac == 0U) || (frac & 1U))) { - // if halfway, round up if odd, OR if last digit is 0 - ++frac; - } - - // TBD: for very large numbers switch back to native sprintf for exponentials. Anyone want to - // write code to replace this? Normal printf behavior is to print EVERY whole number digit which - // can be 100s of characters overflowing your buffers == bad - if (value > thres_max) { - return 0U; - } - - if (prec == 0U) { - diff = value - (double)whole; - if (diff > 0.5) { - // greater than 0.5, round up, e.g. 1.6 -> 2 - ++whole; - } else if ((diff == 0.5) && (whole & 1)) { - // exactly 0.5 and ODD, then round up - // 1.5 -> 2, but 2.5 -> 2 - ++whole; - } - } else { - unsigned int count = prec; - // now do fractional part, as an unsigned number - while (len < PRINTF_FTOA_BUFFER_SIZE) { - --count; - buf[len++] = (char)(48U + (frac % 10U)); - if (!(frac /= 10U)) { - break; - } - } - // add extra 0s - while ((len < PRINTF_FTOA_BUFFER_SIZE) && (count-- > 0U)) { - buf[len++] = '0'; - } - if (len < PRINTF_FTOA_BUFFER_SIZE) { - // add decimal - buf[len++] = '.'; - } - } - - // do whole part, number is reversed - while (len < PRINTF_FTOA_BUFFER_SIZE) { - buf[len++] = (char)(48 + (whole % 10)); - if (!(whole /= 10)) { - break; - } - } - - // pad leading zeros - while (!(flags & FLAGS_LEFT) && (flags & FLAGS_ZEROPAD) && (len < width) && - (len < PRINTF_FTOA_BUFFER_SIZE)) { - buf[len++] = '0'; - } - - // handle sign - if ((len == width) && (negative || (flags & FLAGS_PLUS) || (flags & FLAGS_SPACE))) { - len--; - } - if (len < PRINTF_FTOA_BUFFER_SIZE) { - if (negative) { - buf[len++] = '-'; - } else if (flags & FLAGS_PLUS) { - buf[len++] = '+'; // ignore the space if the '+' exists - } else if (flags & FLAGS_SPACE) { - buf[len++] = ' '; - } - } - - // pad spaces up to given width - if (!(flags & FLAGS_LEFT) && !(flags & FLAGS_ZEROPAD)) { - for (size_t i = len; i < width; i++) { - out(' ', buffer, idx++, maxlen); - } - } - - // reverse string - for (size_t i = 0U; i < len; i++) { - out(buf[len - i - 1U], buffer, idx++, maxlen); - } - - // append pad spaces up to given width - if (flags & FLAGS_LEFT) { - while (idx < width) { - out(' ', buffer, idx++, maxlen); - } - } - - return idx; -} -#endif // PRINTF_SUPPORT_FLOAT - -// internal vsnprintf -static inline int _vsnprintf(out_fct_type out, char* buffer, const char* format, VaList* va_list) { - unsigned int flags, width, precision, n; - size_t idx = 0U; - auto maxlen = static_cast(-1); - - if (!buffer) { - // use null output function - out = _out_null; - } - - while (*format) { - // format specifier? %[flags][width][.precision][length] - if (*format != '%') { - // no - out(*format, buffer, idx++, maxlen); - format++; - continue; - } else { - // yes, evaluate it - format++; - } - - // evaluate flags - flags = 0U; - do { - switch (*format) { - case '0': - flags |= FLAGS_ZEROPAD; - format++; - n = 1U; - break; - case '-': - flags |= FLAGS_LEFT; - format++; - n = 1U; - break; - case '+': - flags |= FLAGS_PLUS; - format++; - n = 1U; - break; - case ' ': - flags |= FLAGS_SPACE; - format++; - n = 1U; - break; - case '#': - flags |= FLAGS_HASH; - format++; - n = 1U; - break; - default: - n = 0U; - break; - } - } while (n); - - // evaluate width field - width = 0U; - if (_is_digit(*format)) { - width = _atoi(&format); - } else if (*format == '*') { - const int w = vaArgInteger(va_list); // const int w = va.next(cpu, mem); - - if (w < 0) { - flags |= FLAGS_LEFT; // reverse padding - width = (unsigned int)-w; - } else { - width = (unsigned int)w; - } - format++; - } - - // evaluate precision field - precision = 0U; - if (*format == '.') { - flags |= FLAGS_PRECISION; - format++; - if (_is_digit(*format)) { - precision = _atoi(&format); - } else if (*format == '*') { - precision = - vaArgInteger(va_list); // precision = (unsigned int)va.next(cpu, mem); - format++; - } - } - - // evaluate length field - switch (*format) { - case 'l': - flags |= FLAGS_LONG; - format++; - if (*format == 'l') { - flags |= FLAGS_LONG_LONG; - format++; - } - break; - case 'h': - flags |= FLAGS_SHORT; - format++; - if (*format == 'h') { - flags |= FLAGS_CHAR; - format++; - } - break; -#if defined(PRINTF_SUPPORT_PTRDIFF_T) - case 't': - flags |= (sizeof(ptrdiff_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG); - format++; - break; -#endif - case 'j': - flags |= (sizeof(intmax_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG); - format++; - break; - case 'z': - flags |= (sizeof(size_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG); - format++; - break; - default: - break; - } - - // evaluate specifier - switch (*format) { - case 'd': - case 'i': - case 'u': - case 'x': - case 'X': - case 'o': - case 'b': { - // set the base - unsigned int base; - if (*format == 'x' || *format == 'X') { - base = 16U; - } else if (*format == 'o') { - base = 8U; - } else if (*format == 'b') { - base = 2U; - flags &= ~FLAGS_HASH; // no hash for bin format - } else { - base = 10U; - flags &= ~FLAGS_HASH; // no hash for dec format - } - // uppercase - if (*format == 'X') { - flags |= FLAGS_UPPERCASE; - } - - // no plus or space flag for u, x, X, o, b - if ((*format != 'i') && (*format != 'd')) { - flags &= ~(FLAGS_PLUS | FLAGS_SPACE); - } - - // convert the integer - if ((*format == 'i') || (*format == 'd')) { - // signed - if (flags & FLAGS_LONG_LONG) { -#if defined(PRINTF_SUPPORT_LONG_LONG) - auto value = vaArgLongLong( - va_list); // const long long value = va.next(cpu, mem); - idx = _ntoa_long_long(out, buffer, idx, maxlen, - (unsigned long long)(value > 0 ? value : 0 - value), - value < 0, base, precision, width, flags); -#endif - } else if (flags & FLAGS_LONG) { - auto value = vaArgLong(va_list); // const long value = va.next(cpu, mem); - idx = _ntoa_long(out, buffer, idx, maxlen, - (unsigned long)(value > 0 ? value : 0 - value), value < 0, - base, precision, width, flags); - } else { - // const int value = (flags & FLAGS_CHAR) ? (char)va.next(cpu, mem) : - // (flags & FLAGS_SHORT) ? (short int)va.next(cpu, mem): va.next(cpu, - // mem); - const int value = - (flags & FLAGS_CHAR) ? static_cast(vaArgInteger(va_list)) - : (flags & FLAGS_SHORT) ? static_cast(vaArgInteger(va_list)) - : vaArgInteger(va_list); - idx = _ntoa_long(out, buffer, idx, maxlen, - (unsigned int)(value > 0 ? value : 0 - value), value < 0, base, - precision, width, flags); - } - } else { - // unsigned - if (flags & FLAGS_LONG_LONG) { -#if defined(PRINTF_SUPPORT_LONG_LONG) - // idx = _ntoa_long_long(out, buffer, idx, maxlen, va.next(cpu, mem), false, base, precision, width, flags); - idx = _ntoa_long_long(out, buffer, idx, maxlen, - static_cast(vaArgLongLong(va_list)), false, base, - precision, width, flags); -#endif - } else if (flags & FLAGS_LONG) { - // idx = _ntoa_long(out, buffer, idx, maxlen, va.next(cpu, mem), - // false, base, precision, width, flags); - idx = _ntoa_long(out, buffer, idx, maxlen, static_cast(vaArgLong(va_list)), - false, base, precision, width, flags); - } else { - // const unsigned int value = (flags & FLAGS_CHAR) ? (unsigned - // char)va.next(cpu, mem) : (flags & FLAGS_SHORT) ? - // (unsigned short int)va.next(cpu, mem) : va.next(cpu, mem); - const unsigned int value = - (flags & FLAGS_CHAR) ? static_cast(vaArgInteger(va_list)) - : (flags & FLAGS_SHORT) ? static_cast(vaArgInteger(va_list)) - : static_cast(vaArgInteger(va_list)); - idx = _ntoa_long(out, buffer, idx, maxlen, value, false, base, precision, width, - flags); - } - } - format++; - break; - } -#if defined(PRINTF_SUPPORT_FLOAT) - case 'f': - case 'F': - // idx = _ftoa(out, buffer, idx, maxlen, va.next(cpu, mem), precision, width, - // flags); - idx = _ftoa(out, buffer, idx, maxlen, vaArgDouble(va_list), precision, width, flags); - format++; - break; -#endif // PRINTF_SUPPORT_FLOAT - case 'c': { - unsigned int l = 1U; - // pre padding - if (!(flags & FLAGS_LEFT)) { - while (l++ < width) { - out(' ', buffer, idx++, maxlen); - } - } - // char output - // out((char)va.next(cpu, mem), buffer, idx++, maxlen); - out(static_cast(vaArgInteger(va_list)), buffer, idx++, maxlen); - // post padding - if (flags & FLAGS_LEFT) { - while (l++ < width) { - out(' ', buffer, idx++, maxlen); - } - } - format++; - break; - } - - case 's': { - const char* p = vaArgPtr( - va_list); // const char *p = va.next>(cpu, mem).get(mem); - p = p != nullptr ? p : "(null)"; - unsigned int l = _strlen(p); - // pre padding - if (flags & FLAGS_PRECISION) { - l = (l < precision ? l : precision); - } - if (!(flags & FLAGS_LEFT)) { - while (l++ < width) { - out(' ', buffer, idx++, maxlen); - } - } - // string output - while ((*p != 0) && (!(flags & FLAGS_PRECISION) || precision--)) { - out(*(p++), buffer, idx++, maxlen); - } - // post padding - if (flags & FLAGS_LEFT) { - while (l++ < width) { - out(' ', buffer, idx++, maxlen); - } - } - format++; - break; - } - - case 'p': { - width = sizeof(void*) * 2U; - flags |= FLAGS_ZEROPAD | FLAGS_UPPERCASE; -#if defined(PRINTF_SUPPORT_LONG_LONG) - const bool is_ll = sizeof(uintptr_t) == sizeof(long long); - if (is_ll) { - // idx = _ntoa_long_long(out, buffer, idx, maxlen, - // (uintptr_t)va.next>(cpu, mem).address(), false, 16U, precision, width, - // flags); - idx = _ntoa_long_long(out, buffer, idx, maxlen, - reinterpret_cast(vaArgPtr(va_list)), false, - 16U, precision, width, flags); - } else { -#endif - // idx = _ntoa_long(out, buffer, idx, maxlen, (unsigned - // long)((uintptr_t)va.next>(cpu, mem).address()), false, 16U, precision, - // width, flags); - idx = _ntoa_long( - out, buffer, idx, maxlen, - static_cast(reinterpret_cast(vaArgPtr(va_list))), - false, 16U, precision, width, flags); -#if defined(PRINTF_SUPPORT_LONG_LONG) - } -#endif - format++; - break; - } - - case '%': - out('%', buffer, idx++, maxlen); - format++; - break; - - default: - out(*format, buffer, idx++, maxlen); - format++; - break; - } - } - - // termination - out((char)0, buffer, idx < maxlen ? idx : maxlen - 1U, maxlen); - - // return written chars without terminating \0 - return (int)idx; -} - -static int printf_ctx(VaCtx* ctx) { - const char* format = vaArgPtr(&ctx->va_list); - char buffer[256]; - int result = _vsnprintf(_out_buffer, buffer, format, &ctx->va_list); - printf("%s", buffer); - return result; -} - -static int fprintf_ctx(VaCtx* ctx, char* buf) { - const char* format = vaArgPtr(&ctx->va_list); - char buffer[256]; - int result = _vsnprintf(_out_buffer, buffer, format, &ctx->va_list); - std::strcpy(buf, buffer); - return result; -} - -static int vsnprintf_ctx(char* s, size_t n, const char* format, VaList* arg) { - char buffer[n]; - int result = _vsnprintf(_out_buffer, buffer, format, arg); - std::strcpy(s, buffer); - return result; -} -} // namespace Libraries::LibC diff --git a/src/core/libraries/libc/va_ctx.h b/src/core/libraries/libc/va_ctx.h deleted file mode 100644 index 1c031495..00000000 --- a/src/core/libraries/libc/va_ctx.h +++ /dev/null @@ -1,110 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include -#include "common/types.h" - -#define VA_ARGS \ - uint64_t rdi, uint64_t rsi, uint64_t rdx, uint64_t rcx, uint64_t r8, uint64_t r9, \ - uint64_t overflow_arg_area, __m128 xmm0, __m128 xmm1, __m128 xmm2, __m128 xmm3, \ - __m128 xmm4, __m128 xmm5, __m128 xmm6, __m128 xmm7, ... - -#define VA_CTX(ctx) \ - alignas(16) VaCtx ctx; \ - (ctx).reg_save_area.gp[0] = rdi; \ - (ctx).reg_save_area.gp[1] = rsi; \ - (ctx).reg_save_area.gp[2] = rdx; \ - (ctx).reg_save_area.gp[3] = rcx; \ - (ctx).reg_save_area.gp[4] = r8; \ - (ctx).reg_save_area.gp[5] = r9; \ - (ctx).reg_save_area.fp[0] = xmm0; \ - (ctx).reg_save_area.fp[1] = xmm1; \ - (ctx).reg_save_area.fp[2] = xmm2; \ - (ctx).reg_save_area.fp[3] = xmm3; \ - (ctx).reg_save_area.fp[4] = xmm4; \ - (ctx).reg_save_area.fp[5] = xmm5; \ - (ctx).reg_save_area.fp[6] = xmm6; \ - (ctx).reg_save_area.fp[7] = xmm7; \ - (ctx).va_list.reg_save_area = &(ctx).reg_save_area; \ - (ctx).va_list.gp_offset = offsetof(VaRegSave, gp); \ - (ctx).va_list.fp_offset = offsetof(VaRegSave, fp); \ - (ctx).va_list.overflow_arg_area = &overflow_arg_area; - -namespace Libraries::LibC { - -// https://stackoverflow.com/questions/4958384/what-is-the-format-of-the-x86-64-va-list-structure - -struct VaList { - u32 gp_offset; - u32 fp_offset; - void* overflow_arg_area; - void* reg_save_area; -}; - -struct VaRegSave { - u64 gp[6]; - __m128 fp[8]; -}; - -struct VaCtx { - VaRegSave reg_save_area; - VaList va_list; -}; - -template -T vaArgRegSaveAreaGp(VaList* l) { - auto* addr = reinterpret_cast(static_cast(l->reg_save_area) + l->gp_offset); - l->gp_offset += Size; - return *addr; -} -template -T vaArgOverflowArgArea(VaList* l) { - auto ptr = ((reinterpret_cast(l->overflow_arg_area) + (Align - 1)) & ~(Align - 1)); - auto* addr = reinterpret_cast(ptr); - l->overflow_arg_area = reinterpret_cast(ptr + Size); - return *addr; -} - -template -T vaArgRegSaveAreaFp(VaList* l) { - auto* addr = reinterpret_cast(static_cast(l->reg_save_area) + l->fp_offset); - l->fp_offset += Size; - return *addr; -} - -inline int vaArgInteger(VaList* l) { - if (l->gp_offset <= 40) { - return vaArgRegSaveAreaGp(l); - } - return vaArgOverflowArgArea(l); -} - -inline long long vaArgLongLong(VaList* l) { - if (l->gp_offset <= 40) { - return vaArgRegSaveAreaGp(l); - } - return vaArgOverflowArgArea(l); -} -inline long vaArgLong(VaList* l) { - if (l->gp_offset <= 40) { - return vaArgRegSaveAreaGp(l); - } - return vaArgOverflowArgArea(l); -} - -inline double vaArgDouble(VaList* l) { - if (l->fp_offset <= 160) { - return vaArgRegSaveAreaFp(l); - } - return vaArgOverflowArgArea(l); -} - -template -T* vaArgPtr(VaList* l) { - if (l->gp_offset <= 40) { - return vaArgRegSaveAreaGp(l); - } - return vaArgOverflowArgArea(l); -} - -} // namespace Libraries::LibC diff --git a/src/core/libraries/libs.cpp b/src/core/libraries/libs.cpp index 20efd3c0..f0171199 100644 --- a/src/core/libraries/libs.cpp +++ b/src/core/libraries/libs.cpp @@ -8,7 +8,6 @@ #include "core/libraries/disc_map/disc_map.h" #include "core/libraries/gnmdriver/gnmdriver.h" #include "core/libraries/kernel/libkernel.h" -#include "core/libraries/libc/libc.h" #include "core/libraries/libc_internal/libc_internal.h" #include "core/libraries/libs.h" #include "core/libraries/network/http.h" @@ -46,9 +45,6 @@ void InitHLELibs(Core::Loader::SymbolsResolver* sym) { Libraries::Kernel::LibKernel_Register(sym); Libraries::GnmDriver::RegisterlibSceGnmDriver(sym); Libraries::VideoOut::RegisterLib(sym); - if (!Config::isLleLibc()) { - Libraries::LibC::libcSymbolsRegister(sym); - } // New libraries folder from autogen Libraries::UserService::RegisterlibSceUserService(sym); diff --git a/src/emulator.cpp b/src/emulator.cpp index 99a0fa2a..836b989e 100644 --- a/src/emulator.cpp +++ b/src/emulator.cpp @@ -19,7 +19,6 @@ #include "core/file_sys/fs.h" #include "core/libraries/disc_map/disc_map.h" #include "core/libraries/kernel/thread_management.h" -#include "core/libraries/libc/libc.h" #include "core/libraries/libc_internal/libc_internal.h" #include "core/libraries/libs.h" #include "core/libraries/ngs2/ngs2.h" @@ -63,7 +62,6 @@ Emulator::Emulator() { LOG_INFO(Config, "Vulkan vkValidationGpu: {}", Config::vkValidationGpuEnabled()); LOG_INFO(Config, "Vulkan rdocEnable: {}", Config::isRdocEnabled()); LOG_INFO(Config, "Vulkan rdocMarkersEnable: {}", Config::isMarkersEnabled()); - LOG_INFO(Config, "LLE isLibc: {}", Config::isLleLibc()); // Defer until after logging is initialized. memory = Core::Memory::Instance(); @@ -167,23 +165,14 @@ void Emulator::Run(const std::filesystem::path& file) { // check if we have system modules to load LoadSystemModules(file); - // Check if there is a libc.prx in sce_module folder - bool found = false; - if (Config::isLleLibc()) { - std::filesystem::path sce_module_folder = file.parent_path() / "sce_module"; - if (std::filesystem::is_directory(sce_module_folder)) { - for (const auto& entry : std::filesystem::directory_iterator(sce_module_folder)) { - if (entry.path().filename() == "libc.prx") { - found = true; - } - LOG_INFO(Loader, "Loading {}", entry.path().string().c_str()); - linker->LoadModule(entry.path()); - } + // Load all prx from game's sce_module folder + std::filesystem::path sce_module_folder = file.parent_path() / "sce_module"; + if (std::filesystem::is_directory(sce_module_folder)) { + for (const auto& entry : std::filesystem::directory_iterator(sce_module_folder)) { + LOG_INFO(Loader, "Loading {}", entry.path().string().c_str()); + linker->LoadModule(entry.path()); } } - if (!found) { - Libraries::LibC::libcSymbolsRegister(&linker->GetHLESymbols()); - } // start execution std::jthread mainthread = From d4be3dbb3165d2729cfdd45c96dea1d1f888f25d Mon Sep 17 00:00:00 2001 From: Xphalnos <164882787+Xphalnos@users.noreply.github.com> Date: Thu, 22 Aug 2024 17:35:31 +0200 Subject: [PATCH 25/26] Lot of small fixes --- .gitmodules | 26 ++- CMakeLists.txt | 2 +- documents/Quickstart/1.png | Bin 52618 -> 0 bytes documents/changelog.txt | 60 +++-- documents/readme.txt | 35 --- externals/CMakeLists.txt | 5 - externals/discord-rpc | 1 - src/qt_gui/about_dialog.ui | 4 +- src/qt_gui/settings_dialog.ui | 205 ++++++------------ .../frontend/translate/flat_memory.cpp | 0 10 files changed, 127 insertions(+), 211 deletions(-) delete mode 100644 documents/Quickstart/1.png delete mode 100644 documents/readme.txt delete mode 160000 externals/discord-rpc delete mode 100644 src/shader_recompiler/frontend/translate/flat_memory.cpp diff --git a/.gitmodules b/.gitmodules index 60fb5fbb..94996586 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,66 +1,84 @@ -[submodule "externals/discord-rpc"] - path = externals/discord-rpc - url = https://github.com/shadps4-emu/ext-discord-rpc.git [submodule "externals/cryptopp-cmake"] path = externals/cryptopp-cmake url = https://github.com/shadps4-emu/ext-cryptopp-cmake.git + shallow = true [submodule "externals/cryptopp"] path = externals/cryptopp url = https://github.com/shadps4-emu/ext-cryptopp.git + shallow = true [submodule "externals/cryptoppwin"] path = externals/cryptoppwin url = https://github.com/shadps4-emu/ext-cryptoppwin.git + shallow = true [submodule "externals/zlib-ng"] path = externals/zlib-ng url = https://github.com/shadps4-emu/ext-zlib-ng.git + shallow = true [submodule "externals/sdl3"] path = externals/sdl3 url = https://github.com/shadps4-emu/ext-SDL.git + shallow = true [submodule "externals/fmt"] path = externals/fmt url = https://github.com/shadps4-emu/ext-fmt.git + shallow = true [submodule "externals/vulkan-headers"] path = externals/vulkan-headers url = https://github.com/KhronosGroup/Vulkan-Headers.git + shallow = true [submodule "externals/vma"] path = externals/vma url = https://github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator.git + shallow = true [submodule "externals/glslang"] path = externals/glslang url = https://github.com/KhronosGroup/glslang.git + shallow = true [submodule "externals/robin-map"] path = externals/robin-map url = https://github.com/Tessil/robin-map.git + shallow = true [submodule "externals/xbyak"] path = externals/xbyak url = https://github.com/herumi/xbyak.git + shallow = true [submodule "externals/winpthreads"] path = externals/winpthreads url = https://github.com/shadps4-emu/winpthreads.git + shallow = true [submodule "externals/magic_enum"] path = externals/magic_enum url = https://github.com/Neargye/magic_enum.git + shallow = true [submodule "externals/toml11"] path = externals/toml11 url = https://github.com/ToruNiina/toml11.git + shallow = true [submodule "externals/zydis"] path = externals/zydis url = https://github.com/zyantific/zydis.git + shallow = true [submodule "externals/sirit"] path = externals/sirit url = https://github.com/shadps4-emu/sirit.git + shallow = true [submodule "externals/xxhash"] path = externals/xxhash url = https://github.com/Cyan4973/xxHash.git + shallow = true [submodule "externals/tracy"] path = externals/tracy url = https://github.com/shadps4-emu/tracy.git + shallow = true [submodule "externals/ext-boost"] path = externals/ext-boost url = https://github.com/shadps4-emu/ext-boost.git + shallow = true [submodule "externals/date"] path = externals/date url = https://github.com/HowardHinnant/date.git + shallow = true [submodule "externals/ffmpeg-core"] path = externals/ffmpeg-core - url = https://github.com/shadps4-emu/ext-ffmpeg-core + url = https://github.com/shadps4-emu/ext-ffmpeg-core.git + shallow = true \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 478fb43d..8e349e58 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -262,6 +262,7 @@ set(NP_LIBS src/core/libraries/np_manager/np_manager.cpp src/core/libraries/np_trophy/np_trophy.cpp src/core/libraries/np_trophy/np_trophy.h ) + set(MISC_LIBS src/core/libraries/screenshot/screenshot.cpp src/core/libraries/screenshot/screenshot.h ) @@ -411,7 +412,6 @@ set(SHADER_RECOMPILER src/shader_recompiler/exception.h src/shader_recompiler/backend/spirv/spirv_emit_context.h src/shader_recompiler/frontend/translate/data_share.cpp src/shader_recompiler/frontend/translate/export.cpp - src/shader_recompiler/frontend/translate/flat_memory.cpp src/shader_recompiler/frontend/translate/scalar_alu.cpp src/shader_recompiler/frontend/translate/scalar_memory.cpp src/shader_recompiler/frontend/translate/translate.cpp diff --git a/documents/Quickstart/1.png b/documents/Quickstart/1.png deleted file mode 100644 index 6dc0ce2e39181b3f0d80305582dd0512705de998..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 52618 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 +9=YJPx<_xscU5Q*-i|=V#%C&`wzL*YgOwk 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 true - - true - @@ -106,5 +103,6 @@ + diff --git a/src/qt_gui/settings_dialog.ui b/src/qt_gui/settings_dialog.ui index dbb15206..d11f177d 100644 --- a/src/qt_gui/settings_dialog.ui +++ b/src/qt_gui/settings_dialog.ui @@ -51,8 +51,8 @@ 0 0 - 832 - 418 + 836 + 428 @@ -74,7 +74,7 @@ - + System @@ -112,28 +112,11 @@ - - + - - - - Qt::Orientation::Vertical - - - QSizePolicy::Policy::MinimumExpanding - - - - 0 - 0 - - - - @@ -148,35 +131,39 @@ - - - Enable Fullscreen - - - - - - - Show Splash - - - - - - - Is PS4 Pro - - + + + + + Enable Fullscreen + + + + + + + Show Splash + + + + + + + Is PS4 Pro + + + + - Qt::Orientation::Vertical + Qt::Orientation::Horizontal - 20 - 40 + 40 + 20 @@ -261,19 +248,6 @@ - - - - Qt::Orientation::Vertical - - - - 20 - 40 - - - - @@ -282,34 +256,29 @@ - - - - 0 - - - 0 - - - 0 - - - 0 - - - + + + Qt::Orientation::Vertical + + + + 20 + 40 + + + - + - GPU + Graphics - + - + - + @@ -323,8 +292,8 @@ - - + + 0 @@ -340,44 +309,10 @@ - - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - - - Qt::Orientation::Vertical - - - QSizePolicy::Policy::MinimumExpanding - - - - 0 - 0 - - - - - + @@ -505,26 +440,10 @@ - - - - Qt::Orientation::Vertical - - - QSizePolicy::Policy::MinimumExpanding - - - - 0 - 0 - - - - - + 12 @@ -534,7 +453,7 @@ - Additional Settings + Advanced Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignVCenter @@ -565,17 +484,14 @@ - + Qt::Orientation::Vertical - - QSizePolicy::Policy::MinimumExpanding - - 0 - 0 + 20 + 40 @@ -584,6 +500,19 @@ + + + + Qt::Orientation::Vertical + + + + 20 + 40 + + + + diff --git a/src/shader_recompiler/frontend/translate/flat_memory.cpp b/src/shader_recompiler/frontend/translate/flat_memory.cpp deleted file mode 100644 index e69de29b..00000000 From 3e8d7c2040fbe353a09bd17e458676782f5fb1b9 Mon Sep 17 00:00:00 2001 From: georgemoralis Date: Thu, 22 Aug 2024 19:43:45 +0300 Subject: [PATCH 26/26] possible R2/L2 fix --- src/sdl_window.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/sdl_window.cpp b/src/sdl_window.cpp index 9fd59669..eec31c90 100644 --- a/src/sdl_window.cpp +++ b/src/sdl_window.cpp @@ -306,7 +306,13 @@ void WindowSDL::onGamepadEvent(const SDL_Event* event) { : event->gaxis.axis == SDL_GAMEPAD_AXIS_RIGHT_TRIGGER ? Input::Axis::TriggerRight : Input::Axis::AxisMax; if (axis != Input::Axis::AxisMax) { - controller->Axis(0, axis, Input::GetAxis(-0x8000, 0x8000, event->gaxis.value)); + if (event->gaxis.axis == SDL_GAMEPAD_AXIS_LEFT_TRIGGER || + event->gaxis.axis == SDL_GAMEPAD_AXIS_RIGHT_TRIGGER) { + controller->Axis(0, axis, Input::GetAxis(0, 0x8000, event->gaxis.value)); + + } else { + controller->Axis(0, axis, Input::GetAxis(-0x8000, 0x8000, event->gaxis.value)); + } } break; }