diff --git a/.gitmodules b/.gitmodules index 2ca84d342b..2dd1e55e80 100644 --- a/.gitmodules +++ b/.gitmodules @@ -47,3 +47,6 @@ [submodule "ext/rcheevos"] path = ext/rcheevos url = https://github.com/RetroAchievements/rcheevos.git +[submodule "ext/naett"] + path = ext/naett + url = https://github.com/erkkah/naett.git diff --git a/CMakeLists.txt b/CMakeLists.txt index a2e49dd691..1a91796708 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -182,14 +182,13 @@ if(UNIX AND NOT (APPLE OR ANDROID) AND VULKAN) endif() # add_definitions(-DVK_USE_PLATFORM_XCB_KHR) - if(USE_WAYLAND_WSI) - find_package(Wayland) - if(NOT WAYLAND_FOUND) - message(STATUS "Could not find Wayland libraries, disabling Wayland WSI support for Vulkan.") - else() - include_directories(${WAYLAND_INCLUDE_DIR}) - add_definitions(-DVK_USE_PLATFORM_WAYLAND_KHR) - endif() + find_package(Wayland) + if(NOT WAYLAND_FOUND) + message(STATUS "Could not find Wayland libraries, disabling Wayland WSI support for Vulkan.") + else() + include_directories(${WAYLAND_INCLUDE_DIR}) + add_definitions(-DVK_USE_PLATFORM_WAYLAND_KHR) + add_definitions(-DUSE_WAYLAND_WSI=ON) endif() if(USE_VULKAN_DISPLAY_KHR) @@ -219,6 +218,16 @@ else() set(CoreLinkType STATIC) endif() +if(NOT ANDROID AND NOT WIN32 AND NOT APPLE) + set(HTTPS_NOT_AVAILABLE ON) +endif() + +# Made this flag negative because it's hopefully quite temporary and didn't +# want to have to update all build systems. +if(HTTPS_NOT_AVAILABLE) + add_definitions(-DHTTPS_NOT_AVAILABLE) +endif() + # Work around for some misfeature of the current glslang build system include_directories(ext/glslang) @@ -248,11 +257,14 @@ endif() if(NOT LIBRETRO AND NOT IOS AND NOT MACOSX) find_package(SDL2) + find_package(SDL2_TTF) + find_package(Fontconfig) endif() if(MACOSX AND NOT IOS) if(USE_SYSTEM_LIBSDL2) find_package(SDL2) + find_package(SDL2_TTF) else() find_library(SDL2Fwk SDL2 REQUIRED PATHS SDL/macOS) message(STATUS "found SDL2Fwk=${SDL2Fwk}") @@ -406,7 +418,7 @@ if(NOT MSVC) add_definitions(-Wno-psabi) endif() add_definitions(-D_XOPEN_SOURCE=700) - add_definitions(-D_XOPEN_SOURCE_EXTENDED -D__BSD_VISIBLE=1 -D_BSD_SOURCE) + add_definitions(-D_XOPEN_SOURCE_EXTENDED -D__BSD_VISIBLE=1 -D_BSD_SOURCE -D_DEFAULT_SOURCE) add_definitions(-D_LARGEFILE64_SOURCE=1 -D_FILE_OFFSET_BITS=64) elseif(ANDROID) add_definitions(-fsigned-char) @@ -710,6 +722,10 @@ add_library(Common STATIC Common/Net/HTTPClient.h Common/Net/HTTPHeaders.cpp Common/Net/HTTPHeaders.h + Common/Net/HTTPNaettRequest.cpp + Common/Net/HTTPNaettRequest.h + Common/Net/HTTPRequest.cpp + Common/Net/HTTPRequest.h Common/Net/HTTPServer.cpp Common/Net/HTTPServer.h Common/Net/NetBuffer.cpp @@ -1303,6 +1319,15 @@ else() SDL/SDLVulkanGraphicsContext.cpp ) endif() + if(SDL2_TTF_FOUND) + add_definitions(-DUSE_SDL2_TTF) + set(nativeExtraLibs ${nativeExtraLibs} SDL2_ttf::SDL2_ttf) + + if(FONTCONFIG_FOUND) + add_definitions(-DUSE_SDL2_TTF_FONTCONFIG) + set(nativeExtraLibs ${nativeExtraLibs} Fontconfig::Fontconfig) + endif() + endif() if(APPLE) set(nativeExtra ${nativeExtra} SDL/SDLMain.h @@ -1444,6 +1469,8 @@ add_library(native STATIC ${nativeExtra} Common/Render/Text/draw_text_qt.cpp Common/Render/Text/draw_text_qt.h + Common/Render/Text/draw_text_sdl.cpp + Common/Render/Text/draw_text_sdl.h ext/jpge/jpgd.cpp ext/jpge/jpgd.h ext/jpge/jpge.cpp @@ -1512,6 +1539,8 @@ set(CoreExtra) set(CoreExtraLibs) set(CoreExtra ${CoreExtra} + Core/MIPS/IR/IRAnalysis.cpp + Core/MIPS/IR/IRAnalysis.h Core/MIPS/IR/IRCompALU.cpp Core/MIPS/IR/IRCompBranch.cpp Core/MIPS/IR/IRCompFPU.cpp @@ -1595,6 +1624,19 @@ list(APPEND CoreExtra ) list(APPEND CoreExtra + Core/MIPS/RiscV/RiscVAsm.cpp + Core/MIPS/RiscV/RiscVCompALU.cpp + Core/MIPS/RiscV/RiscVCompBranch.cpp + Core/MIPS/RiscV/RiscVCompFPU.cpp + Core/MIPS/RiscV/RiscVCompLoadStore.cpp + Core/MIPS/RiscV/RiscVCompSystem.cpp + Core/MIPS/RiscV/RiscVCompVec.cpp + Core/MIPS/RiscV/RiscVJit.cpp + Core/MIPS/RiscV/RiscVJit.h + Core/MIPS/RiscV/RiscVRegCache.cpp + Core/MIPS/RiscV/RiscVRegCache.h + Core/MIPS/RiscV/RiscVRegCacheFPU.cpp + Core/MIPS/RiscV/RiscVRegCacheFPU.h GPU/Common/VertexDecoderRiscV.cpp ) @@ -2223,6 +2265,10 @@ endif() target_link_libraries(${CoreLibName} Common native kirk cityhash sfmt19937 xbrz xxhash rcheevos ${GlslangLibs} ${CoreExtraLibs} ${OPENGL_LIBRARIES} ${X11_LIBRARIES} ${CMAKE_DL_LIBS}) +if(NOT HTTPS_NOT_AVAILABLE) + target_link_libraries(${CoreLibName} naett) +endif() + target_compile_features(${CoreLibName} PUBLIC cxx_std_17) if(FFmpeg_FOUND) @@ -2373,6 +2419,8 @@ set(WindowsFiles Windows/Debugger/Debugger_VFPUDlg.h Windows/Debugger/WatchItemWindow.cpp Windows/Debugger/WatchItemWindow.h + Windows/Debugger/EditSymbolsWindow.cpp + Windows/Debugger/EditSymbolsWindow.h Windows/GEDebugger/CtrlDisplayListView.cpp Windows/GEDebugger/SimpleGLWindow.cpp Windows/GEDebugger/TabState.cpp diff --git a/Common/CPUDetect.h b/Common/CPUDetect.h index 1feb20483f..d6ae56fe2e 100644 --- a/Common/CPUDetect.h +++ b/Common/CPUDetect.h @@ -109,7 +109,6 @@ struct CPUInfo { bool RiscV_D; bool RiscV_C; bool RiscV_V; - bool RiscV_B; bool RiscV_Zicsr; bool RiscV_Zba; bool RiscV_Zbb; diff --git a/Common/Common.vcxproj b/Common/Common.vcxproj index 2e5a95a43b..2b80aaa4d2 100644 --- a/Common/Common.vcxproj +++ b/Common/Common.vcxproj @@ -399,6 +399,7 @@ + @@ -490,10 +491,12 @@ + + @@ -847,6 +850,7 @@ + @@ -934,10 +938,12 @@ + + diff --git a/Common/Common.vcxproj.filters b/Common/Common.vcxproj.filters index deb16350c3..5527848423 100644 --- a/Common/Common.vcxproj.filters +++ b/Common/Common.vcxproj.filters @@ -512,6 +512,15 @@ System + + Net + + + ext\naett + + + Net + @@ -959,6 +968,15 @@ System + + Net + + + ext\naett + + + Net + @@ -1069,6 +1087,9 @@ {d6d5f6e0-1c72-496b-af11-6d52d5123033} + + {34f45db9-5c08-49cb-b349-b9e760ce3213} + diff --git a/Common/Data/Collections/FastVec.h b/Common/Data/Collections/FastVec.h index f842f5cfdb..3dab59abb8 100644 --- a/Common/Data/Collections/FastVec.h +++ b/Common/Data/Collections/FastVec.h @@ -88,7 +88,7 @@ public: T &insert(T *iter) { int pos = iter - data_; ExtendByOne(); - if (pos + 1 < size_) { + if (pos + 1 < (int)size_) { memmove(data_ + pos + 1, data_ + pos, (size_ - pos) * sizeof(T)); } return data_[pos]; diff --git a/Common/Data/Text/I18n.cpp b/Common/Data/Text/I18n.cpp index 0b0a3d2957..43b03a38b1 100644 --- a/Common/Data/Text/I18n.cpp +++ b/Common/Data/Text/I18n.cpp @@ -41,6 +41,7 @@ static const char * const g_categoryNames[(size_t)I18NCat::CATEGORY_COUNT] = { "Upgrade", "VR", "Achievements", + "PSPSettings", }; I18NRepo g_i18nrepo; diff --git a/Common/Data/Text/I18n.h b/Common/Data/Text/I18n.h index 15cdf25147..dba943fdbc 100644 --- a/Common/Data/Text/I18n.h +++ b/Common/Data/Text/I18n.h @@ -57,6 +57,7 @@ enum class I18NCat : uint8_t { UPGRADE, VR, ACHIEVEMENTS, + PSPSETTINGS, CATEGORY_COUNT, NONE = CATEGORY_COUNT, }; diff --git a/Common/File/PathBrowser.cpp b/Common/File/PathBrowser.cpp index 36e03dca5c..4aba1b41e4 100644 --- a/Common/File/PathBrowser.cpp +++ b/Common/File/PathBrowser.cpp @@ -38,7 +38,7 @@ bool LoadRemoteFileList(const Path &url, const std::string &userAgent, bool *can http::RequestParams req(baseURL.Resource(), "text/plain, text/html; q=0.9, */*; q=0.8"); if (http.Resolve(baseURL.Host().c_str(), baseURL.Port())) { if (http.Connect(2, 20.0, cancel)) { - http::RequestProgress progress(cancel); + net::RequestProgress progress(cancel); code = http.GET(req, &result, responseHeaders, &progress); http.Disconnect(); } diff --git a/Common/GPU/D3D11/D3D11Loader.cpp b/Common/GPU/D3D11/D3D11Loader.cpp index 0dbea80994..6268f99ed5 100644 --- a/Common/GPU/D3D11/D3D11Loader.cpp +++ b/Common/GPU/D3D11/D3D11Loader.cpp @@ -11,7 +11,6 @@ static HMODULE g_D3DCompileModule; LPCREATEDXGIFACTORY ptr_CreateDXGIFactory; LPD3D11CREATEDEVICE ptr_D3D11CreateDevice; -LPD3D11CREATEDEVICEANDSWAPCHAIN ptr_D3D11CreateDeviceAndSwapChain; pD3DCompile ptr_D3DCompile; LoadD3D11Error LoadD3D11() { @@ -22,7 +21,6 @@ LoadD3D11Error LoadD3D11() { g_D3D11Module = LoadLibrary(L"d3d11.dll"); if (g_D3D11Module) { ptr_D3D11CreateDevice = (LPD3D11CREATEDEVICE)GetProcAddress(g_D3D11Module, "D3D11CreateDevice"); - ptr_D3D11CreateDeviceAndSwapChain = (LPD3D11CREATEDEVICEANDSWAPCHAIN)GetProcAddress(g_D3D11Module, "D3D11CreateDeviceAndSwapChain"); } else { return LoadD3D11Error::FAIL_NO_D3D11; } diff --git a/Common/GPU/D3D11/D3D11Loader.h b/Common/GPU/D3D11/D3D11Loader.h index 750d1da980..080f761c57 100644 --- a/Common/GPU/D3D11/D3D11Loader.h +++ b/Common/GPU/D3D11/D3D11Loader.h @@ -14,12 +14,10 @@ #endif typedef HRESULT (WINAPI *LPCREATEDXGIFACTORY)(REFIID, void **); -typedef HRESULT (WINAPI *LPD3D11CREATEDEVICEANDSWAPCHAIN)(__in_opt IDXGIAdapter *pAdapter, D3D_DRIVER_TYPE DriverType, HMODULE Software, UINT Flags, __in_ecount_opt(FeatureLevels) CONST D3D_FEATURE_LEVEL *pFeatureLevels, UINT FeatureLevels, UINT SDKVersion, __in_opt CONST DXGI_SWAP_CHAIN_DESC *pSwapChainDesc, __out_opt IDXGISwapChain **ppSwapChain, __out_opt ID3D11Device **ppDevice, __out_opt D3D_FEATURE_LEVEL *pFeatureLevel, __out_opt ID3D11DeviceContext **ppImmediateContext); typedef HRESULT (WINAPI *LPD3D11CREATEDEVICE)(IDXGIAdapter *, D3D_DRIVER_TYPE, HMODULE, UINT32, D3D_FEATURE_LEVEL *, UINT, UINT32, ID3D11Device **, D3D_FEATURE_LEVEL *, ID3D11DeviceContext **); extern LPCREATEDXGIFACTORY ptr_CreateDXGIFactory; extern LPD3D11CREATEDEVICE ptr_D3D11CreateDevice; -extern LPD3D11CREATEDEVICEANDSWAPCHAIN ptr_D3D11CreateDeviceAndSwapChain; extern pD3DCompile ptr_D3DCompile; enum class LoadD3D11Error { diff --git a/Common/GPU/D3D11/thin3d_d3d11.cpp b/Common/GPU/D3D11/thin3d_d3d11.cpp index 378829f578..3cad78701b 100644 --- a/Common/GPU/D3D11/thin3d_d3d11.cpp +++ b/Common/GPU/D3D11/thin3d_d3d11.cpp @@ -842,7 +842,7 @@ bool D3D11Texture::FillLevel(ID3D11DeviceContext *context, int level, int w, int for (int y = 0; y < h; ++y) { void *dest = (uint8_t *)mapped.pData + mapped.DepthPitch * s + mapped.RowPitch * y; uint32_t byteStride = w * (uint32_t)DataFormatSizeInBytes(format_); - const void *src = data[level] + byteStride * (y + h * d); + const void *src = data[level] + byteStride * (y + h * s); memcpy(dest, src, byteStride); } } diff --git a/Common/GPU/Vulkan/VulkanFrameData.cpp b/Common/GPU/Vulkan/VulkanFrameData.cpp index c555a296da..472afe69a2 100644 --- a/Common/GPU/Vulkan/VulkanFrameData.cpp +++ b/Common/GPU/Vulkan/VulkanFrameData.cpp @@ -4,6 +4,12 @@ #include "Common/Log.h" #include "Common/StringUtils.h" +#if 0 // def _DEBUG +#define VLOG(...) NOTICE_LOG(G3D, __VA_ARGS__) +#else +#define VLOG(...) +#endif + void CachedReadback::Destroy(VulkanContext *vulkan) { if (buffer) { vulkan->Delete().QueueDeleteBufferAllocation(buffer, allocation); @@ -196,12 +202,16 @@ void FrameData::SubmitPending(VulkanContext *vulkan, FrameSubmitType type, Frame VkResult res; if (fenceToTrigger == fence) { + VLOG("Doing queue submit, fencing frame %d", this->index); // The fence is waited on by the main thread, they are not allowed to access it simultaneously. res = vkQueueSubmit(vulkan->GetGraphicsQueue(), 1, &submit_info, fenceToTrigger); - std::lock_guard lock(fenceMutex); - readyForFence = true; - fenceCondVar.notify_one(); + if (sharedData.useMultiThreading) { + std::lock_guard lock(fenceMutex); + readyForFence = true; + fenceCondVar.notify_one(); + } } else { + VLOG("Doing queue submit, fencing something (%p)", fenceToTrigger); res = vkQueueSubmit(vulkan->GetGraphicsQueue(), 1, &submit_info, fenceToTrigger); } @@ -219,7 +229,7 @@ void FrameData::SubmitPending(VulkanContext *vulkan, FrameSubmitType type, Frame } } -void FrameDataShared::Init(VulkanContext *vulkan) { +void FrameDataShared::Init(VulkanContext *vulkan, bool useMultiThreading) { VkSemaphoreCreateInfo semaphoreCreateInfo = { VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO }; semaphoreCreateInfo.flags = 0; VkResult res = vkCreateSemaphore(vulkan->GetDevice(), &semaphoreCreateInfo, nullptr, &acquireSemaphore); @@ -230,6 +240,8 @@ void FrameDataShared::Init(VulkanContext *vulkan) { // This fence is used for synchronizing readbacks. Does not need preinitialization. readbackFence = vulkan->CreateFence(false); vulkan->SetDebugName(readbackFence, VK_OBJECT_TYPE_FENCE, "readbackFence"); + + this->useMultiThreading = useMultiThreading; } void FrameDataShared::Destroy(VulkanContext *vulkan) { diff --git a/Common/GPU/Vulkan/VulkanFrameData.h b/Common/GPU/Vulkan/VulkanFrameData.h index 6fe679a167..63176cf469 100644 --- a/Common/GPU/Vulkan/VulkanFrameData.h +++ b/Common/GPU/Vulkan/VulkanFrameData.h @@ -53,8 +53,9 @@ struct FrameDataShared { // For synchronous readbacks. VkFence readbackFence = VK_NULL_HANDLE; + bool useMultiThreading; - void Init(VulkanContext *vulkan); + void Init(VulkanContext *vulkan, bool useMultiThreading); void Destroy(VulkanContext *vulkan); }; diff --git a/Common/GPU/Vulkan/VulkanFramebuffer.cpp b/Common/GPU/Vulkan/VulkanFramebuffer.cpp index e9c68794a5..975dab04bd 100644 --- a/Common/GPU/Vulkan/VulkanFramebuffer.cpp +++ b/Common/GPU/Vulkan/VulkanFramebuffer.cpp @@ -270,6 +270,12 @@ VkRenderPass CreateRenderPass(VulkanContext *vulkan, const RPKey &key, RenderPas bool multiview = RenderPassTypeHasMultiView(rpType); bool multisample = RenderPassTypeHasMultisample(rpType); + _dbg_assert_(!(isBackbuffer && multisample)); + + if (isBackbuffer) { + _dbg_assert_(key.depthLoadAction == VKRRenderPassLoadAction::CLEAR); + } + if (multiview) { // TODO: Assert that the device has multiview support enabled. } @@ -296,7 +302,7 @@ VkRenderPass CreateRenderPass(VulkanContext *vulkan, const RPKey &key, RenderPas attachments[attachmentCount].storeOp = ConvertStoreAction(key.depthStoreAction); attachments[attachmentCount].stencilLoadOp = multisample ? VK_ATTACHMENT_LOAD_OP_DONT_CARE : ConvertLoadAction(key.stencilLoadAction); attachments[attachmentCount].stencilStoreOp = ConvertStoreAction(key.stencilStoreAction); - attachments[attachmentCount].initialLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + attachments[attachmentCount].initialLayout = isBackbuffer ? VK_IMAGE_LAYOUT_UNDEFINED : VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; attachments[attachmentCount].finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; attachmentCount++; } diff --git a/Common/GPU/Vulkan/VulkanRenderManager.cpp b/Common/GPU/Vulkan/VulkanRenderManager.cpp index 19b106ac9d..4b64beb400 100644 --- a/Common/GPU/Vulkan/VulkanRenderManager.cpp +++ b/Common/GPU/Vulkan/VulkanRenderManager.cpp @@ -247,15 +247,16 @@ bool VKRComputePipeline::CreateAsync(VulkanContext *vulkan) { return true; } -VulkanRenderManager::VulkanRenderManager(VulkanContext *vulkan) +VulkanRenderManager::VulkanRenderManager(VulkanContext *vulkan, bool useThread) : vulkan_(vulkan), queueRunner_(vulkan), initTimeMs_("initTimeMs"), totalGPUTimeMs_("totalGPUTimeMs"), - renderCPUTimeMs_("renderCPUTimeMs") + renderCPUTimeMs_("renderCPUTimeMs"), + useRenderThread_(useThread) { inflightFramesAtStart_ = vulkan_->GetInflightFrames(); - frameDataShared_.Init(vulkan); + frameDataShared_.Init(vulkan, useThread); for (int i = 0; i < inflightFramesAtStart_; i++) { frameData_[i].Init(vulkan, i); @@ -292,12 +293,14 @@ bool VulkanRenderManager::CreateBackbuffers() { outOfDateFrames_ = 0; - // Start the thread. + // Start the thread(s). if (HasBackbuffers()) { run_ = true; // For controlling the compiler thread's exit - INFO_LOG(G3D, "Starting Vulkan submission thread"); - thread_ = std::thread(&VulkanRenderManager::ThreadFunc, this); + if (useRenderThread_) { + INFO_LOG(G3D, "Starting Vulkan submission thread"); + thread_ = std::thread(&VulkanRenderManager::ThreadFunc, this); + } INFO_LOG(G3D, "Starting Vulkan compiler thread"); compileThread_ = std::thread(&VulkanRenderManager::CompileThreadFunc, this); } @@ -306,7 +309,8 @@ bool VulkanRenderManager::CreateBackbuffers() { // Called from main thread. void VulkanRenderManager::StopThread() { - { + if (useRenderThread_) { + _dbg_assert_(thread_.joinable()); // Tell the render thread to quit when it's done. VKRRenderThreadTask *task = new VKRRenderThreadTask(VKRRunType::EXIT); task->frame = vulkan_->GetCurFrame(); @@ -319,7 +323,9 @@ void VulkanRenderManager::StopThread() { run_ = false; // Stop the thread. - thread_.join(); + if (useRenderThread_) { + thread_.join(); + } for (int i = 0; i < vulkan_->GetInflightFrames(); i++) { auto &frameData = frameData_[i]; @@ -492,6 +498,8 @@ void VulkanRenderManager::DrainCompileQueue() { void VulkanRenderManager::ThreadFunc() { SetCurrentThreadName("RenderMan"); while (true) { + _dbg_assert_(useRenderThread_); + // Pop a task of the queue and execute it. VKRRenderThreadTask *task = nullptr; { @@ -534,7 +542,7 @@ void VulkanRenderManager::BeginFrame(bool enableProfiling, bool enableLogProfile // Makes sure the submission from the previous time around has happened. Otherwise // we are not allowed to wait from another thread here.. - { + if (useRenderThread_) { std::unique_lock lock(frameData.fenceMutex); while (!frameData.readyForFence) { frameData.fenceCondVar.wait(lock); @@ -1263,11 +1271,16 @@ void VulkanRenderManager::Finish() { VLOG("PUSH: Frame[%d]", curFrame); VKRRenderThreadTask *task = new VKRRenderThreadTask(VKRRunType::PRESENT); task->frame = curFrame; - { + if (useRenderThread_) { std::unique_lock lock(pushMutex_); renderThreadQueue_.push(task); renderThreadQueue_.back()->steps = std::move(steps_); pushCondVar_.notify_one(); + } else { + // Just do it! + task->steps = std::move(steps_); + Run(*task); + delete task; } steps_.clear(); @@ -1348,7 +1361,7 @@ void VulkanRenderManager::Run(VKRRenderThreadTask &task) { // The submit will trigger the readbackFence, and also do the wait for it. frameData.SubmitPending(vulkan_, FrameSubmitType::Sync, frameDataShared_); - { + if (useRenderThread_) { std::unique_lock lock(syncMutex_); syncCondVar_.notify_one(); } @@ -1374,24 +1387,34 @@ void VulkanRenderManager::FlushSync() { int curFrame = vulkan_->GetCurFrame(); FrameData &frameData = frameData_[curFrame]; - { - VLOG("PUSH: Frame[%d]", curFrame); + if (useRenderThread_) { + { + VLOG("PUSH: Frame[%d]", curFrame); + VKRRenderThreadTask *task = new VKRRenderThreadTask(VKRRunType::SYNC); + task->frame = curFrame; + std::unique_lock lock(pushMutex_); + renderThreadQueue_.push(task); + renderThreadQueue_.back()->steps = std::move(steps_); + pushCondVar_.notify_one(); + steps_.clear(); + } + + { + std::unique_lock lock(syncMutex_); + // Wait for the flush to be hit, since we're syncing. + while (!frameData.syncDone) { + VLOG("PUSH: Waiting for frame[%d].syncDone = 1 (sync)", curFrame); + syncCondVar_.wait(lock); + } + frameData.syncDone = false; + } + } else { VKRRenderThreadTask *task = new VKRRenderThreadTask(VKRRunType::SYNC); task->frame = curFrame; - std::unique_lock lock(pushMutex_); - renderThreadQueue_.push(task); - renderThreadQueue_.back()->steps = std::move(steps_); - pushCondVar_.notify_one(); - } - - { - std::unique_lock lock(syncMutex_); - // Wait for the flush to be hit, since we're syncing. - while (!frameData.syncDone) { - VLOG("PUSH: Waiting for frame[%d].syncDone = 1 (sync)", curFrame); - syncCondVar_.wait(lock); - } - frameData.syncDone = false; + task->steps = std::move(steps_); + Run(*task); + delete task; + steps_.clear(); } } diff --git a/Common/GPU/Vulkan/VulkanRenderManager.h b/Common/GPU/Vulkan/VulkanRenderManager.h index 3343a6781a..012a098ba1 100644 --- a/Common/GPU/Vulkan/VulkanRenderManager.h +++ b/Common/GPU/Vulkan/VulkanRenderManager.h @@ -181,7 +181,7 @@ struct CompileQueueEntry { class VulkanRenderManager { public: - VulkanRenderManager(VulkanContext *vulkan); + VulkanRenderManager(VulkanContext *vulkan, bool useThread); ~VulkanRenderManager(); // Makes sure that the GPU has caught up enough that we can start writing buffers of this frame again. @@ -489,6 +489,8 @@ private: bool insideFrame_ = false; bool run_ = false; + bool useRenderThread_ = true; + // This is the offset within this frame, in case of a mid-frame sync. VKRStep *curRenderStep_ = nullptr; bool curStepHasViewport_ = false; diff --git a/Common/GPU/Vulkan/thin3d_vulkan.cpp b/Common/GPU/Vulkan/thin3d_vulkan.cpp index db4cae6bee..325b894d17 100644 --- a/Common/GPU/Vulkan/thin3d_vulkan.cpp +++ b/Common/GPU/Vulkan/thin3d_vulkan.cpp @@ -384,7 +384,7 @@ class VKFramebuffer; class VKContext : public DrawContext { public: - VKContext(VulkanContext *vulkan); + VKContext(VulkanContext *vulkan, bool useRenderThread); ~VKContext(); void DebugAnnotate(const char *annotation) override; @@ -857,8 +857,8 @@ static DataFormat DataFormatFromVulkanDepth(VkFormat fmt) { return DataFormat::UNDEFINED; } -VKContext::VKContext(VulkanContext *vulkan) - : vulkan_(vulkan), renderManager_(vulkan) { +VKContext::VKContext(VulkanContext *vulkan, bool useRenderThread) + : vulkan_(vulkan), renderManager_(vulkan, useRenderThread) { shaderLanguageDesc_.Init(GLSL_VULKAN); VkFormat depthStencilFormat = vulkan->GetDeviceInfo().preferredDepthStencilFormat; @@ -1582,8 +1582,8 @@ void VKContext::Clear(int clearMask, uint32_t colorval, float depthVal, int sten renderManager_.Clear(colorval, depthVal, stencilVal, mask); } -DrawContext *T3DCreateVulkanContext(VulkanContext *vulkan) { - return new VKContext(vulkan); +DrawContext *T3DCreateVulkanContext(VulkanContext *vulkan, bool useRenderThread) { + return new VKContext(vulkan, useRenderThread); } void AddFeature(std::vector &features, const char *name, VkBool32 available, VkBool32 enabled) { diff --git a/Common/GPU/thin3d_create.h b/Common/GPU/thin3d_create.h index fccb3b076c..5f304af090 100644 --- a/Common/GPU/thin3d_create.h +++ b/Common/GPU/thin3d_create.h @@ -31,6 +31,6 @@ DrawContext *T3DCreateDX9Context(IDirect3D9 *d3d, IDirect3D9Ex *d3dEx, int adapt DrawContext *T3DCreateD3D11Context(ID3D11Device *device, ID3D11DeviceContext *context, ID3D11Device1 *device1, ID3D11DeviceContext1 *context1, D3D_FEATURE_LEVEL featureLevel, HWND hWnd, std::vector adapterNames); #endif -DrawContext *T3DCreateVulkanContext(VulkanContext *context); +DrawContext *T3DCreateVulkanContext(VulkanContext *context, bool useRenderThread); } // namespace Draw diff --git a/Common/Net/HTTPClient.cpp b/Common/Net/HTTPClient.cpp index 74d3784ab7..0d76d86c5f 100644 --- a/Common/Net/HTTPClient.cpp +++ b/Common/Net/HTTPClient.cpp @@ -2,6 +2,7 @@ #include "Common/TimeUtil.h" #include "Common/StringUtils.h" +#include "Common/System/OSD.h" #ifndef _WIN32 #include @@ -217,7 +218,7 @@ bool GetHeaderValue(const std::vector &responseHeaders, const std:: return found; } -void DeChunk(Buffer *inbuffer, Buffer *outbuffer, int contentLength, float *progress) { +void DeChunk(Buffer *inbuffer, Buffer *outbuffer, int contentLength) { int dechunkedBytes = 0; while (true) { std::string line; @@ -236,14 +237,11 @@ void DeChunk(Buffer *inbuffer, Buffer *outbuffer, int contentLength, float *prog return; } dechunkedBytes += chunkSize; - if (progress && contentLength) { - *progress = (float)dechunkedBytes / contentLength; - } inbuffer->Skip(2); } } -int Client::GET(const RequestParams &req, Buffer *output, std::vector &responseHeaders, RequestProgress *progress) { +int Client::GET(const RequestParams &req, Buffer *output, std::vector &responseHeaders, net::RequestProgress *progress) { const char *otherHeaders = "Accept-Encoding: gzip\r\n"; int err = SendRequest("GET", req, otherHeaders, progress); @@ -264,13 +262,13 @@ int Client::GET(const RequestParams &req, Buffer *output, std::vector responseHeaders; int code = GET(req, output, responseHeaders, progress); return code; } -int Client::POST(const RequestParams &req, const std::string &data, const std::string &mime, Buffer *output, RequestProgress *progress) { +int Client::POST(const RequestParams &req, const std::string &data, const std::string &mime, Buffer *output, net::RequestProgress *progress) { char otherHeaders[2048]; if (mime.empty()) { snprintf(otherHeaders, sizeof(otherHeaders), "Content-Length: %lld\r\n", (long long)data.size()); @@ -296,16 +294,16 @@ int Client::POST(const RequestParams &req, const std::string &data, const std::s return code; } -int Client::POST(const RequestParams &req, const std::string &data, Buffer *output, RequestProgress *progress) { +int Client::POST(const RequestParams &req, const std::string &data, Buffer *output, net::RequestProgress *progress) { return POST(req, data, "", output, progress); } -int Client::SendRequest(const char *method, const RequestParams &req, const char *otherHeaders, RequestProgress *progress) { +int Client::SendRequest(const char *method, const RequestParams &req, const char *otherHeaders, net::RequestProgress *progress) { return SendRequestWithData(method, req, "", otherHeaders, progress); } -int Client::SendRequestWithData(const char *method, const RequestParams &req, const std::string &data, const char *otherHeaders, RequestProgress *progress) { - progress->progress = 0.01f; +int Client::SendRequestWithData(const char *method, const RequestParams &req, const std::string &data, const char *otherHeaders, net::RequestProgress *progress) { + progress->Update(0, 0, false); net::Buffer buffer; const char *tpl = @@ -331,7 +329,7 @@ int Client::SendRequestWithData(const char *method, const RequestParams &req, co return 0; } -int Client::ReadResponseHeaders(net::Buffer *readbuf, std::vector &responseHeaders, RequestProgress *progress) { +int Client::ReadResponseHeaders(net::Buffer *readbuf, std::vector &responseHeaders, net::RequestProgress *progress) { // Snarf all the data we can into RAM. A little unsafe but hey. static constexpr float CANCEL_INTERVAL = 0.25f; bool ready = false; @@ -371,7 +369,7 @@ int Client::ReadResponseHeaders(net::Buffer *readbuf, std::vector & while (true) { int sz = readbuf->TakeLineCRLF(&line); - if (!sz) + if (!sz || sz < 0) break; responseHeaders.push_back(line); } @@ -384,7 +382,9 @@ int Client::ReadResponseHeaders(net::Buffer *readbuf, std::vector & return code; } -int Client::ReadResponseEntity(net::Buffer *readbuf, const std::vector &responseHeaders, Buffer *output, RequestProgress *progress) { +int Client::ReadResponseEntity(net::Buffer *readbuf, const std::vector &responseHeaders, Buffer *output, net::RequestProgress *progress) { + _dbg_assert_(progress->cancelled); + bool gzip = false; bool chunked = false; int contentLength = 0; @@ -412,30 +412,18 @@ int Client::ReadResponseEntity(net::Buffer *readbuf, const std::vectorprogress = 0.1f; - } - - if (!contentLength) { - // No way to know how far along we are. Let's just not update the progress counter. - if (!readbuf->ReadAllWithProgress(sock(), contentLength, nullptr, &progress->kBps, progress->cancelled)) - return -1; - } else { - // Let's read in chunks, updating progress between each. - if (!readbuf->ReadAllWithProgress(sock(), contentLength, &progress->progress, &progress->kBps, progress->cancelled)) - return -1; - } + if (!readbuf->ReadAllWithProgress(sock(), contentLength, progress)) + return -1; // output now contains the rest of the reply. Dechunk it. if (!output->IsVoid()) { if (chunked) { - DeChunk(readbuf, output, contentLength, &progress->progress); + DeChunk(readbuf, output, contentLength); } else { output->Append(*readbuf); } @@ -447,30 +435,32 @@ int Client::ReadResponseEntity(net::Buffer *readbuf, const std::vectorprogress = 0.0f; + progress->Update(0, 0, true); return -1; } output->Append(decompressed); } } - progress->progress = 1.0f; + progress->Update(contentLength, contentLength, true); return 0; } -Download::Download(RequestMethod method, const std::string &url, const std::string &postData, const std::string &postMime, const Path &outfile) - : method_(method), progress_(&cancelled_), url_(url), postData_(postData), postMime_(postMime), outfile_(outfile) { +HTTPRequest::HTTPRequest(RequestMethod method, const std::string &url, const std::string &postData, const std::string &postMime, const Path &outfile, ProgressBarMode progressBarMode, const std::string &name) + : Request(method, url, name, &cancelled_, progressBarMode), postData_(postData), postMime_(postMime), outfile_(outfile) { } -Download::~Download() { +HTTPRequest::~HTTPRequest() { + g_OSD.RemoveProgressBar(url_, Failed() ? false : true, 0.5f); + _assert_msg_(joined_, "Download destructed without join"); } -void Download::Start() { - thread_ = std::thread(std::bind(&Download::Do, this)); +void HTTPRequest::Start() { + thread_ = std::thread(std::bind(&HTTPRequest::Do, this)); } -void Download::Join() { +void HTTPRequest::Join() { if (joined_) { ERROR_LOG(IO, "Already joined thread!"); } @@ -478,13 +468,13 @@ void Download::Join() { joined_ = true; } -void Download::SetFailed(int code) { +void HTTPRequest::SetFailed(int code) { failed_ = true; - progress_.progress = 1.0f; + progress_.Update(0, 0, true); completed_ = true; } -int Download::Perform(const std::string &url) { +int HTTPRequest::Perform(const std::string &url) { Url fileUrl(url); if (!fileUrl.Valid()) { return -1; @@ -521,7 +511,7 @@ int Download::Perform(const std::string &url) { } } -std::string Download::RedirectLocation(const std::string &baseUrl) { +std::string HTTPRequest::RedirectLocation(const std::string &baseUrl) { std::string redirectUrl; if (GetHeaderValue(responseHeaders_, "Location", &redirectUrl)) { Url url(baseUrl); @@ -532,11 +522,10 @@ std::string Download::RedirectLocation(const std::string &baseUrl) { return redirectUrl; } -void Download::Do() { - SetCurrentThreadName("Downloader::Do"); +void HTTPRequest::Do() { + SetCurrentThreadName("HTTPDownload::Do"); AndroidJNIThreadContext jniContext; - resultCode_ = 0; std::string downloadURL = url_; @@ -575,97 +564,9 @@ void Download::Do() { resultCode_ = resultCode; } - progress_.progress = 1.0f; - // Set this last to ensure no race conditions when checking Done. Users must always check // Done before looking at the result code. completed_ = true; } -std::shared_ptr Downloader::StartDownload(const std::string &url, const Path &outfile, const char *acceptMime) { - std::shared_ptr dl(new Download(RequestMethod::GET, url, "", "", outfile)); - if (!userAgent_.empty()) - dl->SetUserAgent(userAgent_); - if (acceptMime) - dl->SetAccept(acceptMime); - newDownloads_.push_back(dl); - dl->Start(); - return dl; -} - -std::shared_ptr Downloader::StartDownloadWithCallback( - const std::string &url, - const Path &outfile, - std::function callback, - const char *acceptMime) { - std::shared_ptr dl(new Download(RequestMethod::GET, url, "", "", outfile)); - if (!userAgent_.empty()) - dl->SetUserAgent(userAgent_); - if (acceptMime) - dl->SetAccept(acceptMime); - dl->SetCallback(callback); - newDownloads_.push_back(dl); - dl->Start(); - return dl; -} - -std::shared_ptr Downloader::AsyncPostWithCallback( - const std::string &url, - const std::string &postData, - const std::string &postMime, - std::function callback) { - std::shared_ptr dl(new Download(RequestMethod::POST, url, postData, postMime, Path())); - if (!userAgent_.empty()) - dl->SetUserAgent(userAgent_); - dl->SetCallback(callback); - newDownloads_.push_back(dl); - dl->Start(); - return dl; -} - -void Downloader::Update() { - for (auto iter : newDownloads_) { - downloads_.push_back(iter); - } - newDownloads_.clear(); - - restart: - for (size_t i = 0; i < downloads_.size(); i++) { - auto dl = downloads_[i]; - if (dl->Done()) { - dl->RunCallback(); - dl->Join(); - downloads_.erase(downloads_.begin() + i); - goto restart; - } - } -} - -void Downloader::WaitForAll() { - // TODO: Should lock? Though, OK if called from main thread, where Update() is called from. - while (!downloads_.empty()) { - Update(); - sleep_ms(10); - } -} - -std::vector Downloader::GetCurrentProgress() { - std::vector progress; - for (size_t i = 0; i < downloads_.size(); i++) { - if (!downloads_[i]->IsHidden()) - progress.push_back(downloads_[i]->Progress()); - } - return progress; -} - -void Downloader::CancelAll() { - for (size_t i = 0; i < downloads_.size(); i++) { - downloads_[i]->Cancel(); - } - for (size_t i = 0; i < downloads_.size(); i++) { - downloads_[i]->Join(); - } - downloads_.clear(); -} - } // http diff --git a/Common/Net/HTTPClient.h b/Common/Net/HTTPClient.h index 245eec0ee8..dd104e2fa6 100644 --- a/Common/Net/HTTPClient.h +++ b/Common/Net/HTTPClient.h @@ -8,6 +8,7 @@ #include "Common/File/Path.h" #include "Common/Net/NetBuffer.h" #include "Common/Net/Resolve.h" +#include "Common/Net/HTTPRequest.h" namespace net { @@ -44,16 +45,8 @@ namespace http { bool GetHeaderValue(const std::vector &responseHeaders, const std::string &header, std::string *value); -struct RequestProgress { - RequestProgress() {} - explicit RequestProgress(bool *c) : cancelled(c) {} - - float progress = 0.0f; - float kBps = 0.0f; - bool *cancelled = nullptr; -}; - -struct RequestParams { +class RequestParams { +public: RequestParams() {} explicit RequestParams(const char *r) : resource(r) {} RequestParams(const std::string &r, const char *a) : resource(r), acceptMime(a) {} @@ -68,20 +61,20 @@ public: ~Client(); // Return value is the HTTP return code. 200 means OK. < 0 means some local error. - int GET(const RequestParams &req, Buffer *output, RequestProgress *progress); - int GET(const RequestParams &req, Buffer *output, std::vector &responseHeaders, RequestProgress *progress); + int GET(const RequestParams &req, Buffer *output, net::RequestProgress *progress); + int GET(const RequestParams &req, Buffer *output, std::vector &responseHeaders, net::RequestProgress *progress); // Return value is the HTTP return code. - int POST(const RequestParams &req, const std::string &data, const std::string &mime, Buffer *output, RequestProgress *progress); - int POST(const RequestParams &req, const std::string &data, Buffer *output, RequestProgress *progress); + int POST(const RequestParams &req, const std::string &data, const std::string &mime, Buffer *output, net::RequestProgress *progress); + int POST(const RequestParams &req, const std::string &data, Buffer *output, net::RequestProgress *progress); // HEAD, PUT, DELETE aren't implemented yet, but can be done with SendRequest. - int SendRequest(const char *method, const RequestParams &req, const char *otherHeaders, RequestProgress *progress); - int SendRequestWithData(const char *method, const RequestParams &req, const std::string &data, const char *otherHeaders, RequestProgress *progress); - int ReadResponseHeaders(net::Buffer *readbuf, std::vector &responseHeaders, RequestProgress *progress); + int SendRequest(const char *method, const RequestParams &req, const char *otherHeaders, net::RequestProgress *progress); + int SendRequestWithData(const char *method, const RequestParams &req, const std::string &data, const char *otherHeaders, net::RequestProgress *progress); + int ReadResponseHeaders(net::Buffer *readbuf, std::vector &responseHeaders, net::RequestProgress *progress); // If your response contains a response, you must read it. - int ReadResponseEntity(net::Buffer *readbuf, const std::vector &responseHeaders, Buffer *output, RequestProgress *progress); + int ReadResponseEntity(net::Buffer *readbuf, const std::vector &responseHeaders, Buffer *output, net::RequestProgress *progress); void SetDataTimeout(double t) { dataTimeout_ = t; @@ -97,139 +90,52 @@ protected: double dataTimeout_ = 900.0; }; -enum class RequestMethod { - GET, - POST, -}; - // Really an asynchronous request. -class Download { +class HTTPRequest : public Request { public: - Download(RequestMethod method, const std::string &url, const std::string &postData, const std::string &postMime, const Path &outfile); - ~Download(); + HTTPRequest(RequestMethod method, const std::string &url, const std::string &postData, const std::string &postMime, const Path &outfile, ProgressBarMode progressBarMode = ProgressBarMode::DELAYED, const std::string &name = ""); + ~HTTPRequest(); - void Start(); + void Start() override; + void Join() override; - void Join(); - - // Returns 1.0 when done. That one value can be compared exactly - or just use Done(). - float Progress() const { return progress_.progress; } - float SpeedKBps() const { return progress_.kBps; } - - bool Done() const { return completed_; } - bool Failed() const { return failed_; } + bool Done() override { return completed_; } + bool Failed() const override { return failed_; } // NOTE! The value of ResultCode is INVALID until Done() returns true. - int ResultCode() const { return resultCode_; } + int ResultCode() const override { return resultCode_; } - std::string url() const { return url_; } - const Path &outfile() const { return outfile_; } - - void SetAccept(const char *mime) { - acceptMime_ = mime; - } + const Path &outfile() const override { return outfile_; } // If not downloading to a file, access this to get the result. - Buffer &buffer() { return buffer_; } - const Buffer &buffer() const { return buffer_; } + Buffer &buffer() override { return buffer_; } + const Buffer &buffer() const override { return buffer_; } - void Cancel() { + void Cancel() override { cancelled_ = true; } - bool IsCancelled() const { + bool IsCancelled() const override { return cancelled_; } - // NOTE: Callbacks are NOT executed until RunCallback is called. This is so that - // the call will end up on the thread that calls g_DownloadManager.Update(). - void SetCallback(std::function callback) { - callback_ = callback; - } - void RunCallback() { - if (callback_) { - callback_(*this); - } - } - - // Just metadata. Convenient for download managers, for example, if set, - // Downloader::GetCurrentProgress won't return it in the results. - bool IsHidden() const { return hidden_; } - void SetHidden(bool hidden) { hidden_ = hidden; } - void SetUserAgent(const std::string &userAgent) { - userAgent_ = userAgent; - } - private: void Do(); // Actually does the download. Runs on thread. int Perform(const std::string &url); std::string RedirectLocation(const std::string &baseUrl); void SetFailed(int code); - RequestProgress progress_; - RequestMethod method_; std::string postData_; - std::string userAgent_; Buffer buffer_; std::vector responseHeaders_; - std::string url_; Path outfile_; std::thread thread_; - const char *acceptMime_ = "*/*"; std::string postMime_; int resultCode_ = 0; bool completed_ = false; bool failed_ = false; bool cancelled_ = false; - bool hidden_ = false; bool joined_ = false; - std::function callback_; -}; - -using std::shared_ptr; - -class Downloader { -public: - ~Downloader() { - CancelAll(); - } - - std::shared_ptr StartDownload(const std::string &url, const Path &outfile, const char *acceptMime = nullptr); - - std::shared_ptr StartDownloadWithCallback( - const std::string &url, - const Path &outfile, - std::function callback, - const char *acceptMime = nullptr); - - std::shared_ptr AsyncPostWithCallback( - const std::string &url, - const std::string &postData, - const std::string &postMime, // Use postMime = "application/x-www-form-urlencoded" for standard form-style posts, such as used by retroachievements. For encoding form data manually we have MultipartFormDataEncoder. - std::function callback); - - // Drops finished downloads from the list. - void Update(); - void CancelAll(); - - void WaitForAll(); - void SetUserAgent(const std::string &userAgent) { - userAgent_ = userAgent; - } - - std::vector GetCurrentProgress(); - - size_t GetActiveCount() const { - return downloads_.size(); - } - -private: - std::vector> downloads_; - // These get copied to downloads_ in Update(). It's so that callbacks can add new downloads - // while running. - std::vector> newDownloads_; - - std::string userAgent_; }; } // http diff --git a/Common/Net/HTTPNaettRequest.cpp b/Common/Net/HTTPNaettRequest.cpp new file mode 100644 index 0000000000..9660ba6a3f --- /dev/null +++ b/Common/Net/HTTPNaettRequest.cpp @@ -0,0 +1,127 @@ +#ifndef HTTPS_NOT_AVAILABLE + +#include + +#include "Common/Net/HTTPRequest.h" +#include "Common/Net/HTTPNaettRequest.h" +#include "Common/Thread/ThreadUtil.h" +#include "Common/StringUtils.h" +#include "Common/Log.h" + +#include "ext/naett/naett.h" + +namespace http { + +HTTPSRequest::HTTPSRequest(RequestMethod method, const std::string &url, const std::string &postData, const std::string &postMime, const Path &outfile, ProgressBarMode progressBarMode, const std::string &name) + : Request(method, url, name, &cancelled_, progressBarMode), method_(method), postData_(postData), postMime_(postMime), outfile_(outfile) { +} + +HTTPSRequest::~HTTPSRequest() { + Join(); +} + +void HTTPSRequest::Start() { + _dbg_assert_(!req_); + _dbg_assert_(!res_); + + std::vector options; + options.push_back(naettMethod(method_ == RequestMethod::GET ? "GET" : "POST")); + options.push_back(naettHeader("Accept", acceptMime_)); + options.push_back(naettUserAgent(userAgent_.c_str())); + if (!postMime_.empty()) { + options.push_back(naettHeader("Content-Type", postMime_.c_str())); + } + if (method_ == RequestMethod::POST) { + if (!postData_.empty()) { + // Note: Naett does not take ownership over the body. + options.push_back(naettBody(postData_.data(), (int)postData_.size())); + } + } else { + _dbg_assert_(postData_.empty()); + } + // 30 s timeout - not sure what's reasonable? + options.push_back(naettTimeout(30 * 1000)); // milliseconds + + const naettOption **opts = (const naettOption **)options.data(); + req_ = naettRequestWithOptions(url_.c_str(), (int)options.size(), opts); + res_ = naettMake(req_); + + progress_.Update(0, 0, false); +} + +void HTTPSRequest::Join() { + if (!res_ || !req_) + return; // No pending operation. + // Tear down. + if (completed_ && res_) { + _dbg_assert_(req_); + naettClose(res_); + naettFree(req_); + res_ = nullptr; + req_ = nullptr; + } else { + ERROR_LOG(IO, "HTTPSDownload::Join not implemented"); + } +} + +bool HTTPSRequest::Done() { + if (completed_) + return true; + + if (!naettComplete(res_)) { + int total = 0; + int size = naettGetTotalBytesRead(res_, &total); + progress_.Update(size, total, false); + return false; + } + + // -1000 is a code specified by us to represent cancellation, that is unlikely to ever collide with naett error codes. + resultCode_ = IsCancelled() ? -1000 : naettGetStatus(res_); + if (resultCode_ < 0) { + // It's a naett error. Translate and handle. + switch (resultCode_) { + case naettConnectionError: // -1 + ERROR_LOG(IO, "Connection error"); + break; + case naettProtocolError: // -2 + ERROR_LOG(IO, "Protocol error"); + break; + case naettReadError: // -3 + ERROR_LOG(IO, "Read error"); + break; + case naettWriteError: // -4 + ERROR_LOG(IO, "Write error"); + break; + case naettGenericError: // -5 + ERROR_LOG(IO, "Generic error"); + break; + default: + ERROR_LOG(IO, "Unhandled naett error %d", resultCode_); + break; + } + failed_ = true; + progress_.Update(0, 0, true); + } else if (resultCode_ == 200) { + int bodyLength; + const void *body = naettGetBody(res_, &bodyLength); + char *dest = buffer_.Append(bodyLength); + memcpy(dest, body, bodyLength); + if (!outfile_.empty() && !buffer_.FlushToFile(outfile_)) { + ERROR_LOG(IO, "Failed writing download to '%s'", outfile_.c_str()); + } + progress_.Update(bodyLength, bodyLength, true); + } else { + WARN_LOG(IO, "Naett request failed: %d", resultCode_); + failed_ = true; + progress_.Update(0, 0, true); + } + + completed_ = true; + + // The callback will be called later. + return true; +} + +} // namespace http + +#endif // HTTPS_NOT_AVAILABLE diff --git a/Common/Net/HTTPNaettRequest.h b/Common/Net/HTTPNaettRequest.h new file mode 100644 index 0000000000..e5ec147466 --- /dev/null +++ b/Common/Net/HTTPNaettRequest.h @@ -0,0 +1,63 @@ +#pragma once + +#include + +#include "Common/Net/HTTPRequest.h" + +#ifndef HTTPS_NOT_AVAILABLE + +#include "ext/naett/naett.h" + +namespace http { + +// Really an asynchronous request. +class HTTPSRequest : public Request { +public: + HTTPSRequest(RequestMethod method, const std::string &url, const std::string &postData, const std::string &postMime, const Path &outfile, ProgressBarMode progressBarMode = ProgressBarMode::DELAYED, const std::string &name = ""); + ~HTTPSRequest(); + + void Start() override; + void Join() override; + + // Also acts as a Poll. + bool Done() override; + bool Failed() const override { return failed_; } + + // NOTE! The value of ResultCode is INVALID until Done() returns true. + int ResultCode() const override { return resultCode_; } + + const Path &outfile() const override { return outfile_; } + + // If not downloading to a file, access this to get the result. + Buffer &buffer() override { return buffer_; } + const Buffer &buffer() const override { return buffer_; } + + void Cancel() override { + cancelled_ = true; + } + + bool IsCancelled() const override { + return cancelled_; + } + +private: + RequestMethod method_; + std::string postData_; + Buffer buffer_; + std::vector responseHeaders_; + Path outfile_; + std::string postMime_; + int resultCode_ = 0; + bool completed_ = false; + bool failed_ = false; + bool cancelled_ = false; + bool joined_ = false; + + // Naett state + naettReq *req_ = nullptr; + naettRes *res_ = nullptr; +}; + +} // namespace http + +#endif // HTTPS_NOT_AVAILABLE diff --git a/Common/Net/HTTPRequest.cpp b/Common/Net/HTTPRequest.cpp new file mode 100644 index 0000000000..50ea7eaa37 --- /dev/null +++ b/Common/Net/HTTPRequest.cpp @@ -0,0 +1,147 @@ +#include "Common/Net/HTTPRequest.h" +#include "Common/Net/HTTPClient.h" +#include "Common/Net/HTTPNaettRequest.h" +#include "Common/TimeUtil.h" +#include "Common/StringUtils.h" +#include "Common/Log.h" +#include "Common/System/OSD.h" + +namespace http { + +Request::Request(RequestMethod method, const std::string &url, const std::string &name, bool *cancelled, ProgressBarMode mode) : method_(method), url_(url), name_(name), progress_(cancelled), progressBarMode_(mode) { + progress_.callback = [=](int64_t bytes, int64_t contentLength, bool done) { + std::string message; + if (!name_.empty()) { + message = name_; + } else { + std::size_t pos = url_.rfind('/'); + if (pos != std::string::npos) { + message = url_.substr(pos + 1); + } else { + message = url_; + } + } + if (progressBarMode_ != ProgressBarMode::NONE) { + if (!done) { + g_OSD.SetProgressBar(url_, std::move(message), 0.0f, (float)contentLength, (float)bytes, progressBarMode_ == ProgressBarMode::DELAYED ? 3.0f : 0.0f); // delay 3 seconds before showing. + } else { + g_OSD.RemoveProgressBar(url_, Failed() ? false : true, 0.5f); + } + } + }; +} + +bool RequestManager::IsHttpsUrl(const std::string &url) { + return startsWith(url, "https:"); +} + +std::shared_ptr RequestManager::StartDownload(const std::string &url, const Path &outfile, ProgressBarMode mode, const char *acceptMime) { + std::shared_ptr dl; + if (IsHttpsUrl(url)) { +#ifndef HTTPS_NOT_AVAILABLE + dl.reset(new HTTPSRequest(RequestMethod::GET, url, "", "", outfile, mode)); +#else + return std::shared_ptr(); +#endif + } else { + dl.reset(new HTTPRequest(RequestMethod::GET, url, "", "", outfile, mode)); + } + + if (!userAgent_.empty()) + dl->SetUserAgent(userAgent_); + if (acceptMime) + dl->SetAccept(acceptMime); + newDownloads_.push_back(dl); + dl->Start(); + return dl; +} + +std::shared_ptr RequestManager::StartDownloadWithCallback( + const std::string &url, + const Path &outfile, + ProgressBarMode mode, + std::function callback, + const std::string &name, + const char *acceptMime) { + std::shared_ptr dl; + if (IsHttpsUrl(url)) { +#ifndef HTTPS_NOT_AVAILABLE + dl.reset(new HTTPSRequest(RequestMethod::GET, url, "", "", outfile, mode, name)); +#else + return std::shared_ptr(); +#endif + } else { + dl.reset(new HTTPRequest(RequestMethod::GET, url, "", "", outfile, mode, name)); + } + if (!userAgent_.empty()) + dl->SetUserAgent(userAgent_); + if (acceptMime) + dl->SetAccept(acceptMime); + dl->SetCallback(callback); + newDownloads_.push_back(dl); + dl->Start(); + return dl; +} + +std::shared_ptr RequestManager::AsyncPostWithCallback( + const std::string &url, + const std::string &postData, + const std::string &postMime, + ProgressBarMode mode, + std::function callback, + const std::string &name) { + std::shared_ptr dl; + if (IsHttpsUrl(url)) { +#ifndef HTTPS_NOT_AVAILABLE + dl.reset(new HTTPSRequest(RequestMethod::POST, url, postData, postMime, Path(), mode, name)); +#else + return std::shared_ptr(); +#endif + } else { + dl.reset(new HTTPRequest(RequestMethod::POST, url, postData, postMime, Path(), mode, name)); + } + if (!userAgent_.empty()) + dl->SetUserAgent(userAgent_); + dl->SetCallback(callback); + newDownloads_.push_back(dl); + dl->Start(); + return dl; +} + +void RequestManager::Update() { + for (auto iter : newDownloads_) { + downloads_.push_back(iter); + } + newDownloads_.clear(); + +restart: + for (size_t i = 0; i < downloads_.size(); i++) { + auto dl = downloads_[i]; + if (dl->Done()) { + dl->RunCallback(); + dl->Join(); + downloads_.erase(downloads_.begin() + i); + goto restart; + } + } +} + +void RequestManager::WaitForAll() { + // TODO: Should lock? Though, OK if called from main thread, where Update() is called from. + while (!downloads_.empty()) { + Update(); + sleep_ms(10); + } +} + +void RequestManager::CancelAll() { + for (size_t i = 0; i < downloads_.size(); i++) { + downloads_[i]->Cancel(); + } + for (size_t i = 0; i < downloads_.size(); i++) { + downloads_[i]->Join(); + } + downloads_.clear(); +} + +} // namespace diff --git a/Common/Net/HTTPRequest.h b/Common/Net/HTTPRequest.h new file mode 100644 index 0000000000..b04b04d87c --- /dev/null +++ b/Common/Net/HTTPRequest.h @@ -0,0 +1,127 @@ +#pragma once + +#include +#include +#include + +#include "Common/File/Path.h" +#include "Common/Net/NetBuffer.h" + +namespace http { + +enum class RequestMethod { + GET, + POST, +}; + +enum class ProgressBarMode { + NONE, + VISIBLE, + DELAYED, +}; + +// Abstract request. +class Request { +public: + Request(RequestMethod method, const std::string &url, const std::string &name, bool *cancelled, ProgressBarMode mode); + virtual ~Request() {} + + void SetAccept(const char *mime) { + acceptMime_ = mime; + } + + void SetUserAgent(const std::string &userAgent) { + userAgent_ = userAgent; + } + + // NOTE: Completion callbacks (which these are) are deferred until RunCallback is called. This is so that + // the call will end up on the thread that calls g_DownloadManager.Update(). + void SetCallback(std::function callback) { + callback_ = callback; + } + void RunCallback() { + if (callback_) { + callback_(*this); + } + } + + virtual void Start() = 0; + virtual void Join() = 0; + + virtual bool Done() = 0; + virtual bool Failed() const = 0; + + virtual int ResultCode() const = 0; + + // Returns 1.0 when done. That one value can be compared exactly - or just use Done(). + float Progress() const { return progress_.progress; } + float SpeedKBps() const { return progress_.kBps; } + std::string url() const { return url_; } + virtual const Path &outfile() const = 0; + + virtual void Cancel() = 0; + virtual bool IsCancelled() const = 0; + + // Response + virtual Buffer &buffer() = 0; + virtual const Buffer &buffer() const = 0; + +protected: + std::function callback_; + RequestMethod method_; + std::string url_; + std::string name_; + const char *acceptMime_ = "*/*"; + std::string userAgent_; + + net::RequestProgress progress_; + ProgressBarMode progressBarMode_; +}; + +using std::shared_ptr; + +class RequestManager { +public: + ~RequestManager() { + CancelAll(); + } + + std::shared_ptr StartDownload(const std::string &url, const Path &outfile, ProgressBarMode mode, const char *acceptMime = nullptr); + + std::shared_ptr StartDownloadWithCallback( + const std::string &url, + const Path &outfile, + ProgressBarMode mode, + std::function callback, + const std::string &name = "", + const char *acceptMime = nullptr); + + std::shared_ptr AsyncPostWithCallback( + const std::string &url, + const std::string &postData, + const std::string &postMime, // Use postMime = "application/x-www-form-urlencoded" for standard form-style posts, such as used by retroachievements. For encoding form data manually we have MultipartFormDataEncoder. + ProgressBarMode mode, + std::function callback, + const std::string &name = ""); + + // Drops finished downloads from the list. + void Update(); + void CancelAll(); + + void WaitForAll(); + void SetUserAgent(const std::string &userAgent) { + userAgent_ = userAgent; + } + +private: + bool IsHttpsUrl(const std::string &url); + + std::vector> downloads_; + // These get copied to downloads_ in Update(). It's so that callbacks can add new downloads + // while running. + std::vector> newDownloads_; + + std::string userAgent_; +}; + +} // namespace net diff --git a/Common/Net/HTTPServer.cpp b/Common/Net/HTTPServer.cpp index e1fa53bc6e..3822083b79 100644 --- a/Common/Net/HTTPServer.cpp +++ b/Common/Net/HTTPServer.cpp @@ -60,7 +60,7 @@ namespace http { // Note: charset here helps prevent XSS. const char *const DEFAULT_MIME_TYPE = "text/html; charset=utf-8"; -Request::Request(int fd) +ServerRequest::ServerRequest(int fd) : fd_(fd) { in_ = new net::InputSink(fd); out_ = new net::OutputSink(fd); @@ -73,7 +73,7 @@ Request::Request(int fd) } } -Request::~Request() { +ServerRequest::~ServerRequest() { Close(); if (!in_->Empty()) { @@ -86,7 +86,7 @@ Request::~Request() { delete out_; } -void Request::WriteHttpResponseHeader(const char *ver, int status, int64_t size, const char *mimeType, const char *otherHeaders) const { +void ServerRequest::WriteHttpResponseHeader(const char *ver, int status, int64_t size, const char *mimeType, const char *otherHeaders) const { const char *statusStr; switch (status) { case 200: statusStr = "OK"; break; @@ -123,18 +123,18 @@ void Request::WriteHttpResponseHeader(const char *ver, int status, int64_t size, buffer->Push("\r\n"); } -void Request::WritePartial() const { +void ServerRequest::WritePartial() const { _assert_(fd_); out_->Flush(); } -void Request::Write() { +void ServerRequest::Write() { _assert_(fd_); WritePartial(); Close(); } -void Request::Close() { +void ServerRequest::Close() { if (fd_) { closesocket(fd_); fd_ = 0; @@ -317,7 +317,7 @@ void Server::Stop() { } void Server::HandleConnection(int conn_fd) { - Request request(conn_fd); + ServerRequest request(conn_fd); if (!request.IsOK()) { WARN_LOG(IO, "Bad request, ignoring."); return; @@ -331,11 +331,11 @@ void Server::HandleConnection(int conn_fd) { request.Write(); } -void Server::HandleRequest(const Request &request) { +void Server::HandleRequest(const ServerRequest &request) { HandleRequestDefault(request); } -void Server::HandleRequestDefault(const Request &request) { +void Server::HandleRequestDefault(const ServerRequest &request) { if (request.resource() == nullptr) { fallback_(request); return; @@ -350,14 +350,14 @@ void Server::HandleRequestDefault(const Request &request) { } } -void Server::Handle404(const Request &request) { +void Server::Handle404(const ServerRequest &request) { INFO_LOG(IO, "No handler for '%s', falling back to 404.", request.resource()); const char *payload = "404 not found\r\n"; request.WriteHttpResponseHeader("1.0", 404, (int)strlen(payload)); request.Out()->Push(payload); } -void Server::HandleListing(const Request &request) { +void Server::HandleListing(const ServerRequest &request) { request.WriteHttpResponseHeader("1.0", 200, -1, "text/plain"); for (auto iter = handlers_.begin(); iter != handlers_.end(); ++iter) { request.Out()->Printf("%s\n", iter->first.c_str()); diff --git a/Common/Net/HTTPServer.h b/Common/Net/HTTPServer.h index 40e336bb5a..c9051226a6 100644 --- a/Common/Net/HTTPServer.h +++ b/Common/Net/HTTPServer.h @@ -25,10 +25,10 @@ class OutputSink; namespace http { -class Request { +class ServerRequest { public: - Request(int fd); - ~Request(); + ServerRequest(int fd); + ~ServerRequest(); const char *resource() const { return header_.resource; @@ -75,7 +75,7 @@ public: Server(NewThreadExecutor *executor); virtual ~Server(); - typedef std::function UrlHandlerFunc; + typedef std::function UrlHandlerFunc; typedef std::map UrlHandlerMap; // Runs forever, serving request. If you want to do something else than serve pages, @@ -94,7 +94,7 @@ public: // If you want to customize things at a lower level than just a simple path handler, // then inherit and override this. Implementations should forward to HandleRequestDefault // if they don't recognize the url. - virtual void HandleRequest(const Request &request); + virtual void HandleRequest(const ServerRequest &request); int Port() { return port_; @@ -107,11 +107,11 @@ private: void HandleConnection(int conn_fd); // Things like default 404, etc. - void HandleRequestDefault(const Request &request); + void HandleRequestDefault(const ServerRequest &request); // Neat built-in handlers that are tied to the server. - void HandleListing(const Request &request); - void Handle404(const Request &request); + void HandleListing(const ServerRequest &request); + void Handle404(const ServerRequest &request); int listener_; int port_ = 0; diff --git a/Common/Net/NetBuffer.cpp b/Common/Net/NetBuffer.cpp index 17a5aae916..09a4853963 100644 --- a/Common/Net/NetBuffer.cpp +++ b/Common/Net/NetBuffer.cpp @@ -22,6 +22,18 @@ namespace net { +void RequestProgress::Update(int64_t downloaded, int64_t totalBytes, bool done) { + if (totalBytes) { + progress = (double)downloaded / (double)totalBytes; + } else { + progress = 0.01f; + } + + if (callback) { + callback(downloaded, totalBytes, done); + } +} + bool Buffer::FlushSocket(uintptr_t sock, double timeout, bool *cancelled) { static constexpr float CANCEL_INTERVAL = 0.25f; for (size_t pos = 0, end = data_.size(); pos < end; ) { @@ -48,7 +60,7 @@ bool Buffer::FlushSocket(uintptr_t sock, double timeout, bool *cancelled) { return true; } -bool Buffer::ReadAllWithProgress(int fd, int knownSize, float *progress, float *kBps, bool *cancelled) { +bool Buffer::ReadAllWithProgress(int fd, int knownSize, RequestProgress *progress) { static constexpr float CANCEL_INTERVAL = 0.25f; std::vector buf; // We're non-blocking and reading from an OS buffer, so try to read as much as we can at a time. @@ -64,11 +76,15 @@ bool Buffer::ReadAllWithProgress(int fd, int knownSize, float *progress, float * int total = 0; while (true) { bool ready = false; - while (!ready && cancelled) { - if (*cancelled) + + // If we might need to cancel, check on a timer for it to be ready. + // After this, we'll block on reading so we do this while first if we have a cancel pointer. + while (!ready && progress && progress->cancelled) { + if (*progress->cancelled) return false; ready = fd_util::WaitUntilReady(fd, CANCEL_INTERVAL, false); } + int retval = recv(fd, &buf[0], (int)buf.size(), MSG_NOSIGNAL); if (retval == 0) { return true; @@ -88,10 +104,10 @@ bool Buffer::ReadAllWithProgress(int fd, int knownSize, float *progress, float * char *p = Append((size_t)retval); memcpy(p, &buf[0], retval); total += retval; - if (progress) - *progress = (float)total / (float)knownSize; - if (kBps) - *kBps = (float)(total / (time_now_d() - st)) / 1024.0f; + if (progress) { + progress->Update(total, knownSize, false); + progress->kBps = (float)(total / (time_now_d() - st)) / 1024.0f; + } } return true; } @@ -114,4 +130,4 @@ int Buffer::Read(int fd, size_t sz) { return (int)received; } -} +} // namespace diff --git a/Common/Net/NetBuffer.h b/Common/Net/NetBuffer.h index 12a4ba37c8..b30851d12d 100644 --- a/Common/Net/NetBuffer.h +++ b/Common/Net/NetBuffer.h @@ -1,16 +1,29 @@ -#pragma once +#pragma once #include +#include #include "Common/Buffer.h" namespace net { +class RequestProgress { +public: + explicit RequestProgress(bool *c) : cancelled(c) {} + + void Update(int64_t downloaded, int64_t totalBytes, bool done); + + float progress = 0.0f; + float kBps = 0.0f; + bool *cancelled = nullptr; + std::function callback; +}; + class Buffer : public ::Buffer { public: bool FlushSocket(uintptr_t sock, double timeout, bool *cancelled = nullptr); - bool ReadAllWithProgress(int fd, int knownSize, float *progress, float *kBps, bool *cancelled); + bool ReadAllWithProgress(int fd, int knownSize, RequestProgress *progress); // < 0: error // >= 0: number of bytes read diff --git a/Common/Net/Resolve.cpp b/Common/Net/Resolve.cpp index 76f0055f50..2438fc5b33 100644 --- a/Common/Net/Resolve.cpp +++ b/Common/Net/Resolve.cpp @@ -30,8 +30,18 @@ #include "Common/Log.h" #include "Common/TimeUtil.h" +#ifndef HTTPS_NOT_AVAILABLE +#include "ext/naett/naett.h" +#endif + +#if PPSSPP_PLATFORM(ANDROID) +#include +extern JavaVM *gJvm; +#endif + namespace net { +static bool g_naettInitialized; void Init() { @@ -40,6 +50,17 @@ void Init() WSADATA wsaData = {0}; WSAStartup(MAKEWORD(2, 2), &wsaData); #endif + if (!g_naettInitialized) { +#ifndef HTTPS_NOT_AVAILABLE +#if PPSSPP_PLATFORM(ANDROID) + _assert_(gJvm != nullptr); + naettInit(gJvm); +#else + naettInit(NULL); +#endif +#endif + g_naettInitialized = true; + } } void Shutdown() diff --git a/Common/Net/WebsocketServer.cpp b/Common/Net/WebsocketServer.cpp index 3b6f98ed27..e55c0644f7 100644 --- a/Common/Net/WebsocketServer.cpp +++ b/Common/Net/WebsocketServer.cpp @@ -69,7 +69,7 @@ static bool ListContainsNoCase(const std::string &list, const std::string value) return false; } -WebSocketServer *WebSocketServer::CreateAsUpgrade(const http::Request &request, const std::string &protocol) { +WebSocketServer *WebSocketServer::CreateAsUpgrade(const http::ServerRequest &request, const std::string &protocol) { auto requireHeader = [&](const char *name, const char *expected) { std::string val; if (!request.GetHeader(name, &val)) { diff --git a/Common/Net/WebsocketServer.h b/Common/Net/WebsocketServer.h index 7a05b0f03a..591e800e94 100644 --- a/Common/Net/WebsocketServer.h +++ b/Common/Net/WebsocketServer.h @@ -29,7 +29,7 @@ enum class WebSocketClose : uint16_t { // RFC 6455 class WebSocketServer { public: - static WebSocketServer *CreateAsUpgrade(const http::Request &request, const std::string &protocol = ""); + static WebSocketServer *CreateAsUpgrade(const http::ServerRequest &request, const std::string &protocol = ""); void Send(const std::string &str); void Send(const std::vector &payload); diff --git a/Common/Render/Text/draw_text.cpp b/Common/Render/Text/draw_text.cpp index 4d4b58e7f9..cb09dbda98 100644 --- a/Common/Render/Text/draw_text.cpp +++ b/Common/Render/Text/draw_text.cpp @@ -11,6 +11,7 @@ #include "Common/Render/Text/draw_text_uwp.h" #include "Common/Render/Text/draw_text_qt.h" #include "Common/Render/Text/draw_text_android.h" +#include "Common/Render/Text/draw_text_sdl.h" TextDrawer::TextDrawer(Draw::DrawContext *draw) : draw_(draw) { // These probably shouldn't be state. @@ -92,6 +93,8 @@ TextDrawer *TextDrawer::Create(Draw::DrawContext *draw) { drawer = new TextDrawerQt(draw); #elif PPSSPP_PLATFORM(ANDROID) drawer = new TextDrawerAndroid(draw); +#elif USE_SDL2_TTF + drawer = new TextDrawerSDL(draw); #endif if (drawer && !drawer->IsReady()) { delete drawer; diff --git a/Common/Render/Text/draw_text_android.cpp b/Common/Render/Text/draw_text_android.cpp index cab0bdca7e..d1e6244cb2 100644 --- a/Common/Render/Text/draw_text_android.cpp +++ b/Common/Render/Text/draw_text_android.cpp @@ -1,4 +1,5 @@ #include "ppsspp_config.h" + #include "Common/Log.h" #include "Common/StringUtils.h" #include "Common/System/Display.h" diff --git a/Common/Render/Text/draw_text_sdl.cpp b/Common/Render/Text/draw_text_sdl.cpp new file mode 100644 index 0000000000..701ecc64c8 --- /dev/null +++ b/Common/Render/Text/draw_text_sdl.cpp @@ -0,0 +1,470 @@ +#include "ppsspp_config.h" + +#include "Common/Log.h" +#include "Common/StringUtils.h" +#include "Common/System/Display.h" +#include "Common/GPU/thin3d.h" +#include "Common/Data/Hash/Hash.h" +#include "Common/Data/Text/WrapText.h" +#include "Common/Data/Encoding/Utf8.h" +#include "Common/File/VFS/VFS.h" +#include "Common/File/FileUtil.h" +#include "Common/File/Path.h" +#include "Common/Render/Text/draw_text.h" +#include "Common/Render/Text/draw_text_sdl.h" + +#if defined(USE_SDL2_TTF) + +#include "SDL2/SDL.h" +#include "SDL2/SDL_ttf.h" + +TextDrawerSDL::TextDrawerSDL(Draw::DrawContext *draw): TextDrawer(draw) { + if (TTF_Init() < 0) { + ERROR_LOG(G3D, "Unable to initialize SDL2_ttf"); + } + + dpiScale_ = CalculateDPIScale(); + +#if defined(USE_SDL2_TTF_FONTCONFIG) + config = FcInitLoadConfigAndFonts(); +#endif + + PrepareFallbackFonts(); +} + +TextDrawerSDL::~TextDrawerSDL() { + ClearCache(); + TTF_Quit(); + +#if defined(USE_SDL2_TTF_FONTCONFIG) + FcConfigDestroy(config); + FcFini(); +#endif +} + +// If a user complains about missing characters on SDL, re-visit this! +void TextDrawerSDL::PrepareFallbackFonts() { +#if defined(USE_SDL2_TTF_FONTCONFIG) + FcObjectSet *os = FcObjectSetBuild (FC_FILE, FC_INDEX, (char *) 0); + + FcPattern *names[] = { + FcNameParse((const FcChar8 *) "Source Han Sans Medium"), + FcNameParse((const FcChar8 *) "Droid Sans Bold"), + FcNameParse((const FcChar8 *) "DejaVu Sans Condensed"), + FcNameParse((const FcChar8 *) "Noto Sans CJK Medium"), + FcNameParse((const FcChar8 *) "Noto Sans Hebrew Medium"), + FcNameParse((const FcChar8 *) "Noto Sans Lao Medium"), + FcNameParse((const FcChar8 *) "Noto Sans Thai Medium") + }; + + for (int i = 0; i < ARRAY_SIZE(names); i++) { + FcFontSet *foundFonts = FcFontList(config, names[i], os); + + for (int j = 0; foundFonts && j < foundFonts->nfont; ++j) { + FcPattern* font = foundFonts->fonts[j]; + FcChar8 *path; + int fontIndex; + + if (FcPatternGetInteger(font, FC_INDEX, 0, &fontIndex) != FcResultMatch) { + fontIndex = 0; // The 0th face is guaranteed to exist + } + + if (FcPatternGetString(font, FC_FILE, 0, &path) == FcResultMatch) { + std::string path_str((const char*)path); + fallbackFontPaths_.push_back(std::make_pair(path_str, fontIndex)); + } + } + + if (foundFonts) { + FcFontSetDestroy(foundFonts); + } + + FcPatternDestroy(names[i]); + } + + if (os) { + FcObjectSetDestroy(os); + } +#elif PPSSPP_PLATFORM(MAC) + const char *fontDirs[] = { + "/System/Library/Fonts/", + "/System/Library/Fonts/Supplemental/", + "/Library/Fonts/" + }; + + const char *fallbackFonts[] = { + "Hiragino Sans GB.ttc", + "PingFang.ttc", + "PingFang SC.ttc", + "PingFang TC.ttc", + "ヒラギノ角ゴシック W4.ttc", + "AppleGothic.ttf", + "Arial Unicode.ttf", + }; + + for (int i = 0; i < ARRAY_SIZE(fontDirs); i++) { + for (int j = 0; j < ARRAY_SIZE(fallbackFonts); j++) { + Path fontPath = Path(fontDirs[i]) / fallbackFonts[j]; + + if (File::Exists(fontPath)) { + TTF_Font *openedFont = TTF_OpenFont(fontPath.ToString().c_str(), 24); + int64_t numFaces = TTF_FontFaces(openedFont); + + for (int k = 0; k < numFaces; k++) { + TTF_Font *fontFace = TTF_OpenFontIndex(fontPath.ToString().c_str(), 24, k); + std::string fontFaceName(TTF_FontFaceStyleName(fontFace)); + TTF_CloseFont(fontFace); + + if (strstr(fontFaceName.c_str(), "Medium") || + strstr(fontFaceName.c_str(), "Regular")) + { + fallbackFontPaths_.push_back(std::make_pair(fontPath.ToString(), k)); + break; + } + } + + TTF_CloseFont(openedFont); + } + } + } +#else + // We don't have a fallback font for this platform. + // Unsupported characters will be rendered as squares. +#endif +} + +uint32_t TextDrawerSDL::CheckMissingGlyph(const std::string& text) { + TTF_Font *font = fontMap_.find(fontHash_)->second; + UTF8 utf8Decoded(text.c_str()); + + uint32_t missingGlyph = 0; + for (int i = 0; i < text.length(); ) { + uint32_t glyph = utf8Decoded.next(); + i = utf8Decoded.byteIndex(); + + if (!TTF_GlyphIsProvided32(font, glyph)) { + missingGlyph = glyph; + break; + } + } + + return missingGlyph; +} + +// If this returns true, the first font in fallbackFonts_ can be used as a fallback. +bool TextDrawerSDL::FindFallbackFonts(uint32_t missingGlyph, int ptSize) { + // If we encounter a missing glyph, try to use one of the fallback fonts. + for (int i = 0; i < fallbackFonts_.size(); i++) { + TTF_Font *fallbackFont = fallbackFonts_[i]; + if (TTF_GlyphIsProvided32(fallbackFont, missingGlyph)) { + fallbackFonts_.erase(fallbackFonts_.begin() + i); + fallbackFonts_.insert(fallbackFonts_.begin(), fallbackFont); + return true; + } + } + + // If none of the loaded fonts can handle it, load more fonts. + for (int i = 0; i < fallbackFontPaths_.size(); i++) { + std::string& fontPath = fallbackFontPaths_[i].first; + int faceIndex = fallbackFontPaths_[i].second; + + TTF_Font *font = TTF_OpenFontIndex(fontPath.c_str(), ptSize, faceIndex); + + if (TTF_GlyphIsProvided32(font, missingGlyph)) { + fallbackFonts_.insert(fallbackFonts_.begin(), font); + fallbackFontPaths_.erase(fallbackFontPaths_.begin() + i); + return true; + } else { + TTF_CloseFont(font); + } + } + + return false; +} + +uint32_t TextDrawerSDL::SetFont(const char *fontName, int size, int flags) { + uint32_t fontHash = fontName && strlen(fontName) ? hash::Adler32((const uint8_t *)fontName, strlen(fontName)) : 0; + fontHash ^= size; + fontHash ^= flags << 10; + + auto iter = fontMap_.find(fontHash); + if (iter != fontMap_.end()) { + fontHash_ = fontHash; + return fontHash; + } + + const char *useFont = fontName ? fontName : "Roboto-Condensed.ttf"; + const int ptSize = (int)((size + 6) / dpiScale_); + + TTF_Font *font = TTF_OpenFont(useFont, ptSize); + + if (!font) { + File::FileInfo fileInfo; + g_VFS.GetFileInfo("Roboto-Condensed.ttf", &fileInfo); + font = TTF_OpenFont(fileInfo.fullName.c_str(), ptSize); + } + + fontMap_[fontHash] = font; + fontHash_ = fontHash; + return fontHash; +} + +void TextDrawerSDL::SetFont(uint32_t fontHandle) { + uint32_t fontHash = fontHandle; + auto iter = fontMap_.find(fontHash); + if (iter != fontMap_.end()) { + fontHash_ = fontHandle; + } else { + ERROR_LOG(G3D, "Invalid font handle %08x", fontHandle); + } +} + +void TextDrawerSDL::MeasureString(const char *str, size_t len, float *w, float *h) { + CacheKey key{ std::string(str, len), fontHash_ }; + + TextMeasureEntry *entry; + auto iter = sizeCache_.find(key); + if (iter != sizeCache_.end()) { + entry = iter->second.get(); + } else { + TTF_Font *font = fontMap_.find(fontHash_)->second; + int ptSize = TTF_FontHeight(font) / 1.35; + + uint32_t missingGlyph = CheckMissingGlyph(key.text); + + if (missingGlyph && FindFallbackFonts(missingGlyph, ptSize)) { + font = fallbackFonts_[0]; + } + + int width = 0; + int height = 0; + TTF_SizeUTF8(font, key.text.c_str(), &width, &height); + + entry = new TextMeasureEntry(); + entry->width = width; + entry->height = height; + sizeCache_[key] = std::unique_ptr(entry); + } + + entry->lastUsedFrame = frameCount_; + *w = entry->width * fontScaleX_ * dpiScale_; + *h = entry->height * fontScaleY_ * dpiScale_; +} + +void TextDrawerSDL::MeasureStringRect(const char *str, size_t len, const Bounds &bounds, float *w, float *h, int align) { + std::string toMeasure = std::string(str, len); + int wrap = align & (FLAG_WRAP_TEXT | FLAG_ELLIPSIZE_TEXT); + if (wrap) { + bool rotated = (align & (ROTATE_90DEG_LEFT | ROTATE_90DEG_RIGHT)) != 0; + WrapString(toMeasure, toMeasure.c_str(), rotated ? bounds.h : bounds.w, wrap); + } + + TTF_Font *font = fontMap_.find(fontHash_)->second; + int ptSize = TTF_FontHeight(font) / 1.35; + uint32_t missingGlyph = CheckMissingGlyph(toMeasure); + + if (missingGlyph && FindFallbackFonts(missingGlyph, ptSize)) { + font = fallbackFonts_[0]; + } + + std::vector lines; + SplitString(toMeasure, '\n', lines); + + int total_w = 0; + int total_h = 0; + for (size_t i = 0; i < lines.size(); i++) { + CacheKey key{ lines[i], fontHash_ }; + + TextMeasureEntry *entry; + auto iter = sizeCache_.find(key); + if (iter != sizeCache_.end()) { + entry = iter->second.get(); + } else { + int width = 0; + int height = 0; + TTF_SizeUTF8(font, lines[i].c_str(), &width, &height); + entry = new TextMeasureEntry(); + entry->width = width; + entry->height = height; + sizeCache_[key] = std::unique_ptr(entry); + } + + entry->lastUsedFrame = frameCount_; + if (total_w < entry->width) { + total_w = entry->width; + } + total_h += TTF_FontLineSkip(font); + } + + *w = total_w * fontScaleX_ * dpiScale_; + *h = total_h * fontScaleY_ * dpiScale_; +} + +void TextDrawerSDL::DrawString(DrawBuffer &target, const char *str, float x, float y, uint32_t color, int align) { + using namespace Draw; + if (!strlen(str)) + return; + + CacheKey key{ std::string(str), fontHash_ }; + target.Flush(true); + + TextStringEntry *entry; + + auto iter = cache_.find(key); + if (iter != cache_.end()) { + entry = iter->second.get(); + entry->lastUsedFrame = frameCount_; + } else { + DataFormat texFormat = Draw::DataFormat::R4G4B4A4_UNORM_PACK16; + + entry = new TextStringEntry(); + + TextureDesc desc{}; + std::vector bitmapData; + DrawStringBitmap(bitmapData, *entry, texFormat, str, align); + desc.initData.push_back(&bitmapData[0]); + + desc.type = TextureType::LINEAR2D; + desc.format = texFormat; + desc.width = entry->bmWidth; + desc.height = entry->bmHeight; + desc.depth = 1; + desc.mipLevels = 1; + desc.tag = "TextDrawer"; + entry->texture = draw_->CreateTexture(desc); + cache_[key] = std::unique_ptr(entry); + } + + if (entry->texture) { + draw_->BindTexture(0, entry->texture); + } + + float w = entry->bmWidth * fontScaleX_ * dpiScale_; + float h = entry->bmHeight * fontScaleY_ * dpiScale_; + DrawBuffer::DoAlign(align, &x, &y, &w, &h); + if (entry->texture) { + target.DrawTexRect(x, y, x + w, y + h, 0.0f, 0.0f, 1.0f, 1.0f, color); + target.Flush(true); + } +} + +void TextDrawerSDL::DrawStringBitmap(std::vector &bitmapData, TextStringEntry &entry, Draw::DataFormat texFormat, const char *str, int align) { + if (!strlen(str)) { + bitmapData.clear(); + return; + } + + // Replace "&&" with "&" + std::string processedStr = ReplaceAll(str, "&&", "&"); + + // If a string includes only newlines, SDL2_ttf will refuse to render it + // thinking it is empty. Add a space to avoid that. + bool isAllNewline = processedStr.find_first_not_of('\n') == std::string::npos; + + if (isAllNewline) { + processedStr.push_back(' '); + } + + TTF_Font *font = fontMap_.find(fontHash_)->second; + int ptSize = TTF_FontHeight(font) / 1.35; + + uint32_t missingGlyph = CheckMissingGlyph(processedStr); + + if (missingGlyph && FindFallbackFonts(missingGlyph, ptSize)) { + font = fallbackFonts_[0]; + } + + if (align & ALIGN_HCENTER) + TTF_SetFontWrappedAlign(font, TTF_WRAPPED_ALIGN_CENTER); + else if (align & ALIGN_LEFT) + TTF_SetFontWrappedAlign(font, TTF_WRAPPED_ALIGN_LEFT); + else if (align & ALIGN_RIGHT) + TTF_SetFontWrappedAlign(font, TTF_WRAPPED_ALIGN_RIGHT); + + SDL_Color fgColor = { 0xFF, 0xFF, 0xFF, 0xFF }; + SDL_Surface *text = TTF_RenderUTF8_Blended_Wrapped(font, processedStr.c_str(), fgColor, 0); + SDL_LockSurface(text); + + entry.texture = nullptr; + entry.bmWidth = entry.width = text->pitch / sizeof(uint32_t); + entry.bmHeight = entry.height = text->h; + entry.lastUsedFrame = frameCount_; + + uint32_t *imageData = (uint32_t *)text->pixels; + + if (texFormat == Draw::DataFormat::B4G4R4A4_UNORM_PACK16 || texFormat == Draw::DataFormat::R4G4B4A4_UNORM_PACK16) { + bitmapData.resize(entry.bmWidth * entry.bmHeight * sizeof(uint16_t)); + uint16_t *bitmapData16 = (uint16_t *)&bitmapData[0]; + + for (int x = 0; x < entry.bmWidth; x++) { + for (int y = 0; y < entry.bmHeight; y++) { + uint64_t index = entry.bmWidth * y + x; + bitmapData16[index] = 0xfff0 | (imageData[index] >> 28); + } + } + } else if (texFormat == Draw::DataFormat::R8_UNORM) { + bitmapData.resize(entry.bmWidth * entry.bmHeight); + for (int x = 0; x < entry.bmWidth; x++) { + for (int y = 0; y < entry.bmHeight; y++) { + uint64_t index = entry.bmWidth * y + x; + bitmapData[index] = imageData[index] >> 24; + } + } + } else { + _assert_msg_(false, "Bad TextDrawer format"); + } + + SDL_UnlockSurface(text); + SDL_FreeSurface(text); +} + +void TextDrawerSDL::OncePerFrame() { + // Reset everything if DPI changes + float newDpiScale = CalculateDPIScale(); + if (newDpiScale != dpiScale_) { + dpiScale_ = newDpiScale; + ClearCache(); + } + + // Drop old strings. Use a prime number to reduce clashing with other rhythms + if (frameCount_ % 23 == 0) { + for (auto iter = cache_.begin(); iter != cache_.end();) { + if (frameCount_ - iter->second->lastUsedFrame > 100) { + if (iter->second->texture) + iter->second->texture->Release(); + cache_.erase(iter++); + } else { + iter++; + } + } + + for (auto iter = sizeCache_.begin(); iter != sizeCache_.end(); ) { + if (frameCount_ - iter->second->lastUsedFrame > 100) { + sizeCache_.erase(iter++); + } else { + iter++; + } + } + } +} + +void TextDrawerSDL::ClearCache() { + for (auto &iter : cache_) { + if (iter.second->texture) + iter.second->texture->Release(); + } + cache_.clear(); + sizeCache_.clear(); + + for (auto iter : fontMap_) { + TTF_CloseFont(iter.second); + } + for (auto iter : fallbackFonts_) { + TTF_CloseFont(iter); + } + fontMap_.clear(); + fallbackFonts_.clear(); + fontHash_ = 0; +} + +#endif diff --git a/Common/Render/Text/draw_text_sdl.h b/Common/Render/Text/draw_text_sdl.h new file mode 100644 index 0000000000..87c543a660 --- /dev/null +++ b/Common/Render/Text/draw_text_sdl.h @@ -0,0 +1,47 @@ +#pragma once + +#include "ppsspp_config.h" + +#include +#include "Common/Render/Text/draw_text.h" + +#if defined(USE_SDL2_TTF_FONTCONFIG) +#include +#endif + +// SDL2_ttf's TTF_Font is a typedef of _TTF_Font. +struct _TTF_Font; + +class TextDrawerSDL : public TextDrawer { +public: + TextDrawerSDL(Draw::DrawContext *draw); + ~TextDrawerSDL(); + + uint32_t SetFont(const char *fontName, int size, int flags) override; + void SetFont(uint32_t fontHandle) override; // Shortcut once you've set the font once. + void MeasureString(const char *str, size_t len, float *w, float *h) override; + void MeasureStringRect(const char *str, size_t len, const Bounds &bounds, float *w, float *h, int align = ALIGN_TOPLEFT) override; + void DrawString(DrawBuffer &target, const char *str, float x, float y, uint32_t color, int align = ALIGN_TOPLEFT) override; + void DrawStringBitmap(std::vector &bitmapData, TextStringEntry &entry, Draw::DataFormat texFormat, const char *str, int align = ALIGN_TOPLEFT) override; + // Use for housekeeping like throwing out old strings. + void OncePerFrame() override; + +protected: + void ClearCache() override; + void PrepareFallbackFonts(); + uint32_t CheckMissingGlyph(const std::string& text); + bool FindFallbackFonts(uint32_t missingGlyph, int ptSize); + + uint32_t fontHash_; + std::map fontMap_; + + std::map> cache_; + std::map> sizeCache_; + + std::vector<_TTF_Font *> fallbackFonts_; + std::vector> fallbackFontPaths_; // path and font face index + +#if defined(USE_SDL2_TTF_FONTCONFIG) + FcConfig *config; +#endif +}; diff --git a/Common/RiscVCPUDetect.cpp b/Common/RiscVCPUDetect.cpp index 55165116bd..5f18db8cd9 100644 --- a/Common/RiscVCPUDetect.cpp +++ b/Common/RiscVCPUDetect.cpp @@ -194,7 +194,7 @@ void CPUInfo::Detect() truncate_cpy(cpu_string, parser.ISAString().c_str()); - // A number of CPUs support a limited set of B. It's not all U74, so we use SOC for now... + // A number of CPUs support a limited set of bitmanip. It's not all U74, so we use SOC for now... if (parser.FirmwareMatchesCompatible("starfive,jh7110")) { RiscV_Zba = true; RiscV_Zbb = true; @@ -208,7 +208,6 @@ void CPUInfo::Detect() RiscV_D = ExtensionSupported(hwcap, 'D'); RiscV_C = ExtensionSupported(hwcap, 'C'); RiscV_V = ExtensionSupported(hwcap, 'V'); - RiscV_B = ExtensionSupported(hwcap, 'B'); // We assume as in RVA20U64 that F means Zicsr is available. RiscV_Zicsr = RiscV_F; @@ -221,7 +220,8 @@ void CPUInfo::Detect() RiscV_D = info.features.D; RiscV_C = info.features.C; RiscV_V = info.features.V; - RiscV_Zicsr = info.features.Zicsr; + // Seems to be wrong sometimes, assume we have it if we have F. + RiscV_Zicsr = info.features.Zicsr || info.features.F; truncate_cpy(brand_string, info.uarch); #endif @@ -241,11 +241,10 @@ std::vector CPUInfo::Features() { { RiscV_D, "Double" }, { RiscV_C, "Compressed" }, { RiscV_V, "Vector" }, - { RiscV_B, "Bitmanip" }, - { RiscV_Zba, "Zba" }, - { RiscV_Zbb, "Zbb" }, - { RiscV_Zbc, "Zbc" }, - { RiscV_Zbs, "Zbs" }, + { RiscV_Zba, "Bitmanip Zba" }, + { RiscV_Zbb, "Bitmanip Zbb" }, + { RiscV_Zbc, "Bitmanip Zbc" }, + { RiscV_Zbs, "Bitmanip Zbs" }, { RiscV_Zicsr, "Zicsr" }, { CPU64bit, "64-bit" }, }; diff --git a/Common/RiscVEmitter.cpp b/Common/RiscVEmitter.cpp index 1fa966e1fb..5c986bdf81 100644 --- a/Common/RiscVEmitter.cpp +++ b/Common/RiscVEmitter.cpp @@ -62,10 +62,10 @@ static inline bool SupportsVector() { static inline bool SupportsBitmanip(char zbx) { switch (zbx) { - case 'a': return cpu_info.RiscV_B || cpu_info.RiscV_Zba; - case 'b': return cpu_info.RiscV_B || cpu_info.RiscV_Zbb; - case 'c': return cpu_info.RiscV_B || cpu_info.RiscV_Zbc; - case 's': return cpu_info.RiscV_B || cpu_info.RiscV_Zbs; + case 'a': return cpu_info.RiscV_Zba; + case 'b': return cpu_info.RiscV_Zbb; + case 'c': return cpu_info.RiscV_Zbc; + case 's': return cpu_info.RiscV_Zbs; default: return false; } } @@ -1180,6 +1180,19 @@ bool RiscVEmitter::CJInRange(const void *src, const void *dst) const { return BJInRange(src, dst, 12); } +void RiscVEmitter::QuickJAL(RiscVReg scratchreg, RiscVReg rd, const u8 *dst) { + if (!JInRange(GetCodePointer(), dst)) { + static_assert(sizeof(intptr_t) <= sizeof(int64_t)); + int64_t pcdelta = (int64_t)dst - (int64_t)GetCodePointer(); + int32_t lower = (int32_t)SignReduce64(pcdelta, 12); + uintptr_t upper = ((pcdelta - lower) >> 12) << 12; + LI(scratchreg, (uintptr_t)GetCodePointer() + upper); + JALR(rd, scratchreg, lower); + } else { + JAL(rd, dst); + } +} + void RiscVEmitter::SetRegToImmediate(RiscVReg rd, uint64_t value, RiscVReg temp) { int64_t svalue = (int64_t)value; _assert_msg_(IsGPR(rd) && IsGPR(temp), "SetRegToImmediate only supports GPRs"); @@ -1194,13 +1207,24 @@ void RiscVEmitter::SetRegToImmediate(RiscVReg rd, uint64_t value, RiscVReg temp) auto useUpper = [&](int64_t v, void (RiscVEmitter::*upperOp)(RiscVReg, s32), bool force = false) { if (SignReduce64(v, 32) == v || force) { - int32_t lower = (int32_t)SignReduce64(svalue, 12); + int32_t lower = (int32_t)SignReduce64(v, 12); int32_t upper = ((v - lower) >> 12) << 12; - _assert_msg_(force || (int64_t)upper + lower == v, "Upper + ADDI immediate math mistake?"); + bool clearUpper = v >= 0 && upper < 0; + if (clearUpper) { + _assert_msg_(BitsSupported() >= 64, "Shouldn't be possible on 32-bit"); + _assert_msg_(force || (((int64_t)upper + lower) & 0xFFFFFFFF) == v, "Upper + ADDI immediate math mistake?"); + // This isn't safe to do using AUIPC. We can't have the high bit set this way. + if (upperOp == &RiscVEmitter::AUIPC) + return false; + } else { + _assert_msg_(force || (int64_t)upper + lower == v, "Upper + ADDI immediate math mistake?"); + } // Should be fused on some processors. (this->*upperOp)(rd, upper); - if (lower != 0) + if (clearUpper) + ADDIW(rd, rd, lower); + else if (lower != 0) ADDI(rd, rd, lower); return true; } @@ -2124,6 +2148,7 @@ void RiscVEmitter::FCVT(FConv to, FConv from, RiscVReg rd, RiscVReg rs1, Round r _assert_msg_(rm == Round::DYNAMIC || rm == Round::NEAREST_EVEN, "Invalid rounding mode for widening FCVT"); rm = Round::NEAREST_EVEN; } + _assert_msg_(fromFmt != toFmt, "FCVT cannot convert to same float type"); Write32(EncodeR(Opcode32::OP_FP, rd, (Funct3)rm, rs1, (RiscVReg)fromFmt, toFmt, Funct5::FCVT_SZ)); } else { Funct5 funct5 = FConvToIntegerBits(to) == 0 ? Funct5::FCVT_FROMX : Funct5::FCVT_TOX; diff --git a/Common/RiscVEmitter.h b/Common/RiscVEmitter.h index 16ad0176e5..fedb0b0c7f 100644 --- a/Common/RiscVEmitter.h +++ b/Common/RiscVEmitter.h @@ -213,6 +213,19 @@ public: bool BInRange(const void *func) const; bool JInRange(const void *func) const; + void QuickJAL(RiscVReg scratchreg, RiscVReg rd, const u8 *dst); + void QuickJ(RiscVReg scratchreg, const u8 *dst) { + QuickJAL(scratchreg, R_ZERO, dst); + } + void QuickCallFunction(const u8 *func) { + QuickJAL(R_RA, R_RA, func); + } + template + void QuickCallFunction(T *func) { + static_assert(std::is_function::value, "QuickCallFunction without function"); + QuickCallFunction((const u8 *)func); + } + void LUI(RiscVReg rd, s32 simm32); void AUIPC(RiscVReg rd, s32 simm32); diff --git a/Common/System/OSD.cpp b/Common/System/OSD.cpp index fd85dde9ca..e8cf697b27 100644 --- a/Common/System/OSD.cpp +++ b/Common/System/OSD.cpp @@ -128,25 +128,36 @@ void OnScreenDisplay::ShowAchievementUnlocked(int achievementID) { entries_.insert(entries_.begin(), msg); } -void OnScreenDisplay::ShowAchievementProgress(int achievementID, float duration_s) { +void OnScreenDisplay::ShowAchievementProgress(int achievementID, bool show) { double now = time_now_d(); + // There can only be one of these at a time. for (auto &entry : sideEntries_) { - if (entry.numericID == achievementID && entry.type == OSDType::ACHIEVEMENT_PROGRESS) { - // Duplicate, let's just bump the timer. - entry.startTime = now; - entry.endTime = now + (double)duration_s; - // We're done. + if (entry.type == OSDType::ACHIEVEMENT_PROGRESS) { + if (!show) { + // Hide and eventually delete it. + entry.endTime = now + (double)FadeoutTime(); + // Found it, we're done. + return; + } + // Else update it. + entry.numericID = achievementID; + entry.endTime = now + forever_s; return; } } + if (!show) { + // Sanity check + return; + } + // OK, let's make a new side-entry. Entry entry; entry.numericID = achievementID; entry.type = OSDType::ACHIEVEMENT_PROGRESS; entry.startTime = now; - entry.endTime = now + (double)duration_s; + entry.endTime = now + forever_s; sideEntries_.insert(sideEntries_.begin(), entry); } @@ -215,10 +226,15 @@ void OnScreenDisplay::ShowOnOff(const std::string &message, bool on, float durat Show(OSDType::MESSAGE_INFO, message + ": " + (on ? "on" : "off"), duration_s); } -void OnScreenDisplay::SetProgressBar(std::string id, std::string &&message, int minValue, int maxValue, int progress) { - std::lock_guard guard(mutex_); +void OnScreenDisplay::SetProgressBar(std::string id, std::string &&message, float minValue, float maxValue, float progress, float delay) { + _dbg_assert_(!my_isnanorinf(progress)); + _dbg_assert_(!my_isnanorinf(minValue)); + _dbg_assert_(!my_isnanorinf(maxValue)); + double now = time_now_d(); bool found = false; + + std::lock_guard guard(mutex_); for (auto &bar : bars_) { if (bar.id == id) { bar.minValue = minValue; @@ -236,16 +252,27 @@ void OnScreenDisplay::SetProgressBar(std::string id, std::string &&message, int bar.minValue = minValue; bar.maxValue = maxValue; bar.progress = progress; + bar.startTime = now + delay; bar.endTime = now + 60.0; // Show the progress bar for 60 seconds, then fade it out. bars_.push_back(bar); } -void OnScreenDisplay::RemoveProgressBar(std::string id) { +void OnScreenDisplay::RemoveProgressBar(std::string id, bool success, float delay_s) { std::lock_guard guard(mutex_); for (auto iter = bars_.begin(); iter != bars_.end(); iter++) { if (iter->id == id) { - iter->progress = iter->maxValue; - iter->endTime = time_now_d() + FadeoutTime(); + if (success) { + // Quickly shoot up to max, if we weren't there. + if (iter->maxValue != 0.0f) { + iter->progress = iter->maxValue; + } else { + // Fake a full progress + iter->minValue = 0; + iter->maxValue = 1; + iter->progress = 1; + } + } + iter->endTime = time_now_d() + delay_s + FadeoutTime(); break; } } diff --git a/Common/System/OSD.h b/Common/System/OSD.h index 928e1d44d6..cecf0cae79 100644 --- a/Common/System/OSD.h +++ b/Common/System/OSD.h @@ -19,6 +19,7 @@ enum class OSDType { // Side entries ACHIEVEMENT_PROGRESS, // Achievement icon + "measured_progress" text, auto-hide after 2s ACHIEVEMENT_CHALLENGE_INDICATOR, // Achievement icon ONLY, no auto-hide + LEADERBOARD_TRACKER, }; @@ -43,15 +44,15 @@ public: // Specialized achievement-related types. These go to the side notifications, not the top-middle. void ShowAchievementUnlocked(int achievementID); - void ShowAchievementProgress(int achievementID, float duration_s); + void ShowAchievementProgress(int achievementID, bool show); // call with show=false to hide. There can only be one of these. When hiding it's ok to not pass a valid achievementID. void ShowChallengeIndicator(int achievementID, bool show); // call with show=false to hide. void ShowLeaderboardTracker(int leaderboardTrackerID, const char *trackerText, bool show); // show=true is used both for create and update. // Progress bar controls // Set is both create and update. If you set maxValue <= minValue, you'll create an "indeterminate" progress // bar that doesn't show a specific amount of progress. - void SetProgressBar(std::string id, std::string &&message, int minValue, int maxValue, int progress); - void RemoveProgressBar(std::string id); + void SetProgressBar(std::string id, std::string &&message, float minValue, float maxValue, float progress, float delay_s); + void RemoveProgressBar(std::string id, bool success, float delay_s); // Call every frame to keep the sidebar visible. Otherwise it'll fade out. void NudgeSidebar(); @@ -74,9 +75,10 @@ public: struct ProgressBar { std::string id; std::string message; - int minValue; - int maxValue; - int progress; + float minValue; + float maxValue; + float progress; + double startTime; double endTime; }; diff --git a/Common/System/Request.cpp b/Common/System/Request.cpp index dbfcff7c62..8e78352908 100644 --- a/Common/System/Request.cpp +++ b/Common/System/Request.cpp @@ -1,3 +1,5 @@ +#include "ppsspp_config.h" + #include #include "Common/System/Request.h" @@ -6,6 +8,17 @@ #include "Common/File/Path.h" #include "Common/TimeUtil.h" +#if PPSSPP_PLATFORM(ANDROID) + +// Maybe not the most natural place for this, but not sure what would be. It needs to be in the Common project +// unless we want to make another System_ function to retrieve it. + +#include + +JavaVM *gJvm = nullptr; + +#endif + RequestManager g_requestManager; const char *RequestTypeAsString(SystemRequestType type) { diff --git a/Common/System/System.h b/Common/System/System.h index 14001fefa7..17eeda194d 100644 --- a/Common/System/System.h +++ b/Common/System/System.h @@ -117,6 +117,7 @@ enum SystemProperty { SYSPROP_BOARDNAME, SYSPROP_CLIPBOARD_TEXT, SYSPROP_GPUDRIVER_VERSION, + SYSPROP_BUILD_VERSION, // Separate SD cards or similar. // Need hacky solutions to get at this. @@ -135,6 +136,8 @@ enum SystemProperty { SYSPROP_CAN_CREATE_SHORTCUT, + SYSPROP_SUPPORTS_HTTPS, + // Available as Int: SYSPROP_SYSTEMVERSION, SYSPROP_DISPLAY_XRES, diff --git a/Common/UI/IconCache.cpp b/Common/UI/IconCache.cpp index f5b28881a0..830655e3df 100644 --- a/Common/UI/IconCache.cpp +++ b/Common/UI/IconCache.cpp @@ -239,6 +239,7 @@ bool IconCache::InsertIcon(const std::string &key, IconFormat format, std::strin } if (data.empty()) { + _dbg_assert_(false); ERROR_LOG(G3D, "Can't insert empty data into icon cache"); return false; } diff --git a/Common/UI/PopupScreens.h b/Common/UI/PopupScreens.h index fa2f2e00d9..8fdfb68e1f 100644 --- a/Common/UI/PopupScreens.h +++ b/Common/UI/PopupScreens.h @@ -55,6 +55,9 @@ class MessagePopupScreen : public PopupScreen { public: MessagePopupScreen(std::string title, std::string message, std::string button1, std::string button2, std::function callback) : PopupScreen(title, button1, button2), message_(message), callback_(callback) {} + + const char *tag() const override { return "MessagePopupScreen"; } + UI::Event OnChoice; protected: diff --git a/Common/UI/UIScreen.cpp b/Common/UI/UIScreen.cpp index 9fe4e44e1d..62ad927e55 100644 --- a/Common/UI/UIScreen.cpp +++ b/Common/UI/UIScreen.cpp @@ -1,5 +1,6 @@ #include #include "Common/System/Display.h" +#include "Common/System/System.h" #include "Common/Input/InputState.h" #include "Common/Input/KeyCodes.h" #include "Common/Math/curves.h" @@ -9,7 +10,6 @@ #include "Common/UI/Root.h" #include "Common/Data/Text/I18n.h" #include "Common/Render/DrawBuffer.h" - #include "Common/Log.h" static const bool ClickDebug = false; @@ -426,7 +426,6 @@ void PopupScreen::CreateViews() { CreatePopupContents(box_); root_->SetDefaultFocusView(box_); - if (ShowButtons() && !button1_.empty()) { // And the two buttons at the bottom. LinearLayout *buttonRow = new LinearLayout(ORIENT_HORIZONTAL, new LinearLayoutParams(200, WRAP_CONTENT)); diff --git a/Common/UI/View.cpp b/Common/UI/View.cpp index 9495334990..587db038ab 100644 --- a/Common/UI/View.cpp +++ b/Common/UI/View.cpp @@ -607,6 +607,7 @@ CollapsibleHeader::CollapsibleHeader(bool *toggle, const std::string &text, Layo void CollapsibleHeader::Draw(UIContext &dc) { Style style = dc.theme->itemStyle; + style.background.color = 0; if (HasFocus()) style = dc.theme->itemFocusedStyle; if (down_) style = dc.theme->itemDownStyle; if (!IsEnabled()) style = dc.theme->itemDisabledStyle; @@ -618,7 +619,9 @@ void CollapsibleHeader::Draw(UIContext &dc) { dc.SetFontStyle(dc.theme->uiFontSmall); dc.DrawText(text_.c_str(), bounds_.x + 4 + xoff, bounds_.centerY(), dc.theme->headerStyle.fgColor, ALIGN_LEFT | ALIGN_VCENTER); dc.Draw()->DrawImageCenterTexel(dc.theme->whiteImage, bounds_.x, bounds_.y2() - 2, bounds_.x2(), bounds_.y2(), dc.theme->headerStyle.fgColor); - dc.Draw()->DrawImageRotated(ImageID("I_ARROW"), bounds_.x + 20.0f, bounds_.y + 20.0f, 1.0f, *toggle_ ? -M_PI / 2 : M_PI); + if (hasSubItems_) { + dc.Draw()->DrawImageRotated(ImageID("I_ARROW"), bounds_.x + 20.0f, bounds_.y + 20.0f, 1.0f, *toggle_ ? -M_PI / 2 : M_PI); + } } void CollapsibleHeader::GetContentDimensionsBySpec(const UIContext &dc, MeasureSpec horiz, MeasureSpec vert, float &w, float &h) const { diff --git a/Common/UI/View.h b/Common/UI/View.h index 52a7b5fe26..91dc739d5e 100644 --- a/Common/UI/View.h +++ b/Common/UI/View.h @@ -870,6 +870,10 @@ public: void Draw(UIContext &dc) override; void GetContentDimensionsBySpec(const UIContext &dc, MeasureSpec horiz, MeasureSpec vert, float &w, float &h) const override; void GetContentDimensions(const UIContext &dc, float &w, float &h) const override; + + void SetHasSubitems(bool hasSubItems) { hasSubItems_ = hasSubItems; } +private: + bool hasSubItems_ = true; }; class BitCheckBox : public CheckBox { diff --git a/Common/UI/ViewGroup.cpp b/Common/UI/ViewGroup.cpp index 757192fab8..e418d63008 100644 --- a/Common/UI/ViewGroup.cpp +++ b/Common/UI/ViewGroup.cpp @@ -1183,9 +1183,9 @@ StickyChoice *ChoiceStrip::Choice(int index) { CollapsibleSection::CollapsibleSection(const std::string &title, LayoutParams *layoutParams) : LinearLayout(ORIENT_VERTICAL, layoutParams) { SetSpacing(0.0f); - CollapsibleHeader *heading = new CollapsibleHeader(&open_, title); - views_.push_back(heading); - heading->OnClick.Add([=](UI::EventParams &) { + heading_ = new CollapsibleHeader(&open_, title); + views_.push_back(heading_); + heading_->OnClick.Add([=](UI::EventParams &) { // Change the visibility of all children except the first one. // Later maybe try something more ambitious. for (size_t i = 1; i < views_.size(); i++) { @@ -1195,4 +1195,9 @@ CollapsibleSection::CollapsibleSection(const std::string &title, LayoutParams *l }); } +void CollapsibleSection::Update() { + ViewGroup::Update(); + heading_->SetHasSubitems(views_.size() > 1); +} + } // namespace UI diff --git a/Common/UI/ViewGroup.h b/Common/UI/ViewGroup.h index 2f014ac48b..310eb20766 100644 --- a/Common/UI/ViewGroup.h +++ b/Common/UI/ViewGroup.h @@ -324,12 +324,17 @@ private: std::vector tabTweens_; }; +class CollapsibleHeader; + class CollapsibleSection : public LinearLayout { public: CollapsibleSection(const std::string &title, LayoutParams *layoutParams = nullptr); + void Update() override; + private: bool open_ = true; + CollapsibleHeader *heading_; }; } // namespace UI diff --git a/Common/VR/PPSSPPVR.cpp b/Common/VR/PPSSPPVR.cpp index 85bfe7ff58..187fe5759a 100644 --- a/Common/VR/PPSSPPVR.cpp +++ b/Common/VR/PPSSPPVR.cpp @@ -907,7 +907,7 @@ void UpdateVRViewMatrices() { XrQuaternionf_ToMatrix4f(&invView.orientation, M); // Apply 6Dof head movement - if (!flatScreen && g_Config.bEnable6DoF && !g_Config.bHeadRotationEnabled && (g_Config.iCameraPitch == 0)) { + if (g_Config.bEnable6DoF && !g_Config.bHeadRotationEnabled && (g_Config.iCameraPitch == 0)) { M[3] -= vrView[0].pose.position.x * (vrMirroring[VR_MIRRORING_AXIS_X] ? -1.0f : 1.0f) * scale; M[7] -= vrView[0].pose.position.y * (vrMirroring[VR_MIRRORING_AXIS_Y] ? -1.0f : 1.0f) * scale; M[11] -= vrView[0].pose.position.z * (vrMirroring[VR_MIRRORING_AXIS_Z] ? -1.0f : 1.0f) * scale; diff --git a/Core/Config.cpp b/Core/Config.cpp index 030d828fda..da1c279033 100644 --- a/Core/Config.cpp +++ b/Core/Config.cpp @@ -57,7 +57,7 @@ #include "GPU/Common/FramebufferManagerCommon.h" // TODO: Find a better place for this. -http::Downloader g_DownloadManager; +http::RequestManager g_DownloadManager; Config g_Config; @@ -129,7 +129,7 @@ std::string CreateRandMAC() { } static int DefaultCpuCore() { -#if PPSSPP_ARCH(ARM) || PPSSPP_ARCH(ARM64) || PPSSPP_ARCH(X86) || PPSSPP_ARCH(AMD64) +#if PPSSPP_ARCH(ARM) || PPSSPP_ARCH(ARM64) || PPSSPP_ARCH(X86) || PPSSPP_ARCH(AMD64) || PPSSPP_ARCH(RISCV64) if (System_GetPropertyBool(SYSPROP_CAN_JIT)) return (int)CPUCore::JIT; return (int)CPUCore::IR_JIT; @@ -139,7 +139,7 @@ static int DefaultCpuCore() { } static bool DefaultCodeGen() { -#if PPSSPP_ARCH(ARM) || PPSSPP_ARCH(ARM64) || PPSSPP_ARCH(X86) || PPSSPP_ARCH(AMD64) +#if PPSSPP_ARCH(ARM) || PPSSPP_ARCH(ARM64) || PPSSPP_ARCH(X86) || PPSSPP_ARCH(AMD64) || PPSSPP_ARCH(RISCV64) return true; #else return false; @@ -611,6 +611,8 @@ static const ConfigSetting graphicsSettings[] = { ConfigSetting("InflightFrames", &g_Config.iInflightFrames, 3, CfgFlag::DEFAULT), ConfigSetting("RenderDuplicateFrames", &g_Config.bRenderDuplicateFrames, false, CfgFlag::PER_GAME), + ConfigSetting("MultiThreading", &g_Config.bRenderMultiThreading, true, CfgFlag::DEFAULT), + ConfigSetting("ShaderCache", &g_Config.bShaderCache, true, CfgFlag::DONT_SAVE), // Doesn't save. Ini-only. ConfigSetting("GpuLogProfiler", &g_Config.bGpuLogProfiler, false, CfgFlag::DEFAULT), }; @@ -773,6 +775,7 @@ static const ConfigSetting controlSettings[] = { ConfigSetting("MouseSmoothing", &g_Config.fMouseSmoothing, 0.9f, CfgFlag::PER_GAME), ConfigSetting("SystemControls", &g_Config.bSystemControls, true, CfgFlag::DEFAULT), + ConfigSetting("RapidFileInterval", &g_Config.iRapidFireInterval, 5, CfgFlag::DEFAULT), }; static const ConfigSetting networkSettings[] = { @@ -796,25 +799,12 @@ static const ConfigSetting networkSettings[] = { ConfigSetting("QuickChat5", &g_Config.sQuickChat4, "Quick Chat 5", CfgFlag::PER_GAME), }; -static int DefaultSystemParamLanguage() { - int defaultLang = PSP_SYSTEMPARAM_LANGUAGE_ENGLISH; - if (g_Config.bFirstRun) { - // TODO: Be smart about same language, different country - auto &langValuesMapping = g_Config.GetLangValuesMapping(); - auto iter = langValuesMapping.find(g_Config.sLanguageIni); - if (iter != langValuesMapping.end()) { - defaultLang = iter->second.second; - } - } - return defaultLang; -} - static const ConfigSetting systemParamSettings[] = { ConfigSetting("PSPModel", &g_Config.iPSPModel, PSP_MODEL_SLIM, CfgFlag::PER_GAME | CfgFlag::REPORT), ConfigSetting("PSPFirmwareVersion", &g_Config.iFirmwareVersion, PSP_DEFAULT_FIRMWARE, CfgFlag::PER_GAME | CfgFlag::REPORT), ConfigSetting("NickName", &g_Config.sNickName, "PPSSPP", CfgFlag::PER_GAME), ConfigSetting("MacAddress", &g_Config.sMACAddress, "", CfgFlag::PER_GAME), - ConfigSetting("Language", &g_Config.iLanguage, &DefaultSystemParamLanguage, CfgFlag::PER_GAME | CfgFlag::REPORT), + ConfigSetting("GameLanguage", &g_Config.iLanguage, -1, CfgFlag::PER_GAME | CfgFlag::REPORT), ConfigSetting("ParamTimeFormat", &g_Config.iTimeFormat, PSP_SYSTEMPARAM_TIME_FORMAT_24HR, CfgFlag::PER_GAME), ConfigSetting("ParamDateFormat", &g_Config.iDateFormat, PSP_SYSTEMPARAM_DATE_FORMAT_YYYYMMDD, CfgFlag::PER_GAME), ConfigSetting("TimeZone", &g_Config.iTimeZone, 0, CfgFlag::PER_GAME), @@ -1174,8 +1164,7 @@ void Config::Load(const char *iniFileName, const char *controllerIniFilename) { if (iRunCount % 10 == 0 && bCheckForNewVersion) { const char *versionUrl = "http://www.ppsspp.org/version.json"; const char *acceptMime = "application/json, text/*; q=0.9, */*; q=0.8"; - auto dl = g_DownloadManager.StartDownloadWithCallback(versionUrl, Path(), &DownloadCompletedCallback, acceptMime); - dl->SetHidden(true); + g_DownloadManager.StartDownloadWithCallback(versionUrl, Path(), http::ProgressBarMode::NONE, &DownloadCompletedCallback, "version", acceptMime); } INFO_LOG(LOADER, "Loading controller config: %s", controllerIniFilename_.c_str()); @@ -1364,7 +1353,7 @@ void Config::NotifyUpdatedCpuCore() { #define PPSSPP_GIT_VERSION "v0.0.1-gaaaaaaaaa" #endif -void Config::DownloadCompletedCallback(http::Download &download) { +void Config::DownloadCompletedCallback(http::Request &download) { if (download.ResultCode() != 200) { ERROR_LOG(LOADER, "Failed to download %s: %d", download.url().c_str(), download.ResultCode()); return; @@ -1772,3 +1761,18 @@ void Config::GetReportingInfo(UrlEncoder &data) { bool Config::IsPortrait() const { return (iInternalScreenRotation == ROTATION_LOCKED_VERTICAL || iInternalScreenRotation == ROTATION_LOCKED_VERTICAL180) && !bSkipBufferEffects; } + +int Config::GetPSPLanguage() { + if (g_Config.iLanguage == -1) { + const auto &langValuesMapping = GetLangValuesMapping(); + auto iter = langValuesMapping.find(g_Config.sLanguageIni); + if (iter != langValuesMapping.end()) { + return iter->second.second; + } else { + // Fallback to English + return PSP_SYSTEMPARAM_LANGUAGE_ENGLISH; + } + } else { + return g_Config.iLanguage; + } +} diff --git a/Core/Config.h b/Core/Config.h index bd1b4223a1..aabc73fd02 100644 --- a/Core/Config.h +++ b/Core/Config.h @@ -41,8 +41,8 @@ enum ChatPositions { }; namespace http { - class Download; - class Downloader; + class Request; + class RequestManager; } struct UrlEncoder; @@ -238,6 +238,7 @@ public: bool bGfxDebugOutput; int iInflightFrames; bool bRenderDuplicateFrames; + bool bRenderMultiThreading; // Sound bool bEnableSound; @@ -386,6 +387,7 @@ public: float fMouseSmoothing; bool bSystemControls; + int iRapidFireInterval; // Use the hardware scaler to scale up the image to save fillrate. Similar to Windows' window size, really. int iAndroidHwScale; // 0 = device resolution. 1 = 480x272 (extended to correct aspect), 2 = 960x544 etc. @@ -543,7 +545,7 @@ public: void RemoveRecent(const std::string &file); void CleanRecent(); - static void DownloadCompletedCallback(http::Download &download); + static void DownloadCompletedCallback(http::Request &download); void DismissUpgrade(); void ResetControlLayout(); @@ -569,6 +571,10 @@ public: void SetAppendedConfigIni(const Path &path); void UpdateAfterSettingAutoFrameSkip(); void NotifyUpdatedCpuCore(); + + // Applies the Auto setting if set. Returns an enum value from PSP_SYSTEMPARAM_LANGUAGE_*. + int GetPSPLanguage(); + protected: void LoadStandardControllerIni(); void LoadLangValuesMapping(); @@ -595,6 +601,6 @@ private: std::string CreateRandMAC(); // TODO: Find a better place for this. -extern http::Downloader g_DownloadManager; +extern http::RequestManager g_DownloadManager; extern Config g_Config; diff --git a/Core/Core.vcxproj b/Core/Core.vcxproj index 5a347d4006..9794f02f81 100644 --- a/Core/Core.vcxproj +++ b/Core/Core.vcxproj @@ -580,6 +580,7 @@ + @@ -593,6 +594,16 @@ + + + + + + + + + + @@ -1154,6 +1165,7 @@ + @@ -1161,6 +1173,9 @@ + + + diff --git a/Core/Core.vcxproj.filters b/Core/Core.vcxproj.filters index 49c2a10c7b..e437d0d813 100644 --- a/Core/Core.vcxproj.filters +++ b/Core/Core.vcxproj.filters @@ -91,6 +91,9 @@ {678fa299-0ff7-4983-982d-2da47b52e238} + + {067e3128-3aaf-4ed1-b19e-bab11606abe7} + @@ -1204,6 +1207,39 @@ Core + + MIPS\RiscV + + + MIPS\RiscV + + + MIPS\RiscV + + + MIPS\RiscV + + + MIPS\RiscV + + + MIPS\RiscV + + + MIPS\RiscV + + + MIPS\RiscV + + + MIPS\RiscV + + + MIPS\RiscV + + + MIPS\IR + @@ -1950,6 +1986,18 @@ Core + + MIPS\RiscV + + + MIPS\RiscV + + + MIPS\RiscV + + + MIPS\IR + diff --git a/Core/Debugger/WebSocket.cpp b/Core/Debugger/WebSocket.cpp index e704a5c00e..1ba39ad0ec 100644 --- a/Core/Debugger/WebSocket.cpp +++ b/Core/Debugger/WebSocket.cpp @@ -131,7 +131,7 @@ static void SetupDebuggerLock() { } } -void HandleDebuggerRequest(const http::Request &request) { +void HandleDebuggerRequest(const http::ServerRequest &request) { net::WebSocketServer *ws = net::WebSocketServer::CreateAsUpgrade(request, "debugger.ppsspp.org"); if (!ws) return; diff --git a/Core/Debugger/WebSocket.h b/Core/Debugger/WebSocket.h index 0151ca686a..f05856db20 100644 --- a/Core/Debugger/WebSocket.h +++ b/Core/Debugger/WebSocket.h @@ -18,9 +18,9 @@ #pragma once namespace http { -class Request; +class ServerRequest; } -void HandleDebuggerRequest(const http::Request &request); +void HandleDebuggerRequest(const http::ServerRequest &request); // Note: blocks. void StopAllDebuggers(); diff --git a/Core/Dialog/PSPDialog.cpp b/Core/Dialog/PSPDialog.cpp index 964f7ae301..dd05b2441c 100644 --- a/Core/Dialog/PSPDialog.cpp +++ b/Core/Dialog/PSPDialog.cpp @@ -42,7 +42,7 @@ PSPDialog::~PSPDialog() { void PSPDialog::InitCommon() { UpdateCommon(); - if (GetCommonParam() && GetCommonParam()->language != g_Config.iLanguage) { + if (GetCommonParam() && GetCommonParam()->language != g_Config.GetPSPLanguage()) { WARN_LOG(SCEUTILITY, "Game requested language %d, ignoring and using user language", GetCommonParam()->language); } } diff --git a/Core/FileLoaders/HTTPFileLoader.h b/Core/FileLoaders/HTTPFileLoader.h index ec92b8b9d5..e4bf8ee9be 100644 --- a/Core/FileLoaders/HTTPFileLoader.h +++ b/Core/FileLoaders/HTTPFileLoader.h @@ -71,7 +71,7 @@ private: s64 filepos_ = 0; Url url_; http::Client client_; - http::RequestProgress progress_; + net::RequestProgress progress_; ::Path filename_; bool connected_ = false; bool cancel_ = false; diff --git a/Core/HLE/HLE.cpp b/Core/HLE/HLE.cpp index ce7a04c345..363a809476 100644 --- a/Core/HLE/HLE.cpp +++ b/Core/HLE/HLE.cpp @@ -540,6 +540,9 @@ void HLEReturnFromMipsCall() { const static u32 deadbeefRegs[12] = {0xDEADBEEF, 0xDEADBEEF, 0xDEADBEEF, 0xDEADBEEF, 0xDEADBEEF, 0xDEADBEEF, 0xDEADBEEF, 0xDEADBEEF, 0xDEADBEEF, 0xDEADBEEF, 0xDEADBEEF, 0xDEADBEEF}; inline static void SetDeadbeefRegs() { + // Not exactly the same, but any time a syscall happens, it should clear ll. + currentMIPS->llBit = 0; + if (g_Config.bSkipDeadbeefFilling) return; diff --git a/Core/HLE/sceCtrl.cpp b/Core/HLE/sceCtrl.cpp index e2d60a723c..3c5ca06055 100644 --- a/Core/HLE/sceCtrl.cpp +++ b/Core/HLE/sceCtrl.cpp @@ -103,6 +103,8 @@ static u8 vibrationRightDropout = 160; // Not related to sceCtrl*RapidFire(), although it may do the same thing. static bool emuRapidFire = false; static u32 emuRapidFireFrames = 0; +static bool emuRapidFireToggle = true; +static u32 emuRapidFireInterval = 5; // These buttons are not affected by rapid fire (neither is analog.) const u32 CTRL_EMU_RAPIDFIRE_MASK = CTRL_UP | CTRL_DOWN | CTRL_LEFT | CTRL_RIGHT; @@ -113,7 +115,7 @@ static void __CtrlUpdateLatch() u64 t = CoreTiming::GetGlobalTimeUs(); u32 buttons = ctrlCurrent.buttons; - if (emuRapidFire && (emuRapidFireFrames % 10) < 5) + if (emuRapidFire && emuRapidFireToggle) buttons &= CTRL_EMU_RAPIDFIRE_MASK; ReplayApplyCtrl(buttons, ctrlCurrent.analog, t); @@ -173,6 +175,19 @@ u32 __CtrlPeekButtons() return ctrlCurrent.buttons; } +u32 __CtrlPeekButtonsVisual() +{ + u32 buttons; + { + std::lock_guard guard(ctrlMutex); + buttons = ctrlCurrent.buttons; + } + + if (emuRapidFire && emuRapidFireToggle) + buttons &= CTRL_EMU_RAPIDFIRE_MASK; + return buttons; +} + void __CtrlPeekAnalog(int stick, float *x, float *y) { std::lock_guard guard(ctrlMutex); @@ -206,9 +221,11 @@ void __CtrlSetAnalogXY(int stick, float x, float y) ctrlCurrent.analog[stick][CTRL_ANALOG_Y] = scaledY; } -void __CtrlSetRapidFire(bool state) +void __CtrlSetRapidFire(bool state, int interval) { emuRapidFire = state; + emuRapidFireToggle = true; + emuRapidFireInterval = interval; } bool __CtrlGetRapidFire() @@ -298,6 +315,10 @@ retry: static void __CtrlVblank() { emuRapidFireFrames++; + if (emuRapidFireFrames >= emuRapidFireInterval) { + emuRapidFireFrames = 0; + emuRapidFireToggle = !emuRapidFireToggle; + } // Reduce gamepad Vibration by set % each frame leftVibration *= (float)vibrationLeftDropout / 256.0f; diff --git a/Core/HLE/sceCtrl.h b/Core/HLE/sceCtrl.h index 02feaceb43..e633dd62de 100644 --- a/Core/HLE/sceCtrl.h +++ b/Core/HLE/sceCtrl.h @@ -73,11 +73,12 @@ void __CtrlUpdateButtons(u32 bitsToSet, u32 bitsToClear); void __CtrlSetAnalogXY(int stick, float x, float y); // Call this to enable rapid-fire. This will cause buttons other than arrows to alternate. -void __CtrlSetRapidFire(bool state); +void __CtrlSetRapidFire(bool state, int interval); bool __CtrlGetRapidFire(); // For use by internal UI like MsgDialog u32 __CtrlPeekButtons(); +u32 __CtrlPeekButtonsVisual(); // also incorporates rapid-fire void __CtrlPeekAnalog(int stick, float *x, float *y); u32 __CtrlReadLatch(); diff --git a/Core/HLE/sceDisplay.cpp b/Core/HLE/sceDisplay.cpp index 19a1224bc0..70fbc0796f 100644 --- a/Core/HLE/sceDisplay.cpp +++ b/Core/HLE/sceDisplay.cpp @@ -594,7 +594,7 @@ void __DisplayFlip(int cyclesLate) { #ifndef _DEBUG auto err = GetI18NCategory(I18NCat::ERRORS); if (g_Config.bSoftwareRendering) { - g_OSD.Show(OSDType::MESSAGE_INFO, err->T("Running slow: Try turning off Software Rendering")); + g_OSD.Show(OSDType::MESSAGE_INFO, err->T("Running slow: Try turning off Software Rendering"), 5.0f); } else { g_OSD.Show(OSDType::MESSAGE_INFO, err->T("Running slow: try frameskip, sound is choppy when slow")); } diff --git a/Core/HLE/sceImpose.cpp b/Core/HLE/sceImpose.cpp index c5ab64a24a..5cb102a8d0 100644 --- a/Core/HLE/sceImpose.cpp +++ b/Core/HLE/sceImpose.cpp @@ -40,7 +40,7 @@ static u32 backlightOffTime; void __ImposeInit() { - language = g_Config.iLanguage; + language = g_Config.GetPSPLanguage(); if (PSP_CoreParameter().compat.flags().EnglishOrJapaneseOnly) { if (language != PSP_SYSTEMPARAM_LANGUAGE_ENGLISH && language != PSP_SYSTEMPARAM_LANGUAGE_JAPANESE) { language = PSP_SYSTEMPARAM_LANGUAGE_ENGLISH; @@ -76,7 +76,7 @@ static u32 sceImposeGetBatteryIconStatus(u32 chargingPtr, u32 iconStatusPtr) static u32 sceImposeSetLanguageMode(u32 languageVal, u32 buttonVal) { language = languageVal; buttonValue = buttonVal; - if (language != g_Config.iLanguage) { + if (language != g_Config.GetPSPLanguage()) { return hleLogWarning(SCEUTILITY, 0, "ignoring requested language"); } return hleLogSuccessI(SCEUTILITY, 0); diff --git a/Core/HLE/sceNp2.cpp b/Core/HLE/sceNp2.cpp index 59f7b0cfdf..70b349145b 100644 --- a/Core/HLE/sceNp2.cpp +++ b/Core/HLE/sceNp2.cpp @@ -131,7 +131,8 @@ static int sceNpMatching2ContextStart(int ctxId) //npMatching2Ctx.started = true; Url url("http://static-resource.np.community.playstation.net/np/resource/psp-title/" + std::string(npTitleId.data) + "_00/matching/" + std::string(npTitleId.data) + "_00-matching.xml"); http::Client client; - http::RequestProgress progress; + bool cancelled = false; + net::RequestProgress progress(&cancelled); if (!client.Resolve(url.Host().c_str(), url.Port())) { return hleLogError(SCENET, SCE_NP_COMMUNITY_SERVER_ERROR_NO_SUCH_TITLE, "HTTP failed to resolve %s", url.Resource().c_str()); } diff --git a/Core/HLE/sceUtility.cpp b/Core/HLE/sceUtility.cpp index 1ff229a9ca..1f559d79e5 100644 --- a/Core/HLE/sceUtility.cpp +++ b/Core/HLE/sceUtility.cpp @@ -898,7 +898,7 @@ static u32 sceUtilityGetSystemParamInt(u32 id, u32 destaddr) param = g_Config.bDayLightSavings?PSP_SYSTEMPARAM_DAYLIGHTSAVINGS_SAVING:PSP_SYSTEMPARAM_DAYLIGHTSAVINGS_STD; break; case PSP_SYSTEMPARAM_ID_INT_LANGUAGE: - param = g_Config.iLanguage; + param = g_Config.GetPSPLanguage(); if (PSP_CoreParameter().compat.flags().EnglishOrJapaneseOnly) { if (param != PSP_SYSTEMPARAM_LANGUAGE_ENGLISH && param != PSP_SYSTEMPARAM_LANGUAGE_JAPANESE) { param = PSP_SYSTEMPARAM_LANGUAGE_ENGLISH; diff --git a/Core/MIPS/ARM/ArmCompLoadStore.cpp b/Core/MIPS/ARM/ArmCompLoadStore.cpp index 1c62d7f986..e4e9131a28 100644 --- a/Core/MIPS/ARM/ArmCompLoadStore.cpp +++ b/Core/MIPS/ARM/ArmCompLoadStore.cpp @@ -385,6 +385,12 @@ namespace MIPSComp } } + void ArmJit::Comp_StoreSync(MIPSOpcode op) { + CONDITIONAL_DISABLE(LSU); + + DISABLE; + } + void ArmJit::Comp_Cache(MIPSOpcode op) { CONDITIONAL_DISABLE(LSU); diff --git a/Core/MIPS/ARM/ArmJit.h b/Core/MIPS/ARM/ArmJit.h index 829f53ef7d..04f1ac79d3 100644 --- a/Core/MIPS/ARM/ArmJit.h +++ b/Core/MIPS/ARM/ArmJit.h @@ -61,6 +61,7 @@ public: // Ops void Comp_ITypeMem(MIPSOpcode op) override; + void Comp_StoreSync(MIPSOpcode op) override; void Comp_Cache(MIPSOpcode op) override; void Comp_RelBranch(MIPSOpcode op) override; diff --git a/Core/MIPS/ARM64/Arm64Asm.cpp b/Core/MIPS/ARM64/Arm64Asm.cpp index 9f70435a78..5a0b12ce65 100644 --- a/Core/MIPS/ARM64/Arm64Asm.cpp +++ b/Core/MIPS/ARM64/Arm64Asm.cpp @@ -312,7 +312,7 @@ void Arm64Jit::GenerateFixedCode(const JitOptions &jo) { // Leave this at the end, add more stuff above. if (enableDisasm) { - std::vector lines = DisassembleArm64(start, GetCodePtr() - start); + std::vector lines = DisassembleArm64(start, (int)(GetCodePtr() - start)); for (auto s : lines) { INFO_LOG(JIT, "%s", s.c_str()); } diff --git a/Core/MIPS/ARM64/Arm64CompFPU.cpp b/Core/MIPS/ARM64/Arm64CompFPU.cpp index c1cd969582..2756f5e8ed 100644 --- a/Core/MIPS/ARM64/Arm64CompFPU.cpp +++ b/Core/MIPS/ARM64/Arm64CompFPU.cpp @@ -292,7 +292,7 @@ void Arm64Jit::Comp_FPU2op(MIPSOpcode op) { fp.FMOV(fpr.R(fd), S0); } else { fp.FCMP(fpr.R(fs), fpr.R(fs)); - fp.FCVTS(fpr.R(fd), fpr.R(fs), ROUND_Z); + fp.FCVTS(fpr.R(fd), fpr.R(fs), ROUND_N); FixupBranch skip_nan = B(CC_VC); MOVI2R(SCRATCH1, 0x7FFFFFFF); fp.FMOV(fpr.R(fd), SCRATCH1); diff --git a/Core/MIPS/ARM64/Arm64CompLoadStore.cpp b/Core/MIPS/ARM64/Arm64CompLoadStore.cpp index 08e7d541a6..9824f07001 100644 --- a/Core/MIPS/ARM64/Arm64CompLoadStore.cpp +++ b/Core/MIPS/ARM64/Arm64CompLoadStore.cpp @@ -434,6 +434,12 @@ namespace MIPSComp { } } + void Arm64Jit::Comp_StoreSync(MIPSOpcode op) { + CONDITIONAL_DISABLE(LSU); + + DISABLE; + } + void Arm64Jit::Comp_Cache(MIPSOpcode op) { CONDITIONAL_DISABLE(LSU); diff --git a/Core/MIPS/ARM64/Arm64Jit.h b/Core/MIPS/ARM64/Arm64Jit.h index a9c97a85ad..f3d6acc6a7 100644 --- a/Core/MIPS/ARM64/Arm64Jit.h +++ b/Core/MIPS/ARM64/Arm64Jit.h @@ -62,6 +62,7 @@ public: // Ops void Comp_ITypeMem(MIPSOpcode op) override; + void Comp_StoreSync(MIPSOpcode op) override; void Comp_Cache(MIPSOpcode op) override; void Comp_RelBranch(MIPSOpcode op) override; @@ -213,7 +214,9 @@ private: bool ReplaceJalTo(u32 dest); + // Clobbers SCRATCH2. void SaveStaticRegisters(); + // Clobbers SCRATCH2. void LoadStaticRegisters(); void WriteExit(u32 destination, int exit_num); diff --git a/Core/MIPS/ARM64/Arm64RegCache.cpp b/Core/MIPS/ARM64/Arm64RegCache.cpp index ac97e33e74..7dc2ba1ddf 100644 --- a/Core/MIPS/ARM64/Arm64RegCache.cpp +++ b/Core/MIPS/ARM64/Arm64RegCache.cpp @@ -180,7 +180,7 @@ void Arm64RegCache::MapRegTo(ARM64Reg reg, MIPSGPReg mipsReg, int mapFlags) { ar[reg].isDirty = (mapFlags & MAP_DIRTY) ? true : false; if ((mapFlags & MAP_NOINIT) != MAP_NOINIT) { if (mipsReg == MIPS_REG_ZERO) { - // If we get a request to load the zero register, at least we won't spend + // If we get a request to map the zero register, at least we won't spend // time on a memory access... emit_->MOVI2R(reg, 0); @@ -777,7 +777,7 @@ void Arm64RegCache::FlushAll() { // Re-pointerify emit_->MOVK(EncodeRegTo64(allocs[i].ar), ((uint64_t)Memory::base) >> 32, SHIFT_32); ar[allocs[i].ar].pointerified = true; - } else { + } else if (!allocs[i].pointerified) { // If this register got pointerified on the way, mark it as not, so that after save/reload (like in an interpreter fallback), it won't be regarded as such, as it simply won't be. ar[allocs[i].ar].pointerified = false; } diff --git a/Core/MIPS/ARM64/Arm64RegCacheFPU.cpp b/Core/MIPS/ARM64/Arm64RegCacheFPU.cpp index 6a7b7ada56..6771713652 100644 --- a/Core/MIPS/ARM64/Arm64RegCacheFPU.cpp +++ b/Core/MIPS/ARM64/Arm64RegCacheFPU.cpp @@ -319,6 +319,7 @@ void Arm64RegCacheFPU::FlushR(MIPSReg r) { if (mr[r].reg == INVALID_REG) { ERROR_LOG(JIT, "FlushR: MipsReg had bad ArmReg"); } + FlushArmReg((ARM64Reg)(S0 + mr[r].reg)); break; case ML_MEM: @@ -329,8 +330,6 @@ void Arm64RegCacheFPU::FlushR(MIPSReg r) { //BAD break; } - mr[r].loc = ML_MEM; - mr[r].reg = (int)INVALID_REG; } Arm64Gen::ARM64Reg Arm64RegCacheFPU::ARM64RegForFlush(int r) { diff --git a/Core/MIPS/ARM64/Arm64RegCacheFPU.h b/Core/MIPS/ARM64/Arm64RegCacheFPU.h index 657d218611..40b7647448 100644 --- a/Core/MIPS/ARM64/Arm64RegCacheFPU.h +++ b/Core/MIPS/ARM64/Arm64RegCacheFPU.h @@ -17,8 +17,6 @@ #pragma once -#pragma once - #include "Core/MIPS/MIPS.h" #include "Core/MIPS/ARM64/Arm64RegCache.h" #include "Core/MIPS/MIPSVFPUUtils.h" @@ -165,7 +163,6 @@ private: MIPSComp::JitOptions *jo_; int numARMFpuReg_; - int qTime_; enum { // On ARM64, each of the 32 registers are full 128-bit. No sharing of components! diff --git a/Core/MIPS/IR/IRAnalysis.cpp b/Core/MIPS/IR/IRAnalysis.cpp new file mode 100644 index 0000000000..0a8b861b89 --- /dev/null +++ b/Core/MIPS/IR/IRAnalysis.cpp @@ -0,0 +1,135 @@ +// Copyright (c) 2016- PPSSPP Project. + +// 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, version 2.0 or later versions. + +// 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 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#include "Core/MIPS/IR/IRAnalysis.h" + +static bool IRReadsFrom(const IRInst &inst, int reg, char type, bool directly = false) { + const IRMeta *m = GetIRMeta(inst.op); + + if (m->types[1] == type && inst.src1 == reg) { + return true; + } + if (m->types[2] == type && inst.src2 == reg) { + return true; + } + if ((m->flags & (IRFLAG_SRC3 | IRFLAG_SRC3DST)) != 0 && m->types[0] == type && inst.src3 == reg) { + return true; + } + if (!directly) { + if (inst.op == IROp::Interpret || inst.op == IROp::CallReplacement || inst.op == IROp::Syscall || inst.op == IROp::Break) + return true; + if (inst.op == IROp::Breakpoint || inst.op == IROp::MemoryCheck) + return true; + } + return false; +} + +bool IRReadsFromFPR(const IRInst &inst, int reg, bool directly) { + if (IRReadsFrom(inst, reg, 'F', directly)) + return true; + + const IRMeta *m = GetIRMeta(inst.op); + + // We also need to check V and 2. Indirect reads already checked, don't check again. + if (m->types[1] == 'V' && reg >= inst.src1 && reg < inst.src1 + 4) + return true; + if (m->types[1] == '2' && reg >= inst.src1 && reg < inst.src1 + 2) + return true; + if (m->types[2] == 'V' && reg >= inst.src2 && reg < inst.src2 + 4) + return true; + if (m->types[2] == '2' && reg >= inst.src2 && reg < inst.src2 + 2) + return true; + if ((m->flags & (IRFLAG_SRC3 | IRFLAG_SRC3DST)) != 0) { + if (m->types[0] == 'V' && reg >= inst.src3 && reg <= inst.src3 + 4) + return true; + if (m->types[0] == '2' && reg >= inst.src3 && reg <= inst.src3 + 2) + return true; + } + return false; +} + +bool IRReadsFromGPR(const IRInst &inst, int reg, bool directly) { + return IRReadsFrom(inst, reg, 'G', directly); +} + +int IRDestGPR(const IRInst &inst) { + const IRMeta *m = GetIRMeta(inst.op); + + if ((m->flags & IRFLAG_SRC3) == 0 && m->types[0] == 'G') { + return inst.dest; + } + return -1; +} + +bool IRWritesToGPR(const IRInst &inst, int reg) { + return IRDestGPR(inst) == reg; +} + +bool IRWritesToFPR(const IRInst &inst, int reg) { + const IRMeta *m = GetIRMeta(inst.op); + + // Doesn't write to anything. + if ((m->flags & IRFLAG_SRC3) != 0) + return false; + + if (m->types[0] == 'F' && reg == inst.dest) + return true; + if (m->types[0] == 'V' && reg >= inst.dest && reg < inst.dest + 4) + return true; + if (m->types[0] == '2' && reg >= inst.dest && reg < inst.dest + 2) + return true; + return false; +} + +IRUsage IRNextGPRUsage(int gpr, const IRSituation &info) { + // Exclude any "special" regs from this logic for now. + if (gpr >= 32) + return IRUsage::UNKNOWN; + + int count = std::min(info.numInstructions - info.currentIndex, info.lookaheadCount); + for (int i = 0; i < count; ++i) { + const IRInst inst = info.instructions[info.currentIndex + i]; + if (IRReadsFromGPR(inst, gpr)) + return IRUsage::READ; + // We say WRITE when the current instruction writes. It's not useful for spilling. + if (IRDestGPR(inst) == gpr) + return i == 0 ? IRUsage::WRITE : IRUsage::CLOBBERED; + } + + return IRUsage::UNUSED; +} + +IRUsage IRNextFPRUsage(int fpr, const IRSituation &info) { + // Let's only pay attention to standard FP regs and temps. + // See MIPS.h for these offsets. + if (fpr < 0 || (fpr >= 160 && fpr < 192) || fpr >= 208) + return IRUsage::UNKNOWN; + + int count = std::min(info.numInstructions - info.currentIndex, info.lookaheadCount); + for (int i = 0; i < count; ++i) { + const IRInst inst = info.instructions[info.currentIndex + i]; + + if (IRReadsFromFPR(inst, fpr)) + return IRUsage::READ; + // We say WRITE when the current instruction writes. It's not useful for spilling. + if (IRWritesToFPR(inst, fpr)) { + return i == 0 ? IRUsage::WRITE : IRUsage::CLOBBERED; + } + } + + return IRUsage::UNUSED; +} diff --git a/Core/MIPS/IR/IRAnalysis.h b/Core/MIPS/IR/IRAnalysis.h new file mode 100644 index 0000000000..2b5ff598b4 --- /dev/null +++ b/Core/MIPS/IR/IRAnalysis.h @@ -0,0 +1,44 @@ +// Copyright (c) 2016- PPSSPP Project. + +// 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, version 2.0 or later versions. + +// 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 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#pragma once + +#include "Core/MIPS/IR/IRInst.h" + +bool IRReadsFromFPR(const IRInst &inst, int reg, bool directly = false); +bool IRReadsFromGPR(const IRInst &inst, int reg, bool directly = false); +bool IRWritesToGPR(const IRInst &inst, int reg); +bool IRWritesToFPR(const IRInst &inst, int reg); +int IRDestGPR(const IRInst &inst); + +struct IRSituation { + int lookaheadCount; + int currentIndex; + const IRInst *instructions; + int numInstructions; +}; + +enum class IRUsage { + UNKNOWN, + UNUSED, + READ, + WRITE, + CLOBBERED, +}; + +IRUsage IRNextGPRUsage(int gpr, const IRSituation &info); +IRUsage IRNextFPRUsage(int fpr, const IRSituation &info); diff --git a/Core/MIPS/IR/IRCompBranch.cpp b/Core/MIPS/IR/IRCompBranch.cpp index c21e2d6b31..49f0f7d5eb 100644 --- a/Core/MIPS/IR/IRCompBranch.cpp +++ b/Core/MIPS/IR/IRCompBranch.cpp @@ -455,6 +455,7 @@ void IRFrontend::Comp_Syscall(MIPSOpcode op) { } void IRFrontend::Comp_Break(MIPSOpcode op) { + ir.Write(IROp::SetPCConst, 0, ir.AddConstant(GetCompilerPC())); ir.Write(IROp::Break); js.compiling = false; } diff --git a/Core/MIPS/IR/IRCompFPU.cpp b/Core/MIPS/IR/IRCompFPU.cpp index cf6b4faa75..b0edeea0cf 100644 --- a/Core/MIPS/IR/IRCompFPU.cpp +++ b/Core/MIPS/IR/IRCompFPU.cpp @@ -152,7 +152,8 @@ void IRFrontend::Comp_FPU2op(MIPSOpcode op) { ir.Write(IROp::FAbs, fd, fs); break; case 6: //F(fd) = F(fs); break; //mov - ir.Write(IROp::FMov, fd, fs); + if (fd != fs) + ir.Write(IROp::FMov, fd, fs); break; case 7: //F(fd) = -F(fs); break; //neg ir.Write(IROp::FNeg, fd, fs); @@ -203,7 +204,8 @@ void IRFrontend::Comp_mxc1(MIPSOpcode op) { return; } if (fs == 31) { - DISABLE; // TODO: Add a new op + // This needs to insert fpcond. + ir.Write(IROp::FpCtrlToReg, rt); } else if (fs == 0) { ir.Write(IROp::SetConst, rt, ir.AddConstant(MIPSState::FCR0_VALUE)); } else { @@ -219,7 +221,10 @@ void IRFrontend::Comp_mxc1(MIPSOpcode op) { case 6: //ctc1 if (fs == 31) { // Set rounding mode - DISABLE; + RestoreRoundingMode(); + ir.Write(IROp::FpCtrlFromReg, 0, rt); + UpdateRoundingMode(); + ApplyRoundingMode(); } else { Comp_Generic(op); } diff --git a/Core/MIPS/IR/IRCompLoadStore.cpp b/Core/MIPS/IR/IRCompLoadStore.cpp index cb1892d3d2..1a4aac5069 100644 --- a/Core/MIPS/IR/IRCompLoadStore.cpp +++ b/Core/MIPS/IR/IRCompLoadStore.cpp @@ -105,6 +105,30 @@ namespace MIPSComp { } } + void IRFrontend::Comp_StoreSync(MIPSOpcode op) { + CONDITIONAL_DISABLE(LSU); + + int offset = _IMM16; + MIPSGPReg rt = _RT; + MIPSGPReg rs = _RS; + // Note: still does something even if loading to zero. + + CheckMemoryBreakpoint(rs, offset); + + switch (op >> 26) { + case 48: // ll + ir.Write(IROp::Load32Linked, rt, rs, ir.AddConstant(offset)); + break; + + case 56: // sc + ir.Write(IROp::Store32Conditional, rt, rs, ir.AddConstant(offset)); + break; + + default: + INVALIDOP; + } + } + void IRFrontend::Comp_Cache(MIPSOpcode op) { CONDITIONAL_DISABLE(LSU); diff --git a/Core/MIPS/IR/IRCompVFPU.cpp b/Core/MIPS/IR/IRCompVFPU.cpp index 70128b4c62..c902951d96 100644 --- a/Core/MIPS/IR/IRCompVFPU.cpp +++ b/Core/MIPS/IR/IRCompVFPU.cpp @@ -238,7 +238,7 @@ namespace MIPSComp { } else { if (negate) ir.Write(IROp::FNeg, vregs[i], origV[regnum]); - else + else if (vregs[i] != origV[regnum]) ir.Write(IROp::FMov, vregs[i], origV[regnum]); } } else { @@ -855,7 +855,8 @@ namespace MIPSComp { switch (optype) { case 0: // d[i] = s[i]; break; //vmov // Probably for swizzle. - ir.Write(IROp::FMov, tempregs[i], sregs[i]); + if (tempregs[i] != sregs[i]) + ir.Write(IROp::FMov, tempregs[i], sregs[i]); break; case 1: // d[i] = fabsf(s[i]); break; //vabs ir.Write(IROp::FAbs, tempregs[i], sregs[i]); @@ -929,37 +930,17 @@ namespace MIPSComp { VectorSize sz = GetVecSize(op); int n = GetNumVectorElements(sz); - int imm = (op >> 16) & 0x1f; - const float mult = 1.0f / (float)(1UL << imm); + uint8_t imm = (op >> 16) & 0x1f; u8 sregs[4], dregs[4]; GetVectorRegsPrefixS(sregs, sz, _VS); GetVectorRegsPrefixD(dregs, sz, _VD); - u8 tempregs[4]; - for (int i = 0; i < n; ++i) { - if (!IsOverlapSafe(dregs[i], n, sregs)) { - tempregs[i] = IRVTEMP_PFX_T + i; // Need IRVTEMP_0 for the scaling factor - } else { - tempregs[i] = dregs[i]; - } - } - if (mult != 1.0f) - ir.Write(IROp::SetConstF, IRVTEMP_0, ir.AddConstantFloat(mult)); - // TODO: Use the SCVTF with builtin scaling where possible. for (int i = 0; i < n; i++) { - ir.Write(IROp::FCvtSW, tempregs[i], sregs[i]); - } - if (mult != 1.0f) { - for (int i = 0; i < n; i++) { - ir.Write(IROp::FMul, tempregs[i], tempregs[i], IRVTEMP_0); - } - } - - for (int i = 0; i < n; ++i) { - if (dregs[i] != tempregs[i]) { - ir.Write(IROp::FMov, dregs[i], tempregs[i]); - } + if (imm == 0) + ir.Write(IROp::FCvtSW, dregs[i], sregs[i]); + else + ir.Write(IROp::FCvtScaledSW, dregs[i], sregs[i], imm); } ApplyPrefixD(dregs, sz); } @@ -986,7 +967,47 @@ namespace MIPSComp { // d[N] = int(S[N] * mult) // Note: saturates on overflow. - DISABLE; + VectorSize sz = GetVecSize(op); + int n = GetNumVectorElements(sz); + + uint8_t imm = (op >> 16) & 0x1f; + + u8 sregs[4], dregs[4]; + GetVectorRegsPrefixS(sregs, sz, _VS); + GetVectorRegsPrefixD(dregs, sz, _VD); + + // Same values as FCR31. + uint8_t rmode = (op >> 21) & 3; + if (((op >> 21) & 0x1C) != 0x10) + INVALIDOP; + + if (imm != 0) { + for (int i = 0; i < n; i++) + ir.Write(IROp::FCvtScaledWS, dregs[i], sregs[i], imm | (rmode << 6)); + } else { + for (int i = 0; i < n; i++) { + switch (rmode) { + case 0: // vf2in + ir.Write(IROp::FRound, dregs[i], sregs[i]); + break; + + case 1: // vf2iz + ir.Write(IROp::FTrunc, dregs[i], sregs[i]); + break; + + case 2: // vf2iu + ir.Write(IROp::FCeil, dregs[i], sregs[i]); + break; + + case 3: // vf2id + ir.Write(IROp::FFloor, dregs[i], sregs[i]); + break; + + default: + INVALIDOP; + } + } + } } void IRFrontend::Comp_Mftv(MIPSOpcode op) { @@ -1151,7 +1172,8 @@ namespace MIPSComp { } for (int a = 0; a < n; a++) { for (int b = 0; b < n; b++) { - ir.Write(IROp::FMov, dregs[a * 4 + b], sregs[a * 4 + b]); + if (dregs[a * 4 + b] != sregs[a * 4 + b]) + ir.Write(IROp::FMov, dregs[a * 4 + b], sregs[a * 4 + b]); } } } diff --git a/Core/MIPS/IR/IRFrontend.h b/Core/MIPS/IR/IRFrontend.h index 0f9a1ab418..b53e053d73 100644 --- a/Core/MIPS/IR/IRFrontend.h +++ b/Core/MIPS/IR/IRFrontend.h @@ -18,6 +18,7 @@ public: // Ops void Comp_ITypeMem(MIPSOpcode op) override; + void Comp_StoreSync(MIPSOpcode op) override; void Comp_Cache(MIPSOpcode op) override; void Comp_RelBranch(MIPSOpcode op) override; diff --git a/Core/MIPS/IR/IRInst.cpp b/Core/MIPS/IR/IRInst.cpp index 6d3e6f9572..d0e0fe1d74 100644 --- a/Core/MIPS/IR/IRInst.cpp +++ b/Core/MIPS/IR/IRInst.cpp @@ -1,6 +1,5 @@ #include "Common/CommonFuncs.h" #include "Core/MIPS/IR/IRInst.h" -#include "Core/MIPS/IR/IRPassSimplify.h" #include "Core/MIPS/MIPSDebugInterface.h" // Legend @@ -73,6 +72,7 @@ static const IRMeta irMeta[] = { { IROp::Load32, "Load32", "GGC" }, { IROp::Load32Left, "Load32Left", "GGC", IRFLAG_SRC3DST }, { IROp::Load32Right, "Load32Right", "GGC", IRFLAG_SRC3DST }, + { IROp::Load32Linked, "Load32Linked", "GGC" }, { IROp::LoadFloat, "LoadFloat", "FGC" }, { IROp::LoadVec4, "LoadVec4", "VGC" }, { IROp::Store8, "Store8", "GGC", IRFLAG_SRC3 }, @@ -80,6 +80,7 @@ static const IRMeta irMeta[] = { { IROp::Store32, "Store32", "GGC", IRFLAG_SRC3 }, { IROp::Store32Left, "Store32Left", "GGC", IRFLAG_SRC3 }, { IROp::Store32Right, "Store32Right", "GGC", IRFLAG_SRC3 }, + { IROp::Store32Conditional, "Store32Conditional", "GGC", IRFLAG_SRC3DST }, { IROp::StoreFloat, "StoreFloat", "FGC", IRFLAG_SRC3 }, { IROp::StoreVec4, "StoreVec4", "VGC", IRFLAG_SRC3 }, { IROp::FAdd, "FAdd", "FFF" }, @@ -105,6 +106,8 @@ static const IRMeta irMeta[] = { { IROp::FFloor, "FFloor", "FF" }, { IROp::FCvtWS, "FCvtWS", "FF" }, { IROp::FCvtSW, "FCvtSW", "FF" }, + { IROp::FCvtScaledWS, "FCvtScaledWS", "FFI" }, + { IROp::FCvtScaledSW, "FCvtScaledSW", "FFI" }, { IROp::FCmp, "FCmp", "mFF" }, { IROp::FSat0_1, "FSat(0 - 1)", "FF" }, { IROp::FSatMinus1_1, "FSat(-1 - 1)", "FF" }, @@ -112,11 +115,13 @@ static const IRMeta irMeta[] = { { IROp::FMovToGPR, "FMovToGPR", "GF" }, { IROp::ZeroFpCond, "ZeroFpCond", "" }, { IROp::FpCondToReg, "FpCondToReg", "G" }, + { IROp::FpCtrlFromReg, "FpCtrlFromReg", "_G" }, + { IROp::FpCtrlToReg, "FpCtrlToReg", "G" }, { IROp::VfpuCtrlToReg, "VfpuCtrlToReg", "GI" }, { IROp::SetCtrlVFPU, "SetCtrlVFPU", "TC" }, { IROp::SetCtrlVFPUReg, "SetCtrlVFPUReg", "TG" }, { IROp::SetCtrlVFPUFReg, "SetCtrlVFPUFReg", "TF" }, - { IROp::FCmovVfpuCC, "FCmovVfpuCC", "FFI" }, + { IROp::FCmovVfpuCC, "FCmovVfpuCC", "FFI", IRFLAG_SRC3DST }, { IROp::FCmpVfpuBit, "FCmpVfpuBit", "IFF" }, { IROp::FCmpVfpuAggregate, "FCmpVfpuAggregate", "I" }, { IROp::Vec4Init, "Vec4Init", "Vv" }, @@ -263,21 +268,21 @@ void DisassembleParam(char *buf, int bufSize, u8 param, char type, u32 constant) break; case 'F': if (param >= 32) { - snprintf(buf, bufSize, "v%d", param - 32); + snprintf(buf, bufSize, "vf%d", param - 32); } else { snprintf(buf, bufSize, "f%d", param); } break; case 'V': if (param >= 32) { - snprintf(buf, bufSize, "v%d..v%d", param - 32, param - 32 + 3); + snprintf(buf, bufSize, "vf%d..vf%d", param - 32, param - 32 + 3); } else { snprintf(buf, bufSize, "f%d..f%d", param, param + 3); } break; case '2': if (param >= 32) { - snprintf(buf, bufSize, "v%d,v%d", param - 32, param - 32 + 1); + snprintf(buf, bufSize, "vf%d,vf%d", param - 32, param - 32 + 1); } else { snprintf(buf, bufSize, "f%d,f%d", param, param + 1); } diff --git a/Core/MIPS/IR/IRInst.h b/Core/MIPS/IR/IRInst.h index 8c7b67b00a..795dc75233 100644 --- a/Core/MIPS/IR/IRInst.h +++ b/Core/MIPS/IR/IRInst.h @@ -92,6 +92,7 @@ enum class IROp : u8 { Load32, Load32Left, Load32Right, + Load32Linked, LoadFloat, LoadVec4, @@ -100,6 +101,7 @@ enum class IROp : u8 { Store32, Store32Left, Store32Right, + Store32Conditional, StoreFloat, StoreVec4, @@ -127,6 +129,8 @@ enum class IROp : u8 { FCvtWS, FCvtSW, + FCvtScaledWS, + FCvtScaledSW, FMovFromGPR, FMovToGPR, @@ -135,6 +139,8 @@ enum class IROp : u8 { FSatMinus1_1, FpCondToReg, + FpCtrlFromReg, + FpCtrlToReg, VfpuCtrlToReg, ZeroFpCond, @@ -283,7 +289,9 @@ enum IRFpCompareMode { LessEqualUnordered, // ule, ngt (less equal, unordered) }; -enum { +typedef u8 IRReg; + +enum : IRReg { IRTEMP_0 = 192, IRTEMP_1, IRTEMP_2, @@ -300,9 +308,6 @@ enum { IRVTEMP_PFX_D = 232 - 32, IRVTEMP_0 = 236 - 32, - // 16 float temps for vector S and T prefixes and things like that. - // IRVTEMP_0 = 208 - 64, // -64 to be relative to v[0] - // Hacky way to get to other state IRREG_VFPU_CTRL_BASE = 208, IRREG_VFPU_CC = 211, @@ -310,6 +315,7 @@ enum { IRREG_HI = 243, IRREG_FCR31 = 244, IRREG_FPCOND = 245, + IRREG_LLBIT = 250, }; enum IRFlags { @@ -332,11 +338,11 @@ struct IRMeta { struct IRInst { IROp op; union { - u8 dest; - u8 src3; + IRReg dest; + IRReg src3; }; - u8 src1; - u8 src2; + IRReg src1; + IRReg src2; u32 constant; }; diff --git a/Core/MIPS/IR/IRInterpreter.cpp b/Core/MIPS/IR/IRInterpreter.cpp index 5b858bc1b3..aee5c500e8 100644 --- a/Core/MIPS/IR/IRInterpreter.cpp +++ b/Core/MIPS/IR/IRInterpreter.cpp @@ -218,6 +218,11 @@ u32 IRInterpret(MIPSState *mips, const IRInst *inst, int count) { mips->r[inst->dest] = (mips->r[inst->dest] & destMask) | (mem >> shift); break; } + case IROp::Load32Linked: + if (inst->dest != MIPS_REG_ZERO) + mips->r[inst->dest] = Memory::ReadUnchecked_U32(mips->r[inst->src1] + inst->constant); + mips->llBit = 1; + break; case IROp::LoadFloat: mips->f[inst->dest] = Memory::ReadUnchecked_Float(mips->r[inst->src1] + inst->constant); break; @@ -251,6 +256,16 @@ u32 IRInterpret(MIPSState *mips, const IRInst *inst, int count) { Memory::WriteUnchecked_U32(result, addr & 0xfffffffc); break; } + case IROp::Store32Conditional: + if (mips->llBit) { + Memory::WriteUnchecked_U32(mips->r[inst->src3], mips->r[inst->src1] + inst->constant); + if (inst->dest != MIPS_REG_ZERO) { + mips->r[inst->dest] = 1; + } + } else if (inst->dest != MIPS_REG_ZERO) { + mips->r[inst->dest] = 0; + } + break; case IROp::StoreFloat: Memory::WriteUnchecked_Float(mips->f[inst->src3], mips->r[inst->src1] + inst->constant); break; @@ -768,10 +783,28 @@ u32 IRInterpret(MIPSState *mips, const IRInst *inst, int count) { mips->f[inst->dest] = mips->f[inst->src1] / mips->f[inst->src2]; break; case IROp::FMin: - mips->f[inst->dest] = std::min(mips->f[inst->src1], mips->f[inst->src2]); + if (my_isnan(mips->f[inst->src1]) || my_isnan(mips->f[inst->src2])) { + // See interpreter for this logic: this is for vmin, we're comparing mantissa+exp. + if (mips->fi[inst->src1] < 0 && mips->fi[inst->src2] < 0) { + mips->fi[inst->dest] = std::max(mips->fi[inst->src1], mips->fi[inst->src2]); + } else { + mips->fi[inst->dest] = std::min(mips->fi[inst->src1], mips->fi[inst->src2]); + } + } else { + mips->f[inst->dest] = std::min(mips->f[inst->src1], mips->f[inst->src2]); + } break; case IROp::FMax: - mips->f[inst->dest] = std::max(mips->f[inst->src1], mips->f[inst->src2]); + if (my_isnan(mips->f[inst->src1]) || my_isnan(mips->f[inst->src2])) { + // See interpreter for this logic: this is for vmax, we're comparing mantissa+exp. + if (mips->fi[inst->src1] < 0 && mips->fi[inst->src2] < 0) { + mips->fi[inst->dest] = std::min(mips->fi[inst->src1], mips->fi[inst->src2]); + } else { + mips->fi[inst->dest] = std::max(mips->fi[inst->src1], mips->fi[inst->src2]); + } + } else { + mips->f[inst->dest] = std::max(mips->f[inst->src1], mips->f[inst->src2]); + } break; case IROp::FMov: @@ -811,6 +844,17 @@ u32 IRInterpret(MIPSState *mips, const IRInst *inst, int count) { case IROp::FpCondToReg: mips->r[inst->dest] = mips->fpcond; break; + case IROp::FpCtrlFromReg: + mips->fcr31 = mips->r[inst->src1] & 0x0181FFFF; + // Extract the new fpcond value. + // TODO: Is it really helping us to keep it separate? + mips->fpcond = (mips->fcr31 >> 23) & 1; + break; + case IROp::FpCtrlToReg: + // Update the fpcond bit first. + mips->fcr31 = (mips->fcr31 & ~(1 << 23)) | ((mips->fpcond & 1) << 23); + mips->r[inst->dest] = mips->fcr31; + break; case IROp::VfpuCtrlToReg: mips->r[inst->dest] = mips->vfpuCtrl[inst->src1]; break; @@ -821,7 +865,7 @@ u32 IRInterpret(MIPSState *mips, const IRInst *inst, int count) { mips->fi[inst->dest] = my_isinf(value) && value < 0.0f ? -2147483648LL : 2147483647LL; break; } else { - mips->fs[inst->dest] = (int)floorf(value + 0.5f); + mips->fs[inst->dest] = (int)round_ieee_754(value); } break; } @@ -918,6 +962,35 @@ u32 IRInterpret(MIPSState *mips, const IRInst *inst, int count) { } break; //cvt.w.s } + case IROp::FCvtScaledSW: + mips->f[inst->dest] = (float)mips->fs[inst->src1] * (1.0f / (1UL << (inst->src2 & 0x1F))); + break; + case IROp::FCvtScaledWS: + { + float src = mips->f[inst->src1]; + if (my_isnan(src)) { + // TODO: True for negatives too? + mips->fs[inst->dest] = 2147483647L; + break; + } + + float mult = (float)(1UL << (inst->src2 & 0x1F)); + double sv = src * mult; // (float)0x7fffffff == (float)0x80000000 + // Cap/floor it to 0x7fffffff / 0x80000000 + if (sv > (double)0x7fffffff) { + mips->fs[inst->dest] = 0x7fffffff; + } else if (sv <= (double)(int)0x80000000) { + mips->fs[inst->dest] = 0x80000000; + } else { + switch (inst->src2 >> 6) { + case 0: mips->fs[inst->dest] = (int)round_ieee_754(sv); break; + case 1: mips->fs[inst->dest] = src >= 0 ? (int)floor(sv) : (int)ceil(sv); break; + case 2: mips->fs[inst->dest] = (int)ceil(sv); break; + case 3: mips->fs[inst->dest] = (int)floor(sv); break; + } + } + break; + } case IROp::ZeroFpCond: mips->fpcond = 0; diff --git a/Core/MIPS/IR/IRJit.cpp b/Core/MIPS/IR/IRJit.cpp index 6a82ec1f7c..039b8ee197 100644 --- a/Core/MIPS/IR/IRJit.cpp +++ b/Core/MIPS/IR/IRJit.cpp @@ -36,7 +36,6 @@ #include "Core/MIPS/MIPSTables.h" #include "Core/MIPS/IR/IRRegCache.h" #include "Core/MIPS/IR/IRJit.h" -#include "Core/MIPS/IR/IRPassSimplify.h" #include "Core/MIPS/IR/IRInterpreter.h" #include "Core/MIPS/JitCommon/JitCommon.h" #include "Core/Reporting.h" @@ -85,7 +84,8 @@ void IRJit::Compile(u32 em_address) { if (block_num != -1) { IRBlock *b = blocks_.GetBlock(block_num); // Okay, let's link and finalize the block now. - b->Finalize(block_num); + int cookie = b->GetTargetOffset() < 0 ? block_num : b->GetTargetOffset(); + b->Finalize(cookie); if (b->IsValid()) { // Success, we're done. return; @@ -128,13 +128,13 @@ bool IRJit::CompileBlock(u32 em_address, std::vector &instructions, u32 b->SetOriginalSize(mipsBytes); if (preload) { // Hash, then only update page stats, don't link yet. - b->UpdateHash(); - blocks_.FinalizeBlock(block_num, true); - } else { - // Overwrites the first instruction, and also updates stats. // TODO: Should we always hash? Then we can reuse blocks. - blocks_.FinalizeBlock(block_num); + b->UpdateHash(); } + if (!CompileTargetBlock(b, block_num, preload)) + return false; + // Overwrites the first instruction, and also updates stats. + blocks_.FinalizeBlock(block_num, preload); return true; } @@ -262,14 +262,10 @@ void IRJit::UnlinkBlock(u8 *checkedEntry, u32 originalAddress) { Crash(); } -bool IRJit::ReplaceJalTo(u32 dest) { - Crash(); - return false; -} - void IRBlockCache::Clear() { for (int i = 0; i < (int)blocks_.size(); ++i) { - blocks_[i].Destroy(i); + int cookie = blocks_[i].GetTargetOffset() < 0 ? i : blocks_[i].GetTargetOffset(); + blocks_[i].Destroy(cookie); } blocks_.clear(); byPage_.clear(); @@ -288,7 +284,8 @@ void IRBlockCache::InvalidateICache(u32 address, u32 length) { for (int i : blocksInPage) { if (blocks_[i].OverlapsRange(address, length)) { // Not removing from the page, hopefully doesn't build up with small recompiles. - blocks_[i].Destroy(i); + int cookie = blocks_[i].GetTargetOffset() < 0 ? i : blocks_[i].GetTargetOffset(); + blocks_[i].Destroy(cookie); } } } @@ -296,7 +293,8 @@ void IRBlockCache::InvalidateICache(u32 address, u32 length) { void IRBlockCache::FinalizeBlock(int i, bool preload) { if (!preload) { - blocks_[i].Finalize(i); + int cookie = blocks_[i].GetTargetOffset() < 0 ? i : blocks_[i].GetTargetOffset(); + blocks_[i].Finalize(cookie); } u32 startAddr, size; @@ -336,13 +334,30 @@ int IRBlockCache::FindPreloadBlock(u32 em_address) { return -1; } +int IRBlockCache::FindByCookie(int cookie) { + if (blocks_.empty()) + return -1; + // TODO: Maybe a flag to determine target offset mode? + if (blocks_[0].GetTargetOffset() < 0) + return cookie; + + for (int i = 0; i < GetNumBlocks(); ++i) { + int offset = blocks_[i].GetTargetOffset(); + if (offset == cookie) + return i; + } + + return -1; +} + std::vector IRBlockCache::SaveAndClearEmuHackOps() { std::vector result; result.resize(blocks_.size()); for (int number = 0; number < (int)blocks_.size(); ++number) { IRBlock &b = blocks_[number]; - if (b.IsValid() && b.RestoreOriginalFirstOp(number)) { + int cookie = b.GetTargetOffset() < 0 ? number : b.GetTargetOffset(); + if (b.IsValid() && b.RestoreOriginalFirstOp(cookie)) { result[number] = number; } else { result[number] = 0; @@ -362,7 +377,8 @@ void IRBlockCache::RestoreSavedEmuHackOps(std::vector saved) { IRBlock &b = blocks_[number]; // Only if we restored it, write it back. if (b.IsValid() && saved[number] != 0 && b.HasOriginalFirstOp()) { - b.Finalize(number); + int cookie = b.GetTargetOffset() < 0 ? number : b.GetTargetOffset(); + b.Finalize(cookie); } } } @@ -446,8 +462,8 @@ bool IRBlock::HasOriginalFirstOp() const { return Memory::ReadUnchecked_U32(origAddr_) == origFirstOpcode_.encoding; } -bool IRBlock::RestoreOriginalFirstOp(int number) { - const u32 emuhack = MIPS_EMUHACK_OPCODE | number; +bool IRBlock::RestoreOriginalFirstOp(int cookie) { + const u32 emuhack = MIPS_EMUHACK_OPCODE | cookie; if (Memory::ReadUnchecked_U32(origAddr_) == emuhack) { Memory::Write_Opcode_JIT(origAddr_, origFirstOpcode_); return true; @@ -455,19 +471,19 @@ bool IRBlock::RestoreOriginalFirstOp(int number) { return false; } -void IRBlock::Finalize(int number) { +void IRBlock::Finalize(int cookie) { // Check it wasn't invalidated, in case this is after preload. // TODO: Allow reusing blocks when the code matches hash_ again, instead. if (origAddr_) { origFirstOpcode_ = Memory::Read_Opcode_JIT(origAddr_); - MIPSOpcode opcode = MIPSOpcode(MIPS_EMUHACK_OPCODE | number); + MIPSOpcode opcode = MIPSOpcode(MIPS_EMUHACK_OPCODE | cookie); Memory::Write_Opcode_JIT(origAddr_, opcode); } } -void IRBlock::Destroy(int number) { +void IRBlock::Destroy(int cookie) { if (origAddr_) { - MIPSOpcode opcode = MIPSOpcode(MIPS_EMUHACK_OPCODE | number); + MIPSOpcode opcode = MIPSOpcode(MIPS_EMUHACK_OPCODE | cookie); if (Memory::ReadUnchecked_U32(origAddr_) == opcode.encoding) Memory::Write_Opcode_JIT(origAddr_, origFirstOpcode_); @@ -501,7 +517,7 @@ bool IRBlock::OverlapsRange(u32 addr, u32 size) const { } MIPSOpcode IRJit::GetOriginalOp(MIPSOpcode op) { - IRBlock *b = blocks_.GetBlock(op.encoding & 0xFFFFFF); + IRBlock *b = blocks_.GetBlock(blocks_.FindByCookie(op.encoding & 0xFFFFFF)); if (b) { return b->GetOriginalFirstOp(); } diff --git a/Core/MIPS/IR/IRJit.h b/Core/MIPS/IR/IRJit.h index 973ddf524e..35387e7e44 100644 --- a/Core/MIPS/IR/IRJit.h +++ b/Core/MIPS/IR/IRJit.h @@ -38,15 +38,16 @@ namespace MIPSComp { // TODO : Use arena allocators. For now let's just malloc. class IRBlock { public: - IRBlock() : instr_(nullptr), numInstructions_(0), origAddr_(0), origSize_(0) {} - IRBlock(u32 emAddr) : instr_(nullptr), numInstructions_(0), origAddr_(emAddr), origSize_(0) {} + IRBlock() {} + IRBlock(u32 emAddr) : origAddr_(emAddr) {} IRBlock(IRBlock &&b) { instr_ = b.instr_; - numInstructions_ = b.numInstructions_; + hash_ = b.hash_; origAddr_ = b.origAddr_; origSize_ = b.origSize_; origFirstOpcode_ = b.origFirstOpcode_; - hash_ = b.hash_; + targetOffset_ = b.targetOffset_; + numInstructions_ = b.numInstructions_; b.instr_ = nullptr; } @@ -71,6 +72,12 @@ public: void SetOriginalSize(u32 size) { origSize_ = size; } + void SetTargetOffset(int offset) { + targetOffset_ = offset; + } + int GetTargetOffset() const { + return targetOffset_; + } void UpdateHash() { hash_ = CalculateHash(); } @@ -90,12 +97,13 @@ public: private: u64 CalculateHash() const; - IRInst *instr_; - u16 numInstructions_; - u32 origAddr_; - u32 origSize_; + IRInst *instr_ = nullptr; u64 hash_ = 0; + u32 origAddr_ = 0; + u32 origSize_ = 0; MIPSOpcode origFirstOpcode_ = MIPSOpcode(0x68FFFFFF); + int targetOffset_ = -1; + u16 numInstructions_ = 0; }; class IRBlockCache : public JitBlockCacheDebugInterface { @@ -118,6 +126,7 @@ public: } int FindPreloadBlock(u32 em_address); + int FindByCookie(int cookie); std::vector SaveAndClearEmuHackOps(); void RestoreSavedEmuHackOps(std::vector saved); @@ -170,9 +179,9 @@ public: void LinkBlock(u8 *exitPoint, const u8 *checkedEntry) override; void UnlinkBlock(u8 *checkedEntry, u32 originalAddress) override; -private: - bool CompileBlock(u32 em_address, std::vector &instructions, u32 &mipsBytes, bool preload); - bool ReplaceJalTo(u32 dest); +protected: + virtual bool CompileBlock(u32 em_address, std::vector &instructions, u32 &mipsBytes, bool preload); + virtual bool CompileTargetBlock(IRBlock *block, int block_num, bool preload) { return true; } JitOptions jo; @@ -187,4 +196,3 @@ private: }; } // namespace MIPSComp - diff --git a/Core/MIPS/IR/IRPassSimplify.cpp b/Core/MIPS/IR/IRPassSimplify.cpp index 5725454841..c595d26ef9 100644 --- a/Core/MIPS/IR/IRPassSimplify.cpp +++ b/Core/MIPS/IR/IRPassSimplify.cpp @@ -6,6 +6,7 @@ #include "Common/Data/Convert/SmallDataConvert.h" #include "Common/Log.h" #include "Core/Config.h" +#include "Core/MIPS/IR/IRAnalysis.h" #include "Core/MIPS/IR/IRInterpreter.h" #include "Core/MIPS/IR/IRPassSimplify.h" #include "Core/MIPS/IR/IRRegCache.h" @@ -128,7 +129,9 @@ bool OptimizeFPMoves(const IRWriter &in, IRWriter &out, const IROptions &opts) { if (prev.op == IROp::FMovToGPR && prev.dest == inst.src1) { inst.op = IROp::FMov; inst.src1 = prev.src1; - out.Write(inst); + // Skip it entirely if it's just a copy to and back. + if (inst.dest != inst.src1) + out.Write(inst); } else { out.Write(inst); } @@ -228,7 +231,7 @@ bool RemoveLoadStoreLeftRight(const IRWriter &in, IRWriter &out, const IROptions }; auto combineOpposite = [&](IROp matchOp, int matchOff, IROp replaceOp, int replaceOff) { - if (!opts.unalignedLoadStore || i + 1 >= n) + if (i + 1 >= n) return false; const IRInst &next = nextOp(); if (next.op != matchOp || next.dest != inst.dest || next.src1 != inst.src1) @@ -236,8 +239,40 @@ bool RemoveLoadStoreLeftRight(const IRWriter &in, IRWriter &out, const IROptions if (inst.constant + matchOff != next.constant) return false; - // Write out one unaligned op. - out.Write(replaceOp, inst.dest, inst.src1, out.AddConstant(inst.constant + replaceOff)); + if (opts.unalignedLoadStore) { + // Write out one unaligned op. + out.Write(replaceOp, inst.dest, inst.src1, out.AddConstant(inst.constant + replaceOff)); + } else if (replaceOp == IROp::Load32) { + // We can still combine to a simpler set of two loads. + // We start by isolating the address and shift amount. + + // IRTEMP_LR_ADDR = rs + imm + out.Write(IROp::AddConst, IRTEMP_LR_ADDR, inst.src1, out.AddConstant(inst.constant + replaceOff)); + // IRTEMP_LR_SHIFT = (addr & 3) * 8 + out.Write(IROp::AndConst, IRTEMP_LR_SHIFT, IRTEMP_LR_ADDR, out.AddConstant(3)); + out.Write(IROp::ShlImm, IRTEMP_LR_SHIFT, IRTEMP_LR_SHIFT, 3); + // IRTEMP_LR_ADDR = addr & 0xfffffffc + out.Write(IROp::AndConst, IRTEMP_LR_ADDR, IRTEMP_LR_ADDR, out.AddConstant(0xFFFFFFFC)); + // IRTEMP_LR_VALUE = low_word, dest = high_word + out.Write(IROp::Load32, inst.dest, IRTEMP_LR_ADDR, out.AddConstant(0)); + out.Write(IROp::Load32, IRTEMP_LR_VALUE, IRTEMP_LR_ADDR, out.AddConstant(4)); + + // Now we just need to adjust and combine dest and IRTEMP_LR_VALUE. + // inst.dest >>= shift (putting its bits in the right spot.) + out.Write(IROp::Shr, inst.dest, inst.dest, IRTEMP_LR_SHIFT); + // We can't shift by 32, so we compromise by shifting twice. + out.Write(IROp::ShlImm, IRTEMP_LR_VALUE, IRTEMP_LR_VALUE, 8); + // IRTEMP_LR_SHIFT = 24 - shift + out.Write(IROp::Neg, IRTEMP_LR_SHIFT, IRTEMP_LR_SHIFT); + out.Write(IROp::AddConst, IRTEMP_LR_SHIFT, IRTEMP_LR_SHIFT, out.AddConstant(24)); + // IRTEMP_LR_VALUE <<= (24 - shift) + out.Write(IROp::Shl, IRTEMP_LR_VALUE, IRTEMP_LR_VALUE, IRTEMP_LR_SHIFT); + + // At this point the values are aligned, and we just merge. + out.Write(IROp::Or, inst.dest, inst.dest, IRTEMP_LR_VALUE); + } else { + return false; + } // Skip the next one, replaced. i++; return true; @@ -572,6 +607,7 @@ bool PropagateConstants(const IRWriter &in, IRWriter &out, const IROptions &opts case IROp::Store32: case IROp::Store32Left: case IROp::Store32Right: + case IROp::Store32Conditional: if (gpr.IsImm(inst.src1) && inst.src1 != inst.dest) { gpr.MapIn(inst.dest); out.Write(inst.op, inst.dest, 0, out.AddConstant(gpr.GetImm(inst.src1) + inst.constant)); @@ -595,6 +631,7 @@ bool PropagateConstants(const IRWriter &in, IRWriter &out, const IROptions &opts case IROp::Load16: case IROp::Load16Ext: case IROp::Load32: + case IROp::Load32Linked: if (gpr.IsImm(inst.src1) && inst.src1 != inst.dest) { gpr.MapDirty(inst.dest); out.Write(inst.op, inst.dest, 0, out.AddConstant(gpr.GetImm(inst.src1) + inst.constant)); @@ -667,6 +704,8 @@ bool PropagateConstants(const IRWriter &in, IRWriter &out, const IROptions &opts case IROp::FCeil: case IROp::FFloor: case IROp::FCvtSW: + case IROp::FCvtScaledWS: + case IROp::FCvtScaledSW: case IROp::FSin: case IROp::FCos: case IROp::FSqrt: @@ -694,6 +733,13 @@ bool PropagateConstants(const IRWriter &in, IRWriter &out, const IROptions &opts out.Write(inst); } break; + case IROp::FpCtrlFromReg: + gpr.MapDirtyIn(IRREG_FCR31, inst.src1); + gpr.MapDirty(IRREG_FPCOND); + goto doDefault; + case IROp::FpCtrlToReg: + gpr.MapDirtyInIn(inst.dest, IRREG_FPCOND, IRREG_FCR31); + goto doDefault; case IROp::Vec4Init: case IROp::Vec4Mov: @@ -760,27 +806,6 @@ bool PropagateConstants(const IRWriter &in, IRWriter &out, const IROptions &opts return logBlocks; } -bool IRReadsFromGPR(const IRInst &inst, int reg, bool directly = false) { - const IRMeta *m = GetIRMeta(inst.op); - - if (m->types[1] == 'G' && inst.src1 == reg) { - return true; - } - if (m->types[2] == 'G' && inst.src2 == reg) { - return true; - } - if ((m->flags & (IRFLAG_SRC3 | IRFLAG_SRC3DST)) != 0 && m->types[0] == 'G' && inst.src3 == reg) { - return true; - } - if (!directly) { - if (inst.op == IROp::Interpret || inst.op == IROp::CallReplacement || inst.op == IROp::Syscall || inst.op == IROp::Break) - return true; - if (inst.op == IROp::Breakpoint || inst.op == IROp::MemoryCheck) - return true; - } - return false; -} - IRInst IRReplaceSrcGPR(const IRInst &inst, int fromReg, int toReg) { IRInst newInst = inst; const IRMeta *m = GetIRMeta(inst.op); @@ -797,15 +822,6 @@ IRInst IRReplaceSrcGPR(const IRInst &inst, int fromReg, int toReg) { return newInst; } -int IRDestGPR(const IRInst &inst) { - const IRMeta *m = GetIRMeta(inst.op); - - if ((m->flags & IRFLAG_SRC3) == 0 && m->types[0] == 'G') { - return inst.dest; - } - return -1; -} - IRInst IRReplaceDestGPR(const IRInst &inst, int fromReg, int toReg) { IRInst newInst = inst; const IRMeta *m = GetIRMeta(inst.op); @@ -1468,10 +1484,12 @@ bool ApplyMemoryValidation(const IRWriter &in, IRWriter &out, const IROptions &o break; case IROp::Load32: + case IROp::Load32Linked: case IROp::LoadFloat: case IROp::Store32: + case IROp::Store32Conditional: case IROp::StoreFloat: - addValidate(IROp::ValidateAddress32, inst, inst.op == IROp::Store32 || inst.op == IROp::StoreFloat); + addValidate(IROp::ValidateAddress32, inst, inst.op == IROp::Store32 || inst.op == IROp::Store32Conditional || inst.op == IROp::StoreFloat); break; case IROp::LoadVec4: diff --git a/Core/MIPS/IR/IRRegCache.cpp b/Core/MIPS/IR/IRRegCache.cpp index ca54392be1..d731c51a74 100644 --- a/Core/MIPS/IR/IRRegCache.cpp +++ b/Core/MIPS/IR/IRRegCache.cpp @@ -2,7 +2,7 @@ #include "Core/MIPS/IR/IRRegCache.h" #include "Core/MIPS/IR/IRInst.h" -void IRRegCache::Flush(int rd) { +void IRRegCache::Flush(IRReg rd) { if (rd == 0) { return; } @@ -12,7 +12,7 @@ void IRRegCache::Flush(int rd) { } } -void IRRegCache::Discard(int rd) { +void IRRegCache::Discard(IRReg rd) { if (rd == 0) { return; } @@ -31,33 +31,33 @@ void IRRegCache::FlushAll() { } } -void IRRegCache::MapIn(int rd) { +void IRRegCache::MapIn(IRReg rd) { Flush(rd); } -void IRRegCache::MapDirty(int rd) { +void IRRegCache::MapDirty(IRReg rd) { Discard(rd); } -void IRRegCache::MapInIn(int rs, int rt) { +void IRRegCache::MapInIn(IRReg rs, IRReg rt) { Flush(rs); Flush(rt); } -void IRRegCache::MapInInIn(int rd, int rs, int rt) { +void IRRegCache::MapInInIn(IRReg rd, IRReg rs, IRReg rt) { Flush(rd); Flush(rs); Flush(rt); } -void IRRegCache::MapDirtyIn(int rd, int rs) { +void IRRegCache::MapDirtyIn(IRReg rd, IRReg rs) { if (rs != rd) { Discard(rd); } Flush(rs); } -void IRRegCache::MapDirtyInIn(int rd, int rs, int rt) { +void IRRegCache::MapDirtyInIn(IRReg rd, IRReg rs, IRReg rt) { if (rs != rd && rt != rd) { Discard(rd); } diff --git a/Core/MIPS/IR/IRRegCache.h b/Core/MIPS/IR/IRRegCache.h index 9fcdab8b1e..80fad1d6ef 100644 --- a/Core/MIPS/IR/IRRegCache.h +++ b/Core/MIPS/IR/IRRegCache.h @@ -5,6 +5,7 @@ #include "Common/CommonTypes.h" #include "Core/MIPS/MIPS.h" +#include "Core/MIPS/IR/IRInst.h" enum { TOTAL_MAPPABLE_MIPSREGS = 256, @@ -22,26 +23,26 @@ class IRRegCache { public: IRRegCache(IRWriter *ir); - void SetImm(int r, u32 immVal) { + void SetImm(IRReg r, u32 immVal) { reg_[r].isImm = true; reg_[r].immVal = immVal; } - bool IsImm(int r) const { return reg_[r].isImm; } - u32 GetImm(int r) const { return reg_[r].immVal; } + bool IsImm(IRReg r) const { return reg_[r].isImm; } + u32 GetImm(IRReg r) const { return reg_[r].immVal; } void FlushAll(); - void MapDirty(int rd); - void MapIn(int rd); - void MapInIn(int rs, int rt); - void MapInInIn(int rd, int rs, int rt); - void MapDirtyIn(int rd, int rs); - void MapDirtyInIn(int rd, int rs, int rt); + void MapDirty(IRReg rd); + void MapIn(IRReg rd); + void MapInIn(IRReg rs, IRReg rt); + void MapInInIn(IRReg rd, IRReg rs, IRReg rt); + void MapDirtyIn(IRReg rd, IRReg rs); + void MapDirtyInIn(IRReg rd, IRReg rs, IRReg rt); private: - void Flush(int rd); - void Discard(int rd); + void Flush(IRReg rd); + void Discard(IRReg rd); RegIR reg_[TOTAL_MAPPABLE_MIPSREGS]; IRWriter *ir_; }; diff --git a/Core/MIPS/JitCommon/JitCommon.cpp b/Core/MIPS/JitCommon/JitCommon.cpp index 20a8fb6e74..1adfda298a 100644 --- a/Core/MIPS/JitCommon/JitCommon.cpp +++ b/Core/MIPS/JitCommon/JitCommon.cpp @@ -45,6 +45,8 @@ #include "../x86/Jit.h" #elif PPSSPP_ARCH(MIPS) #include "../MIPS/MipsJit.h" +#elif PPSSPP_ARCH(RISCV64) +#include "../RiscV/RiscVJit.h" #else #include "../fake/FakeJit.h" #endif @@ -108,6 +110,8 @@ namespace MIPSComp { return new MIPSComp::Jit(mipsState); #elif PPSSPP_ARCH(MIPS) return new MIPSComp::MipsJit(mipsState); +#elif PPSSPP_ARCH(RISCV64) + return new MIPSComp::RiscVJit(mipsState); #else return new MIPSComp::FakeJit(mipsState); #endif @@ -331,12 +335,12 @@ std::vector DisassembleRV64(const u8 *data, int size) { // Force align in case we're somehow unaligned. len = 2 - ((uintptr_t)data & 1); invalid_count += (int)len; - i +=(int) len; + i += (int)len; continue; } invalid_flush(); - riscv_disasm_inst(temp, sizeof(temp), rv64, i * 4, inst); + riscv_disasm_inst(temp, sizeof(temp), rv64, (uintptr_t)data + i, inst); lines.push_back(ReplaceAll(temp, "\t", " ")); i += (int)len; diff --git a/Core/MIPS/JitCommon/JitCommon.h b/Core/MIPS/JitCommon/JitCommon.h index b5944c3db5..c88c9fe345 100644 --- a/Core/MIPS/JitCommon/JitCommon.h +++ b/Core/MIPS/JitCommon/JitCommon.h @@ -54,6 +54,7 @@ namespace MIPSComp { virtual void Comp_RunBlock(MIPSOpcode op) = 0; virtual void Comp_ReplacementFunc(MIPSOpcode op) = 0; virtual void Comp_ITypeMem(MIPSOpcode op) = 0; + virtual void Comp_StoreSync(MIPSOpcode op) = 0; virtual void Comp_Cache(MIPSOpcode op) = 0; virtual void Comp_RelBranch(MIPSOpcode op) = 0; virtual void Comp_RelBranchRI(MIPSOpcode op) = 0; diff --git a/Core/MIPS/MIPS/MipsJit.h b/Core/MIPS/MIPS/MipsJit.h index 195579b37d..9026ac185c 100644 --- a/Core/MIPS/MIPS/MipsJit.h +++ b/Core/MIPS/MIPS/MipsJit.h @@ -62,6 +62,7 @@ public: // Ops void Comp_ITypeMem(MIPSOpcode op) override {} + void Comp_StoreSync(MIPSOpcode op) override {} void Comp_Cache(MIPSOpcode op) override {} void Comp_RelBranch(MIPSOpcode op) override {} diff --git a/Core/MIPS/MIPSAnalyst.cpp b/Core/MIPS/MIPSAnalyst.cpp index 4cfe9a2292..1cdeeb00ba 100644 --- a/Core/MIPS/MIPSAnalyst.cpp +++ b/Core/MIPS/MIPSAnalyst.cpp @@ -813,7 +813,7 @@ namespace MIPSAnalyst { break; } - if (reg > 32) { + if (reg >= 32) { return USAGE_UNKNOWN; } diff --git a/Core/MIPS/MIPSTables.cpp b/Core/MIPS/MIPSTables.cpp index 3a102f63f1..28ff363da9 100644 --- a/Core/MIPS/MIPSTables.cpp +++ b/Core/MIPS/MIPSTables.cpp @@ -150,7 +150,7 @@ static const MIPSInstruction tableImmediate[64] = // xxxxxx ..... ..... ........ INSTR("swr", JITFUNC(Comp_ITypeMem), Dis_ITypeMem, Int_ITypeMem, IN_IMM16|IN_RS_ADDR|IN_RT|OUT_MEM|MEMTYPE_WORD), INSTR("cache", JITFUNC(Comp_Cache), Dis_Cache, Int_Cache, IN_MEM|IN_IMM16|IN_RS_ADDR), //48 - INSTR("ll", JITFUNC(Comp_Generic), Dis_Generic, Int_StoreSync, IN_MEM|IN_IMM16|IN_RS_ADDR|OUT_RT|OUT_OTHER|MEMTYPE_WORD), + INSTR("ll", JITFUNC(Comp_StoreSync), Dis_ITypeMem, Int_StoreSync, IN_MEM|IN_IMM16|IN_RS_ADDR|OUT_RT|OUT_OTHER|MEMTYPE_WORD), INSTR("lwc1", JITFUNC(Comp_FPULS), Dis_FPULS, Int_FPULS, IN_MEM|IN_IMM16|IN_RS_ADDR|OUT_FT|MEMTYPE_FLOAT|IS_FPU), INSTR("lv.s", JITFUNC(Comp_SV), Dis_SV, Int_SV, IN_MEM|IN_IMM16|IN_RS_ADDR|OUT_OTHER|IS_VFPU|VFPU_NO_PREFIX|MEMTYPE_FLOAT), INVALID, @@ -159,7 +159,7 @@ static const MIPSInstruction tableImmediate[64] = // xxxxxx ..... ..... ........ INSTR("lv.q", JITFUNC(Comp_SVQ), Dis_SVQ, Int_SVQ, IN_MEM|IN_IMM16|IN_RS_ADDR|OUT_OTHER|IS_VFPU|VFPU_NO_PREFIX|MEMTYPE_VQUAD), //copU ENCODING(VFPU5), //56 - INSTR("sc", JITFUNC(Comp_Generic), Dis_Generic, Int_StoreSync, IN_IMM16|IN_RS_ADDR|IN_OTHER|IN_RT|OUT_RT|OUT_MEM|MEMTYPE_WORD), + INSTR("sc", JITFUNC(Comp_StoreSync), Dis_ITypeMem, Int_StoreSync, IN_IMM16|IN_RS_ADDR|IN_OTHER|IN_RT|OUT_RT|OUT_MEM|MEMTYPE_WORD), INSTR("swc1", JITFUNC(Comp_FPULS), Dis_FPULS, Int_FPULS, IN_IMM16|IN_RS_ADDR|IN_FT|OUT_MEM|MEMTYPE_FLOAT|IS_FPU), //copU INSTR("sv.s", JITFUNC(Comp_SV), Dis_SV, Int_SV, IN_IMM16|IN_RS_ADDR|IN_OTHER|OUT_MEM|IS_VFPU|VFPU_NO_PREFIX|MEMTYPE_FLOAT), INVALID, diff --git a/Core/MIPS/RiscV/RiscVAsm.cpp b/Core/MIPS/RiscV/RiscVAsm.cpp new file mode 100644 index 0000000000..e07b59760b --- /dev/null +++ b/Core/MIPS/RiscV/RiscVAsm.cpp @@ -0,0 +1,251 @@ +// Copyright (c) 2023- PPSSPP Project. + +// 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, version 2.0 or later versions. + +// 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 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#include "Common/Log.h" +#include "Core/CoreTiming.h" +#include "Core/MemMap.h" +#include "Core/MIPS/RiscV/RiscVJit.h" +#include "Core/MIPS/RiscV/RiscVRegCache.h" +#include "Core/MIPS/JitCommon/JitCommon.h" +#include "Core/MIPS/JitCommon/JitState.h" +#include "Core/System.h" + +namespace MIPSComp { + +using namespace RiscVGen; +using namespace RiscVJitConstants; + +static const bool enableDebug = false; +static const bool enableDisasm = false; + +static void ShowPC(u32 downcount, void *membase, void *jitbase) { + static int count = 0; + if (currentMIPS) { + ERROR_LOG(JIT, "[%08x] ShowPC Downcount : %08x %d %p %p", currentMIPS->pc, downcount, count, membase, jitbase); + } else { + ERROR_LOG(JIT, "Universe corrupt?"); + } + //if (count > 2000) + // exit(0); + count++; +} + +static void ShowBlockError(int type) { + if (type == 1) { + ERROR_LOG(JIT, "[%08x] ShowBlockError: block num was out of range in emuhack", currentMIPS->pc); + } else if (type == 2) { + ERROR_LOG(JIT, "[%08x] ShowBlockError: block num pointed to null jitblock", currentMIPS->pc); + } else { + ERROR_LOG(JIT, "[%08x] ShowBlockError: invalid error type", currentMIPS->pc); + } +} + +void RiscVJit::GenerateFixedCode(const JitOptions &jo) { + BeginWrite(GetMemoryProtectPageSize()); + const u8 *start = AlignCodePage(); + + if (jo.useStaticAlloc) { + saveStaticRegisters_ = AlignCode16(); + SW(DOWNCOUNTREG, CTXREG, offsetof(MIPSState, downcount)); + gpr.EmitSaveStaticRegisters(); + RET(); + + loadStaticRegisters_ = AlignCode16(); + gpr.EmitLoadStaticRegisters(); + LW(DOWNCOUNTREG, CTXREG, offsetof(MIPSState, downcount)); + RET(); + + start = saveStaticRegisters_; + } else { + saveStaticRegisters_ = nullptr; + loadStaticRegisters_ = nullptr; + } + + applyRoundingMode_ = AlignCode16(); + { + // Not sure if RISC-V has any flush to zero capability? Leaving it off for now... + LWU(SCRATCH2, CTXREG, offsetof(MIPSState, fcr31)); + + // We can skip if the rounding mode is nearest (0) and flush is not set. + // (as restoreRoundingMode cleared it out anyway) + FixupBranch skip = BEQ(SCRATCH2, R_ZERO); + + // MIPS Rounding Mode: RISC-V + // 0: Round nearest 0 + // 1: Round to zero 1 + // 2: Round up (ceil) 3 + // 3: Round down (floor) 2 + if (cpu_info.RiscV_Zbs) { + BEXTI(SCRATCH1, SCRATCH2, 1); + } else { + ANDI(SCRATCH1, SCRATCH2, 2); + SRLI(SCRATCH1, SCRATCH1, 1); + } + // Swap the lowest bit by the second bit. + XOR(SCRATCH2, SCRATCH2, SCRATCH1); + + FSRM(SCRATCH2); + + SetJumpTarget(skip); + RET(); + } + + enterDispatcher_ = AlignCode16(); + + // Start by saving some regs on the stack. There are 12 GPs and 12 FPs we want. + // Note: we leave R_SP as, well, SP, so it doesn't need to be saved. + _assert_msg_(cpu_info.Mode64bit, "RiscVAsm currently assumes RV64, not RV32 or RV128"); + static constexpr RiscVReg regs_to_save[]{ R_RA, X8, X9, X18, X19, X20, X21, X22, X23, X24, X25, X26, X27 }; + // TODO: Maybe we shouldn't regalloc all of these? Is it worth it? + static constexpr RiscVReg regs_to_save_fp[]{ F8, F9, F18, F19, F20, F21, F22, F23, F24, F25, F26, F27 }; + int saveSize = (XLEN / 8) * (int)(ARRAY_SIZE(regs_to_save) + ARRAY_SIZE(regs_to_save_fp)); + if (saveSize & 0xF) + saveSize += 8; + _assert_msg_((saveSize & 0xF) == 0, "Stack must be kept aligned"); + int saveOffset = 0; + ADDI(R_SP, R_SP, -saveSize); + for (RiscVReg r : regs_to_save) { + SD(r, R_SP, saveOffset); + saveOffset += XLEN / 8; + } + for (RiscVReg r : regs_to_save_fp) { + FS(64, r, R_SP, saveOffset); + saveOffset += XLEN / 8; + } + _assert_(saveOffset <= saveSize); + + // Fixed registers, these are always kept when in Jit context. + LI(MEMBASEREG, Memory::base, SCRATCH1); + LI(CTXREG, mips_, SCRATCH1); + LI(JITBASEREG, GetBasePtr(), SCRATCH1); + + LoadStaticRegisters(); + MovFromPC(SCRATCH1); + outerLoopPCInSCRATCH1_ = GetCodePtr(); + MovToPC(SCRATCH1); + outerLoop_ = GetCodePtr(); + // Advance can change the downcount (or thread), so must save/restore around it. + SaveStaticRegisters(); + RestoreRoundingMode(true); + QuickCallFunction(&CoreTiming::Advance); + ApplyRoundingMode(true); + LoadStaticRegisters(); + + dispatcherCheckCoreState_ = GetCodePtr(); + LI(SCRATCH1, &coreState, SCRATCH2); + LW(SCRATCH1, SCRATCH1, 0); + FixupBranch badCoreState = BNE(SCRATCH1, R_ZERO); + + // We just checked coreState, so go to advance if downcount is negative. + BLT(DOWNCOUNTREG, R_ZERO, outerLoop_); + FixupBranch skipToRealDispatch = J(); + + dispatcherPCInSCRATCH1_ = GetCodePtr(); + MovToPC(SCRATCH1); + + dispatcher_ = GetCodePtr(); + FixupBranch bail = BLT(DOWNCOUNTREG, R_ZERO); + SetJumpTarget(skipToRealDispatch); + + dispatcherNoCheck_ = GetCodePtr(); + + // Debug + if (enableDebug) { + MV(X10, DOWNCOUNTREG); + MV(X11, MEMBASEREG); + MV(X12, JITBASEREG); + QuickCallFunction(&ShowPC); + } + + LWU(SCRATCH1, CTXREG, offsetof(MIPSState, pc)); +#ifdef MASKED_PSP_MEMORY + LI(SCRATCH2, 0x3FFFFFFF); + AND(SCRATCH1, SCRATCH1, SCRATCH2); +#endif + ADD(SCRATCH1, SCRATCH1, MEMBASEREG); + dispatcherFetch_ = GetCodePtr(); + LWU(SCRATCH1, SCRATCH1, 0); + SRLI(SCRATCH2, SCRATCH1, 24); + // We're in other words comparing to the top 8 bits of MIPS_EMUHACK_OPCODE by subtracting. + ADDI(SCRATCH2, SCRATCH2, -(MIPS_EMUHACK_OPCODE >> 24)); + FixupBranch needsCompile = BNE(SCRATCH2, R_ZERO); + // Use a wall to mask by 0x00FFFFFF and extract the block jit offset. + SLLI(SCRATCH1, SCRATCH1, XLEN - 24); + SRLI(SCRATCH1, SCRATCH1, XLEN - 24); + ADD(SCRATCH1, JITBASEREG, SCRATCH1); + JR(SCRATCH1); + SetJumpTarget(needsCompile); + + // No block found, let's jit. We don't need to save static regs, they're all callee saved. + RestoreRoundingMode(true); + QuickCallFunction(&MIPSComp::JitAt); + ApplyRoundingMode(true); + + // Try again, the block index should be set now. + J(dispatcherNoCheck_); + + SetJumpTarget(bail); + + LI(SCRATCH1, &coreState, SCRATCH2); + LW(SCRATCH1, SCRATCH1, 0); + BEQ(SCRATCH1, R_ZERO, outerLoop_); + + const uint8_t *quitLoop = GetCodePtr(); + SetJumpTarget(badCoreState); + + SaveStaticRegisters(); + RestoreRoundingMode(true); + + _assert_msg_(cpu_info.Mode64bit, "RiscVAsm currently assumes RV64, not RV32 or RV128"); + saveOffset = 0; + for (RiscVReg r : regs_to_save) { + LD(r, R_SP, saveOffset); + saveOffset += XLEN / 8; + } + for (RiscVReg r : regs_to_save_fp) { + FL(64, r, R_SP, saveOffset); + saveOffset += XLEN / 8; + } + ADDI(R_SP, R_SP, saveSize); + + RET(); + + crashHandler_ = GetCodePtr(); + LI(SCRATCH1, &coreState, SCRATCH2); + LI(SCRATCH2, CORE_RUNTIME_ERROR); + SW(SCRATCH2, SCRATCH1, 0); + J(quitLoop); + + // Leave this at the end, add more stuff above. + if (enableDisasm) { +#if PPSSPP_ARCH(RISCV64) + std::vector lines = DisassembleRV64(start, GetCodePtr() - start); + for (auto s : lines) { + INFO_LOG(JIT, "%s", s.c_str()); + } +#endif + } + + // Let's spare the pre-generated code from unprotect-reprotect. + AlignCodePage(); + jitStartOffset_ = (int)(GetCodePtr() - start); + // Don't forget to zap the instruction cache! This must stay at the end of this function. + FlushIcache(); + EndWrite(); +} + +} // namespace MIPSComp diff --git a/Core/MIPS/RiscV/RiscVCompALU.cpp b/Core/MIPS/RiscV/RiscVCompALU.cpp new file mode 100644 index 0000000000..5cb2ff256c --- /dev/null +++ b/Core/MIPS/RiscV/RiscVCompALU.cpp @@ -0,0 +1,713 @@ +// Copyright (c) 2023- PPSSPP Project. + +// 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, version 2.0 or later versions. + +// 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 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#include "Common/CPUDetect.h" +#include "Core/MemMap.h" +#include "Core/MIPS/RiscV/RiscVJit.h" +#include "Core/MIPS/RiscV/RiscVRegCache.h" + +// This file contains compilation for integer / arithmetic / logic related instructions. +// +// All functions should have CONDITIONAL_DISABLE, so we can narrow things down to a file quickly. +// Currently known non working ones should have DISABLE. No flags because that's in IR already. + +// #define CONDITIONAL_DISABLE { CompIR_Generic(inst); return; } +#define CONDITIONAL_DISABLE {} +#define DISABLE { CompIR_Generic(inst); return; } +#define INVALIDOP { _assert_msg_(false, "Invalid IR inst %d", (int)inst.op); CompIR_Generic(inst); return; } + +namespace MIPSComp { + +using namespace RiscVGen; +using namespace RiscVJitConstants; + +void RiscVJit::CompIR_Arith(IRInst inst) { + CONDITIONAL_DISABLE; + + bool allowPtrMath = true; +#ifdef MASKED_PSP_MEMORY + // Since we modify it, we can't safely. + allowPtrMath = false; +#endif + + // RISC-V only adds signed immediates, so rewrite a small enough subtract to an add. + // We use -2047 and 2048 here because the range swaps. + if (inst.op == IROp::SubConst && (int32_t)inst.constant >= -2047 && (int32_t)inst.constant <= 2048) { + inst.op = IROp::AddConst; + inst.constant = (uint32_t)-(int32_t)inst.constant; + } + + switch (inst.op) { + case IROp::Add: + gpr.MapDirtyInIn(inst.dest, inst.src1, inst.src2, MapType::AVOID_LOAD_MARK_NORM32); + ADDW(gpr.R(inst.dest), gpr.R(inst.src1), gpr.R(inst.src2)); + break; + + case IROp::Sub: + gpr.MapDirtyInIn(inst.dest, inst.src1, inst.src2, MapType::AVOID_LOAD_MARK_NORM32); + SUBW(gpr.R(inst.dest), gpr.R(inst.src1), gpr.R(inst.src2)); + break; + + case IROp::AddConst: + if ((int32_t)inst.constant >= -2048 && (int32_t)inst.constant <= 2047) { + // Typical of stack pointer updates. + if (gpr.IsMappedAsPointer(inst.src1) && inst.dest == inst.src1 && allowPtrMath) { + gpr.MarkPtrDirty(gpr.RPtr(inst.dest)); + ADDI(gpr.RPtr(inst.dest), gpr.RPtr(inst.dest), inst.constant); + } else { + gpr.MapDirtyIn(inst.dest, inst.src1, MapType::AVOID_LOAD_MARK_NORM32); + ADDIW(gpr.R(inst.dest), gpr.R(inst.src1), inst.constant); + } + } else { + gpr.MapDirtyIn(inst.dest, inst.src1, MapType::AVOID_LOAD_MARK_NORM32); + LI(SCRATCH1, (int32_t)inst.constant); + ADDW(gpr.R(inst.dest), gpr.R(inst.src1), SCRATCH1); + } + break; + + case IROp::SubConst: + gpr.MapDirtyIn(inst.dest, inst.src1, MapType::AVOID_LOAD_MARK_NORM32); + LI(SCRATCH1, (int32_t)inst.constant); + SUBW(gpr.R(inst.dest), gpr.R(inst.src1), SCRATCH1); + break; + + case IROp::Neg: + gpr.MapDirtyIn(inst.dest, inst.src1, MapType::AVOID_LOAD_MARK_NORM32); + SUBW(gpr.R(inst.dest), R_ZERO, gpr.R(inst.src1)); + break; + + default: + INVALIDOP; + break; + } +} + +void RiscVJit::CompIR_Logic(IRInst inst) { + CONDITIONAL_DISABLE; + + switch (inst.op) { + case IROp::And: + if (inst.src1 != inst.src2) { + gpr.MapDirtyInIn(inst.dest, inst.src1, inst.src2); + AND(gpr.R(inst.dest), gpr.R(inst.src1), gpr.R(inst.src2)); + } else if (inst.src1 != inst.dest) { + gpr.MapDirtyIn(inst.dest, inst.src1); + MV(gpr.R(inst.dest), gpr.R(inst.src1)); + gpr.MarkDirty(gpr.R(inst.dest), gpr.IsNormalized32(inst.src1)); + } + break; + + case IROp::Or: + if (inst.src1 != inst.src2) { + gpr.MapDirtyInIn(inst.dest, inst.src1, inst.src2); + OR(gpr.R(inst.dest), gpr.R(inst.src1), gpr.R(inst.src2)); + // If both were normalized before, the result is normalized. + if (gpr.IsNormalized32(inst.src1) && gpr.IsNormalized32(inst.src2)) + gpr.MarkDirty(gpr.R(inst.dest), true); + } else if (inst.src1 != inst.dest) { + gpr.MapDirtyIn(inst.dest, inst.src1); + MV(gpr.R(inst.dest), gpr.R(inst.src1)); + gpr.MarkDirty(gpr.R(inst.dest), gpr.IsNormalized32(inst.src1)); + } + break; + + case IROp::Xor: + if (inst.src1 == inst.src2) { + gpr.SetImm(inst.dest, 0); + } else { + gpr.MapDirtyInIn(inst.dest, inst.src1, inst.src2); + XOR(gpr.R(inst.dest), gpr.R(inst.src1), gpr.R(inst.src2)); + } + break; + + case IROp::AndConst: + if ((int32_t)inst.constant >= -2048 && (int32_t)inst.constant <= 2047) { + gpr.MapDirtyIn(inst.dest, inst.src1); + ANDI(gpr.R(inst.dest), gpr.R(inst.src1), inst.constant); + } else { + gpr.MapDirtyIn(inst.dest, inst.src1); + LI(SCRATCH1, (int32_t)inst.constant); + AND(gpr.R(inst.dest), gpr.R(inst.src1), SCRATCH1); + } + // If the sign bits aren't cleared, and it was normalized before - it still is. + if ((inst.constant & 0x80000000) != 0 && gpr.IsNormalized32(inst.src1)) + gpr.MarkDirty(gpr.R(inst.dest), true); + // Otherwise, if we cleared the sign bits, it's naturally normalized. + else if ((inst.constant & 0x80000000) == 0) + gpr.MarkDirty(gpr.R(inst.dest), true); + break; + + case IROp::OrConst: + if ((int32_t)inst.constant >= -2048 && (int32_t)inst.constant <= 2047) { + gpr.MapDirtyIn(inst.dest, inst.src1); + ORI(gpr.R(inst.dest), gpr.R(inst.src1), inst.constant); + } else { + gpr.MapDirtyIn(inst.dest, inst.src1); + LI(SCRATCH1, (int32_t)inst.constant); + OR(gpr.R(inst.dest), gpr.R(inst.src1), SCRATCH1); + } + // Since our constant is normalized, oring its bits in won't hurt normalization. + if (gpr.IsNormalized32(inst.src1)) + gpr.MarkDirty(gpr.R(inst.dest), true); + break; + + case IROp::XorConst: + if ((int32_t)inst.constant >= -2048 && (int32_t)inst.constant <= 2047) { + gpr.MapDirtyIn(inst.dest, inst.src1); + XORI(gpr.R(inst.dest), gpr.R(inst.src1), inst.constant); + } else { + gpr.MapDirtyIn(inst.dest, inst.src1); + LI(SCRATCH1, (int32_t)inst.constant); + XOR(gpr.R(inst.dest), gpr.R(inst.src1), SCRATCH1); + } + break; + + case IROp::Not: + gpr.MapDirtyIn(inst.dest, inst.src1); + NOT(gpr.R(inst.dest), gpr.R(inst.src1)); + break; + + default: + INVALIDOP; + break; + } +} + +void RiscVJit::CompIR_Assign(IRInst inst) { + CONDITIONAL_DISABLE; + + switch (inst.op) { + case IROp::Mov: + if (inst.dest != inst.src1) { + gpr.MapDirtyIn(inst.dest, inst.src1); + MV(gpr.R(inst.dest), gpr.R(inst.src1)); + gpr.MarkDirty(gpr.R(inst.dest), gpr.IsNormalized32(inst.src1)); + } + break; + + case IROp::Ext8to32: + if (cpu_info.RiscV_Zbb) { + gpr.MapDirtyIn(inst.dest, inst.src1, MapType::AVOID_LOAD_MARK_NORM32); + SEXT_B(gpr.R(inst.dest), gpr.R(inst.src1)); + } else { + gpr.MapDirtyIn(inst.dest, inst.src1, MapType::AVOID_LOAD_MARK_NORM32); + SLLI(gpr.R(inst.dest), gpr.R(inst.src1), 24); + SRAIW(gpr.R(inst.dest), gpr.R(inst.dest), 24); + } + break; + + case IROp::Ext16to32: + if (cpu_info.RiscV_Zbb) { + gpr.MapDirtyIn(inst.dest, inst.src1, MapType::AVOID_LOAD_MARK_NORM32); + SEXT_H(gpr.R(inst.dest), gpr.R(inst.src1)); + } else { + gpr.MapDirtyIn(inst.dest, inst.src1, MapType::AVOID_LOAD_MARK_NORM32); + SLLI(gpr.R(inst.dest), gpr.R(inst.src1), 16); + SRAIW(gpr.R(inst.dest), gpr.R(inst.dest), 16); + } + break; + + default: + INVALIDOP; + break; + } +} + +void RiscVJit::CompIR_Bits(IRInst inst) { + CONDITIONAL_DISABLE; + + switch (inst.op) { + case IROp::ReverseBits: + CompIR_Generic(inst); + break; + + case IROp::BSwap16: + CompIR_Generic(inst); + break; + + case IROp::BSwap32: + if (cpu_info.RiscV_Zbb) { + gpr.MapDirtyIn(inst.dest, inst.src1); + REV8(gpr.R(inst.dest), gpr.R(inst.src1)); + if (XLEN >= 64) { + // REV8 swaps the entire register, so get the 32 highest bits. + SRAI(gpr.R(inst.dest), gpr.R(inst.dest), XLEN - 32); + gpr.MarkDirty(gpr.R(inst.dest), true); + } + } else { + CompIR_Generic(inst); + } + break; + + case IROp::Clz: + if (cpu_info.RiscV_Zbb) { + gpr.MapDirtyIn(inst.dest, inst.src1, MapType::AVOID_LOAD_MARK_NORM32); + // This even sets to 32 when zero, perfect. + CLZW(gpr.R(inst.dest), gpr.R(inst.src1)); + } else { + CompIR_Generic(inst); + } + break; + + default: + INVALIDOP; + break; + } +} + +void RiscVJit::CompIR_Shift(IRInst inst) { + CONDITIONAL_DISABLE; + + switch (inst.op) { + case IROp::Shl: + gpr.MapDirtyInIn(inst.dest, inst.src1, inst.src2, MapType::AVOID_LOAD_MARK_NORM32); + SLLW(gpr.R(inst.dest), gpr.R(inst.src1), gpr.R(inst.src2)); + break; + + case IROp::Shr: + gpr.MapDirtyInIn(inst.dest, inst.src1, inst.src2, MapType::AVOID_LOAD_MARK_NORM32); + SRLW(gpr.R(inst.dest), gpr.R(inst.src1), gpr.R(inst.src2)); + break; + + case IROp::Sar: + gpr.MapDirtyInIn(inst.dest, inst.src1, inst.src2, MapType::AVOID_LOAD_MARK_NORM32); + SRAW(gpr.R(inst.dest), gpr.R(inst.src1), gpr.R(inst.src2)); + break; + + case IROp::Ror: + if (cpu_info.RiscV_Zbb) { + gpr.MapDirtyInIn(inst.dest, inst.src1, inst.src2, MapType::AVOID_LOAD_MARK_NORM32); + RORW(gpr.R(inst.dest), gpr.R(inst.src1), gpr.R(inst.src2)); + } else { + CompIR_Generic(inst); + } + break; + + case IROp::ShlImm: + // Shouldn't happen, but let's be safe of any passes that modify the ops. + if (inst.src2 >= 32) { + gpr.SetImm(inst.dest, 0); + } else if (inst.src2 == 0) { + if (inst.dest != inst.src1) { + gpr.MapDirtyIn(inst.dest, inst.src1); + MV(gpr.R(inst.dest), gpr.R(inst.src1)); + gpr.MarkDirty(gpr.R(inst.dest), gpr.IsNormalized32(inst.src1)); + } + } else { + gpr.MapDirtyIn(inst.dest, inst.src1, MapType::AVOID_LOAD_MARK_NORM32); + SLLIW(gpr.R(inst.dest), gpr.R(inst.src1), inst.src2); + } + break; + + case IROp::ShrImm: + // Shouldn't happen, but let's be safe of any passes that modify the ops. + if (inst.src2 >= 32) { + gpr.SetImm(inst.dest, 0); + } else if (inst.src2 == 0) { + if (inst.dest != inst.src1) { + gpr.MapDirtyIn(inst.dest, inst.src1); + MV(gpr.R(inst.dest), gpr.R(inst.src1)); + gpr.MarkDirty(gpr.R(inst.dest), gpr.IsNormalized32(inst.src1)); + } + } else { + gpr.MapDirtyIn(inst.dest, inst.src1, MapType::AVOID_LOAD_MARK_NORM32); + SRLIW(gpr.R(inst.dest), gpr.R(inst.src1), inst.src2); + } + break; + + case IROp::SarImm: + // Shouldn't happen, but let's be safe of any passes that modify the ops. + if (inst.src2 >= 32) { + gpr.MapDirtyIn(inst.dest, inst.src1, MapType::AVOID_LOAD_MARK_NORM32); + SRAIW(gpr.R(inst.dest), gpr.R(inst.src1), 31); + } else if (inst.src2 == 0) { + if (inst.dest != inst.src1) { + gpr.MapDirtyIn(inst.dest, inst.src1); + MV(gpr.R(inst.dest), gpr.R(inst.src1)); + gpr.MarkDirty(gpr.R(inst.dest), gpr.IsNormalized32(inst.src1)); + } + } else { + gpr.MapDirtyIn(inst.dest, inst.src1, MapType::AVOID_LOAD_MARK_NORM32); + SRAIW(gpr.R(inst.dest), gpr.R(inst.src1), inst.src2); + } + break; + + case IROp::RorImm: + if (inst.src2 == 0) { + if (inst.dest != inst.src1) { + gpr.MapDirtyIn(inst.dest, inst.src1); + MV(gpr.R(inst.dest), gpr.R(inst.src1)); + gpr.MarkDirty(gpr.R(inst.dest), gpr.IsNormalized32(inst.src1)); + } + } else if (cpu_info.RiscV_Zbb) { + gpr.MapDirtyIn(inst.dest, inst.src1, MapType::AVOID_LOAD_MARK_NORM32); + RORIW(gpr.R(inst.dest), gpr.R(inst.src1), inst.src2 & 31); + } else { + CompIR_Generic(inst); + } + break; + + default: + INVALIDOP; + break; + } +} + +void RiscVJit::CompIR_Compare(IRInst inst) { + CONDITIONAL_DISABLE; + + RiscVReg lhs = INVALID_REG; + RiscVReg rhs = INVALID_REG; + switch (inst.op) { + case IROp::Slt: + gpr.SpillLock(inst.dest, inst.src1, inst.src2); + gpr.MapReg(inst.src1); + gpr.MapReg(inst.src2); + NormalizeSrc12(inst, &lhs, &rhs, SCRATCH1, SCRATCH2, true); + gpr.MapReg(inst.dest, MIPSMap::NOINIT | MIPSMap::MARK_NORM32); + gpr.ReleaseSpillLock(inst.dest, inst.src1, inst.src2); + + SLT(gpr.R(inst.dest), lhs, rhs); + break; + + case IROp::SltConst: + if (inst.constant == 0) { + // Basically, getting the sign bit. Let's shift instead. + gpr.MapDirtyIn(inst.dest, inst.src1, MapType::AVOID_LOAD_MARK_NORM32); + SRLIW(gpr.R(inst.dest), gpr.R(inst.src1), 31); + } else { + gpr.SpillLock(inst.dest, inst.src1); + gpr.MapReg(inst.src1); + NormalizeSrc1(inst, &lhs, SCRATCH1, false); + gpr.MapReg(inst.dest, MIPSMap::NOINIT | MIPSMap::MARK_NORM32); + gpr.ReleaseSpillLock(inst.dest, inst.src1); + + if ((int32_t)inst.constant >= -2048 && (int32_t)inst.constant <= 2047) { + SLTI(gpr.R(inst.dest), lhs, (int32_t)inst.constant); + } else { + LI(SCRATCH2, (int32_t)inst.constant); + SLT(gpr.R(inst.dest), lhs, SCRATCH2); + } + gpr.MarkDirty(gpr.R(inst.dest), true); + } + break; + + case IROp::SltU: + gpr.SpillLock(inst.dest, inst.src1, inst.src2); + gpr.MapReg(inst.src1); + gpr.MapReg(inst.src2); + // It's still fine to sign extend, the biggest just get even bigger. + NormalizeSrc12(inst, &lhs, &rhs, SCRATCH1, SCRATCH2, true); + gpr.MapReg(inst.dest, MIPSMap::NOINIT | MIPSMap::MARK_NORM32); + gpr.ReleaseSpillLock(inst.dest, inst.src1, inst.src2); + + SLTU(gpr.R(inst.dest), lhs, rhs); + break; + + case IROp::SltUConst: + if (inst.constant == 0) { + gpr.SetImm(inst.dest, 0); + } else { + gpr.SpillLock(inst.dest, inst.src1); + gpr.MapReg(inst.src1); + NormalizeSrc1(inst, &lhs, SCRATCH1, false); + gpr.MapReg(inst.dest, MIPSMap::NOINIT | MIPSMap::MARK_NORM32); + gpr.ReleaseSpillLock(inst.dest, inst.src1); + + // We sign extend because we're comparing against something normalized. + // It's also the most efficient to set. + if ((int32_t)inst.constant >= -2048 && (int32_t)inst.constant <= 2047) { + SLTIU(gpr.R(inst.dest), lhs, (int32_t)inst.constant); + } else { + LI(SCRATCH2, (int32_t)inst.constant); + SLTU(gpr.R(inst.dest), lhs, SCRATCH2); + } + } + break; + + default: + INVALIDOP; + break; + } +} + +void RiscVJit::CompIR_CondAssign(IRInst inst) { + CONDITIONAL_DISABLE; + + RiscVReg lhs = INVALID_REG; + RiscVReg rhs = INVALID_REG; + FixupBranch fixup; + switch (inst.op) { + case IROp::MovZ: + case IROp::MovNZ: + if (inst.dest == inst.src2) + return; + + // We could have a "zero" that with wrong upper due to XOR, so we have to normalize. + gpr.MapDirtyInIn(inst.dest, inst.src1, inst.src2, MapType::ALWAYS_LOAD); + NormalizeSrc1(inst, &lhs, SCRATCH1, true); + + switch (inst.op) { + case IROp::MovZ: + fixup = BNE(lhs, R_ZERO); + break; + case IROp::MovNZ: + fixup = BEQ(lhs, R_ZERO); + break; + default: + INVALIDOP; + break; + } + + MV(gpr.R(inst.dest), gpr.R(inst.src2)); + SetJumpTarget(fixup); + break; + + case IROp::Max: + if (inst.src1 != inst.src2) { + if (cpu_info.RiscV_Zbb) { + gpr.MapDirtyInIn(inst.dest, inst.src1, inst.src2); + NormalizeSrc12(inst, &lhs, &rhs, SCRATCH1, SCRATCH2, true); + MAX(gpr.R(inst.dest), lhs, rhs); + // Because we had to normalize the inputs, the output is normalized. + gpr.MarkDirty(gpr.R(inst.dest), true); + } else { + CompIR_Generic(inst); + } + } else if (inst.dest != inst.src1) { + gpr.MapDirtyIn(inst.dest, inst.src1); + MV(gpr.R(inst.dest), gpr.R(inst.src1)); + gpr.MarkDirty(gpr.R(inst.dest), gpr.IsNormalized32(inst.src1)); + } + break; + + case IROp::Min: + if (inst.src1 != inst.src2) { + if (cpu_info.RiscV_Zbb) { + gpr.MapDirtyInIn(inst.dest, inst.src1, inst.src2); + NormalizeSrc12(inst, &lhs, &rhs, SCRATCH1, SCRATCH2, true); + MIN(gpr.R(inst.dest), lhs, rhs); + // Because we had to normalize the inputs, the output is normalized. + gpr.MarkDirty(gpr.R(inst.dest), true); + } else { + CompIR_Generic(inst); + } + } else if (inst.dest != inst.src1) { + gpr.MapDirtyIn(inst.dest, inst.src1); + MV(gpr.R(inst.dest), gpr.R(inst.src1)); + gpr.MarkDirty(gpr.R(inst.dest), gpr.IsNormalized32(inst.src1)); + } + break; + + default: + INVALIDOP; + break; + } +} + +void RiscVJit::CompIR_HiLo(IRInst inst) { + CONDITIONAL_DISABLE; + + switch (inst.op) { + case IROp::MtLo: + gpr.MapDirtyIn(IRREG_LO, inst.src1); + MV(gpr.R(IRREG_LO), gpr.R(inst.src1)); + gpr.MarkDirty(gpr.R(IRREG_LO), gpr.IsNormalized32(inst.src1)); + break; + + case IROp::MtHi: + gpr.MapDirtyIn(IRREG_HI, inst.src1); + MV(gpr.R(IRREG_HI), gpr.R(inst.src1)); + gpr.MarkDirty(gpr.R(IRREG_HI), gpr.IsNormalized32(inst.src1)); + break; + + case IROp::MfLo: + gpr.MapDirtyIn(inst.dest, IRREG_LO); + MV(gpr.R(inst.dest), gpr.R(IRREG_LO)); + gpr.MarkDirty(gpr.R(inst.dest), gpr.IsNormalized32(IRREG_LO)); + break; + + case IROp::MfHi: + gpr.MapDirtyIn(inst.dest, IRREG_HI); + MV(gpr.R(inst.dest), gpr.R(IRREG_HI)); + gpr.MarkDirty(gpr.R(inst.dest), gpr.IsNormalized32(IRREG_HI)); + break; + + default: + INVALIDOP; + break; + } +} + +void RiscVJit::CompIR_Mult(IRInst inst) { + CONDITIONAL_DISABLE; + + auto makeArgsUnsigned = [&](RiscVReg *lhs, RiscVReg *rhs) { + if (cpu_info.RiscV_Zba) { + ZEXT_W(SCRATCH1, gpr.R(inst.src1)); + ZEXT_W(SCRATCH2, gpr.R(inst.src2)); + } else { + SLLI(SCRATCH1, gpr.R(inst.src1), XLEN - 32); + SRLI(SCRATCH1, SCRATCH1, XLEN - 32); + SLLI(SCRATCH2, gpr.R(inst.src2), XLEN - 32); + SRLI(SCRATCH2, SCRATCH2, XLEN - 32); + } + *lhs = SCRATCH1; + *rhs = SCRATCH2; + }; + auto combinePrevMulResult = [&] { + // TODO: Using a single reg for HI/LO would make this less ugly. + if (cpu_info.RiscV_Zba) { + ZEXT_W(gpr.R(IRREG_LO), gpr.R(IRREG_LO)); + } else { + SLLI(gpr.R(IRREG_LO), gpr.R(IRREG_LO), XLEN - 32); + SRLI(gpr.R(IRREG_LO), gpr.R(IRREG_LO), XLEN - 32); + } + SLLI(gpr.R(IRREG_HI), gpr.R(IRREG_HI), 32); + OR(gpr.R(IRREG_LO), gpr.R(IRREG_LO), gpr.R(IRREG_HI)); + }; + auto splitMulResult = [&] { + SRAI(gpr.R(IRREG_HI), gpr.R(IRREG_LO), 32); + gpr.MarkDirty(gpr.R(IRREG_HI), true); + }; + + RiscVReg lhs = INVALID_REG; + RiscVReg rhs = INVALID_REG; + switch (inst.op) { + case IROp::Mult: + // TODO: Maybe IR could simplify when HI is not needed or clobbered? + // TODO: HI/LO merge optimization? Have to be careful of passes that split them... + gpr.MapDirtyDirtyInIn(IRREG_LO, IRREG_HI, inst.src1, inst.src2); + NormalizeSrc12(inst, &lhs, &rhs, SCRATCH1, SCRATCH2, true); + MUL(gpr.R(IRREG_LO), lhs, rhs); + splitMulResult(); + break; + + case IROp::MultU: + // This is an "anti-norm32" case. Let's just zero always. + // TODO: If we could know that LO was only needed, we could use MULW and be done. + gpr.MapDirtyDirtyInIn(IRREG_LO, IRREG_HI, inst.src1, inst.src2); + makeArgsUnsigned(&lhs, &rhs); + MUL(gpr.R(IRREG_LO), lhs, rhs); + splitMulResult(); + break; + + case IROp::Madd: + gpr.MapDirtyDirtyInIn(IRREG_LO, IRREG_HI, inst.src1, inst.src2, MapType::ALWAYS_LOAD); + NormalizeSrc12(inst, &lhs, &rhs, SCRATCH1, SCRATCH2, true); + MUL(SCRATCH1, lhs, rhs); + + combinePrevMulResult(); + ADD(gpr.R(IRREG_LO), gpr.R(IRREG_LO), SCRATCH1); + splitMulResult(); + break; + + case IROp::MaddU: + gpr.MapDirtyDirtyInIn(IRREG_LO, IRREG_HI, inst.src1, inst.src2, MapType::ALWAYS_LOAD); + makeArgsUnsigned(&lhs, &rhs); + MUL(SCRATCH1, lhs, rhs); + + combinePrevMulResult(); + ADD(gpr.R(IRREG_LO), gpr.R(IRREG_LO), SCRATCH1); + splitMulResult(); + break; + + case IROp::Msub: + gpr.MapDirtyDirtyInIn(IRREG_LO, IRREG_HI, inst.src1, inst.src2, MapType::ALWAYS_LOAD); + NormalizeSrc12(inst, &lhs, &rhs, SCRATCH1, SCRATCH2, true); + MUL(SCRATCH1, lhs, rhs); + + combinePrevMulResult(); + SUB(gpr.R(IRREG_LO), gpr.R(IRREG_LO), SCRATCH1); + splitMulResult(); + break; + + case IROp::MsubU: + gpr.MapDirtyDirtyInIn(IRREG_LO, IRREG_HI, inst.src1, inst.src2, MapType::ALWAYS_LOAD); + makeArgsUnsigned(&lhs, &rhs); + MUL(SCRATCH1, lhs, rhs); + + combinePrevMulResult(); + SUB(gpr.R(IRREG_LO), gpr.R(IRREG_LO), SCRATCH1); + splitMulResult(); + break; + + default: + INVALIDOP; + break; + } +} + +void RiscVJit::CompIR_Div(IRInst inst) { + CONDITIONAL_DISABLE; + + RiscVReg numReg, denomReg; + switch (inst.op) { + case IROp::Div: + gpr.MapDirtyDirtyInIn(IRREG_LO, IRREG_HI, inst.src1, inst.src2, MapType::AVOID_LOAD_MARK_NORM32); + // We have to do this because of the divide by zero and overflow checks below. + NormalizeSrc12(inst, &numReg, &denomReg, SCRATCH1, SCRATCH2, true); + DIVW(gpr.R(IRREG_LO), numReg, denomReg); + REMW(gpr.R(IRREG_HI), numReg, denomReg); + + // Now some tweaks for divide by zero and overflow. + { + // Start with divide by zero, remainder is fine. + FixupBranch skipNonZero = BNE(denomReg, R_ZERO); + FixupBranch keepNegOne = BGE(numReg, R_ZERO); + LI(gpr.R(IRREG_LO), 1); + SetJumpTarget(keepNegOne); + SetJumpTarget(skipNonZero); + + // For overflow, RISC-V sets LO right, but remainder to zero. + // Cheating a bit by using R_RA as a temp... + LI(R_RA, (int32_t)0x80000000); + FixupBranch notMostNegative = BNE(numReg, R_RA); + LI(R_RA, -1); + FixupBranch notNegativeOne = BNE(denomReg, R_RA); + LI(gpr.R(IRREG_HI), -1); + SetJumpTarget(notNegativeOne); + SetJumpTarget(notMostNegative); + } + break; + + case IROp::DivU: + gpr.MapDirtyDirtyInIn(IRREG_LO, IRREG_HI, inst.src1, inst.src2, MapType::AVOID_LOAD_MARK_NORM32); + // We have to do this because of the divide by zero check below. + NormalizeSrc12(inst, &numReg, &denomReg, SCRATCH1, SCRATCH2, true); + DIVUW(gpr.R(IRREG_LO), numReg, denomReg); + REMUW(gpr.R(IRREG_HI), numReg, denomReg); + + // On divide by zero, everything is correct already except the 0xFFFF case. + { + FixupBranch skipNonZero = BNE(denomReg, R_ZERO); + // Luckily, we don't need SCRATCH2/denomReg anymore. + LI(SCRATCH2, 0xFFFF); + FixupBranch keepNegOne = BLTU(SCRATCH2, numReg); + MV(gpr.R(IRREG_LO), SCRATCH2); + SetJumpTarget(keepNegOne); + SetJumpTarget(skipNonZero); + } + break; + + default: + INVALIDOP; + break; + } +} + +} // namespace MIPSComp diff --git a/Core/MIPS/RiscV/RiscVCompBranch.cpp b/Core/MIPS/RiscV/RiscVCompBranch.cpp new file mode 100644 index 0000000000..aa9d2467a0 --- /dev/null +++ b/Core/MIPS/RiscV/RiscVCompBranch.cpp @@ -0,0 +1,147 @@ +// Copyright (c) 2023- PPSSPP Project. + +// 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, version 2.0 or later versions. + +// 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 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#include "Core/MemMap.h" +#include "Core/MIPS/RiscV/RiscVJit.h" +#include "Core/MIPS/RiscV/RiscVRegCache.h" + +// This file contains compilation for exits. +// +// All functions should have CONDITIONAL_DISABLE, so we can narrow things down to a file quickly. +// Currently known non working ones should have DISABLE. No flags because that's in IR already. + +// #define CONDITIONAL_DISABLE { CompIR_Generic(inst); return; } +#define CONDITIONAL_DISABLE {} +#define DISABLE { CompIR_Generic(inst); return; } +#define INVALIDOP { _assert_msg_(false, "Invalid IR inst %d", (int)inst.op); CompIR_Generic(inst); return; } + +namespace MIPSComp { + +using namespace RiscVGen; +using namespace RiscVJitConstants; + +void RiscVJit::CompIR_Exit(IRInst inst) { + CONDITIONAL_DISABLE; + + RiscVReg exitReg = INVALID_REG; + switch (inst.op) { + case IROp::ExitToConst: + FlushAll(); + LI(SCRATCH1, inst.constant); + QuickJ(R_RA, dispatcherPCInSCRATCH1_); + break; + + case IROp::ExitToReg: + exitReg = gpr.MapReg(inst.src1); + FlushAll(); + // TODO: If ever we don't read this back in dispatcherPCInSCRATCH1_, we should zero upper. + MV(SCRATCH1, exitReg); + QuickJ(R_RA, dispatcherPCInSCRATCH1_); + break; + + case IROp::ExitToPC: + FlushAll(); + QuickJ(R_RA, dispatcherCheckCoreState_); + break; + + default: + INVALIDOP; + break; + } +} + +void RiscVJit::CompIR_ExitIf(IRInst inst) { + CONDITIONAL_DISABLE; + + RiscVReg lhs = INVALID_REG; + RiscVReg rhs = INVALID_REG; + FixupBranch fixup; + switch (inst.op) { + case IROp::ExitToConstIfEq: + case IROp::ExitToConstIfNeq: + gpr.MapInIn(inst.src1, inst.src2); + // We can't use SCRATCH1, which is destroyed by FlushAll()... but cheat and use R_RA. + NormalizeSrc12(inst, &lhs, &rhs, R_RA, SCRATCH2, true); + FlushAll(); + + switch (inst.op) { + case IROp::ExitToConstIfEq: + fixup = BNE(lhs, rhs); + break; + + case IROp::ExitToConstIfNeq: + fixup = BEQ(lhs, rhs); + break; + + default: + INVALIDOP; + break; + } + + LI(SCRATCH1, inst.constant); + QuickJ(R_RA, dispatcherPCInSCRATCH1_); + SetJumpTarget(fixup); + break; + + case IROp::ExitToConstIfGtZ: + case IROp::ExitToConstIfGeZ: + case IROp::ExitToConstIfLtZ: + case IROp::ExitToConstIfLeZ: + gpr.MapReg(inst.src1); + NormalizeSrc1(inst, &lhs, SCRATCH2, true); + FlushAll(); + + switch (inst.op) { + case IROp::ExitToConstIfGtZ: + fixup = BGE(R_ZERO, lhs); + break; + + case IROp::ExitToConstIfGeZ: + fixup = BLT(lhs, R_ZERO); + break; + + case IROp::ExitToConstIfLtZ: + fixup = BGE(lhs, R_ZERO); + break; + + case IROp::ExitToConstIfLeZ: + fixup = BLT(R_ZERO, lhs); + break; + + default: + INVALIDOP; + break; + } + + LI(SCRATCH1, inst.constant); + QuickJ(R_RA, dispatcherPCInSCRATCH1_); + SetJumpTarget(fixup); + break; + + case IROp::ExitToConstIfFpTrue: + case IROp::ExitToConstIfFpFalse: + // Note: not used. + DISABLE; + break; + + default: + INVALIDOP; + break; + } +} + +} // namespace MIPSComp diff --git a/Core/MIPS/RiscV/RiscVCompFPU.cpp b/Core/MIPS/RiscV/RiscVCompFPU.cpp new file mode 100644 index 0000000000..18cd1a00c2 --- /dev/null +++ b/Core/MIPS/RiscV/RiscVCompFPU.cpp @@ -0,0 +1,644 @@ +// Copyright (c) 2023- PPSSPP Project. + +// 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, version 2.0 or later versions. + +// 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 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#include "Core/MemMap.h" +#include "Core/MIPS/RiscV/RiscVJit.h" +#include "Core/MIPS/RiscV/RiscVRegCache.h" + +// This file contains compilation for floating point related instructions. +// +// All functions should have CONDITIONAL_DISABLE, so we can narrow things down to a file quickly. +// Currently known non working ones should have DISABLE. No flags because that's in IR already. + +// #define CONDITIONAL_DISABLE { CompIR_Generic(inst); return; } +#define CONDITIONAL_DISABLE {} +#define DISABLE { CompIR_Generic(inst); return; } +#define INVALIDOP { _assert_msg_(false, "Invalid IR inst %d", (int)inst.op); CompIR_Generic(inst); return; } + +namespace MIPSComp { + +using namespace RiscVGen; +using namespace RiscVJitConstants; + +void RiscVJit::CompIR_FArith(IRInst inst) { + CONDITIONAL_DISABLE; + + switch (inst.op) { + case IROp::FAdd: + fpr.MapDirtyInIn(inst.dest, inst.src1, inst.src2); + FADD(32, fpr.R(inst.dest), fpr.R(inst.src1), fpr.R(inst.src2)); + break; + + case IROp::FSub: + fpr.MapDirtyInIn(inst.dest, inst.src1, inst.src2); + FSUB(32, fpr.R(inst.dest), fpr.R(inst.src1), fpr.R(inst.src2)); + break; + + case IROp::FMul: + fpr.MapDirtyInIn(inst.dest, inst.src1, inst.src2); + // TODO: If FMUL consistently produces NAN across chip vendors, we can skip this. + // Luckily this does match the RISC-V canonical NAN. + if (inst.src1 != inst.src2) { + // These will output 0x80/0x01 if infinity, 0x10/0x80 if zero. + // We need to check if one is infinity and the other zero. + + // First, try inf * zero. + FCLASS(32, SCRATCH1, fpr.R(inst.src1)); + FCLASS(32, SCRATCH2, fpr.R(inst.src2)); + ANDI(R_RA, SCRATCH1, 0x81); + FixupBranch lhsNotInf = BEQ(R_RA, R_ZERO); + ANDI(R_RA, SCRATCH2, 0x18); + FixupBranch infZero = BNE(R_RA, R_ZERO); + + // Okay, what about the other order? + SetJumpTarget(lhsNotInf); + ANDI(R_RA, SCRATCH1, 0x18); + FixupBranch lhsNotZero = BEQ(R_RA, R_ZERO); + ANDI(R_RA, SCRATCH2, 0x81); + FixupBranch zeroInf = BNE(R_RA, R_ZERO); + + // Nope, all good. + SetJumpTarget(lhsNotZero); + FMUL(32, fpr.R(inst.dest), fpr.R(inst.src1), fpr.R(inst.src2)); + FixupBranch skip = J(); + + SetJumpTarget(infZero); + SetJumpTarget(zeroInf); + LI(SCRATCH1, 0x7FC00000); + FMV(FMv::W, FMv::X, fpr.R(inst.dest), SCRATCH1); + + SetJumpTarget(skip); + } else { + FMUL(32, fpr.R(inst.dest), fpr.R(inst.src1), fpr.R(inst.src2)); + } + break; + + case IROp::FDiv: + fpr.MapDirtyInIn(inst.dest, inst.src1, inst.src2); + FDIV(32, fpr.R(inst.dest), fpr.R(inst.src1), fpr.R(inst.src2)); + break; + + case IROp::FSqrt: + fpr.MapDirtyIn(inst.dest, inst.src1); + FSQRT(32, fpr.R(inst.dest), fpr.R(inst.src1)); + break; + + case IROp::FNeg: + fpr.MapDirtyIn(inst.dest, inst.src1); + FNEG(32, fpr.R(inst.dest), fpr.R(inst.src1)); + break; + + default: + INVALIDOP; + break; + } +} + +void RiscVJit::CompIR_FCondAssign(IRInst inst) { + CONDITIONAL_DISABLE; + if (inst.op != IROp::FMin && inst.op != IROp::FMax) + INVALIDOP; + bool maxCondition = inst.op == IROp::FMax; + + // FMin and FMax are used by VFPU and handle NAN/INF as just a larger exponent. + fpr.MapDirtyInIn(inst.dest, inst.src1, inst.src2); + FCLASS(32, SCRATCH1, fpr.R(inst.src1)); + FCLASS(32, SCRATCH2, fpr.R(inst.src2)); + + // If either side is a NAN, it needs to participate in the comparison. + OR(SCRATCH1, SCRATCH1, SCRATCH2); + // NAN is either 0x100 or 0x200. + ANDI(SCRATCH1, SCRATCH1, 0x300); + FixupBranch useNormalCond = BEQ(SCRATCH1, R_ZERO); + + // Time to use bits... classify won't help because it ignores -NAN. + FMV(FMv::X, FMv::W, SCRATCH1, fpr.R(inst.src1)); + FMV(FMv::X, FMv::W, SCRATCH2, fpr.R(inst.src2)); + + // If both are negative, we flip the comparison (not two's compliment.) + // We cheat and use RA... + AND(R_RA, SCRATCH1, SCRATCH2); + SRLIW(R_RA, R_RA, 31); + + if (cpu_info.RiscV_Zbb) { + FixupBranch swapCompare = BNE(R_RA, R_ZERO); + if (maxCondition) + MAX(SCRATCH1, SCRATCH1, SCRATCH2); + else + MIN(SCRATCH1, SCRATCH1, SCRATCH2); + FixupBranch skipSwapCompare = J(); + SetJumpTarget(swapCompare); + if (maxCondition) + MIN(SCRATCH1, SCRATCH1, SCRATCH2); + else + MAX(SCRATCH1, SCRATCH1, SCRATCH2); + SetJumpTarget(skipSwapCompare); + } else { + RiscVReg isSrc1LowerReg = gpr.GetAndLockTempR(); + gpr.ReleaseSpillLocksAndDiscardTemps(); + + SLT(isSrc1LowerReg, SCRATCH1, SCRATCH2); + // Flip the flag (to reverse the min/max) based on if both were negative. + XOR(isSrc1LowerReg, isSrc1LowerReg, R_RA); + FixupBranch useSrc1; + if (maxCondition) + useSrc1 = BEQ(isSrc1LowerReg, R_ZERO); + else + useSrc1 = BNE(isSrc1LowerReg, R_ZERO); + MV(SCRATCH1, SCRATCH2); + SetJumpTarget(useSrc1); + } + + FMV(FMv::W, FMv::X, fpr.R(inst.dest), SCRATCH1); + FixupBranch finish = J(); + + SetJumpTarget(useNormalCond); + if (maxCondition) + FMAX(32, fpr.R(inst.dest), fpr.R(inst.src1), fpr.R(inst.src2)); + else + FMIN(32, fpr.R(inst.dest), fpr.R(inst.src1), fpr.R(inst.src2)); + SetJumpTarget(finish); +} + +void RiscVJit::CompIR_FAssign(IRInst inst) { + CONDITIONAL_DISABLE; + + switch (inst.op) { + case IROp::FMov: + if (inst.dest != inst.src1) { + fpr.MapDirtyIn(inst.dest, inst.src1); + FMV(32, fpr.R(inst.dest), fpr.R(inst.src1)); + } + break; + + case IROp::FAbs: + fpr.MapDirtyIn(inst.dest, inst.src1); + FABS(32, fpr.R(inst.dest), fpr.R(inst.src1)); + break; + + case IROp::FSign: + { + fpr.MapDirtyIn(inst.dest, inst.src1); + // Check if it's negative zero, either 0x10/0x08 is zero. + FCLASS(32, SCRATCH1, fpr.R(inst.src1)); + ANDI(SCRATCH1, SCRATCH1, 0x18); + SEQZ(SCRATCH1, SCRATCH1); + // Okay, it's zero if zero, 1 otherwise. Convert 1 to a constant 1.0. + // Probably non-zero is the common case, so we make that the straight line. + FixupBranch skipOne = BEQ(SCRATCH1, R_ZERO); + LI(SCRATCH1, 1.0f); + + // Now we just need the sign from it. + FMV(FMv::X, FMv::W, SCRATCH2, fpr.R(inst.src1)); + // Use a wall to isolate the sign, and combine. + SRAIW(SCRATCH2, SCRATCH2, 31); + SLLIW(SCRATCH2, SCRATCH2, 31); + OR(SCRATCH1, SCRATCH1, SCRATCH2); + + SetJumpTarget(skipOne); + FMV(FMv::W, FMv::X, fpr.R(inst.dest), SCRATCH1); + break; + } + + default: + INVALIDOP; + break; + } +} + +void RiscVJit::CompIR_FRound(IRInst inst) { + CONDITIONAL_DISABLE; + + // TODO: If this is followed by a GPR transfer, might want to combine. + fpr.MapDirtyIn(inst.dest, inst.src1); + + switch (inst.op) { + case IROp::FRound: + FCVT(FConv::W, FConv::S, SCRATCH1, fpr.R(inst.src1), Round::NEAREST_EVEN); + break; + + case IROp::FTrunc: + FCVT(FConv::W, FConv::S, SCRATCH1, fpr.R(inst.src1), Round::TOZERO); + break; + + case IROp::FCeil: + FCVT(FConv::W, FConv::S, SCRATCH1, fpr.R(inst.src1), Round::UP); + break; + + case IROp::FFloor: + FCVT(FConv::W, FConv::S, SCRATCH1, fpr.R(inst.src1), Round::DOWN); + break; + + default: + INVALIDOP; + break; + } + + FMV(FMv::W, FMv::X, fpr.R(inst.dest), SCRATCH1); +} + +void RiscVJit::CompIR_FCvt(IRInst inst) { + CONDITIONAL_DISABLE; + + switch (inst.op) { + case IROp::FCvtWS: + case IROp::FCvtScaledWS: + case IROp::FCvtScaledSW: + CompIR_Generic(inst); + break; + + case IROp::FCvtSW: + // TODO: This is probably proceeded by a GPR transfer, might be ideal to combine. + fpr.MapDirtyIn(inst.dest, inst.src1); + FMV(FMv::X, FMv::W, SCRATCH1, fpr.R(inst.src1)); + FCVT(FConv::S, FConv::W, fpr.R(inst.dest), SCRATCH1); + break; + + default: + INVALIDOP; + break; + } +} + +void RiscVJit::CompIR_FSat(IRInst inst) { + CONDITIONAL_DISABLE; + + RiscVReg tempReg = INVALID_REG; + FixupBranch skipLower; + FixupBranch finishLower; + FixupBranch skipHigher; + switch (inst.op) { + case IROp::FSat0_1: + tempReg = fpr.MapDirtyInTemp(inst.dest, inst.src1); + if (inst.dest != inst.src1) + FMV(32, fpr.R(inst.dest), fpr.R(inst.src1)); + + // First, set SCRATCH1 = clamp to zero, SCRATCH2 = clamp to one. + FCVT(FConv::S, FConv::W, tempReg, R_ZERO); + // FLE here is intentional to convert -0.0 to +0.0. + FLE(32, SCRATCH1, fpr.R(inst.src1), tempReg); + LI(SCRATCH2, 1.0f); + FMV(FMv::W, FMv::X, tempReg, SCRATCH2); + FLT(32, SCRATCH2, tempReg, fpr.R(inst.src1)); + + skipLower = BEQ(SCRATCH1, R_ZERO); + FCVT(FConv::S, FConv::W, fpr.R(inst.dest), R_ZERO); + finishLower = J(); + + SetJumpTarget(skipLower); + skipHigher = BEQ(SCRATCH2, R_ZERO); + // Still has 1.0 in it. + FMV(32, fpr.R(inst.dest), tempReg); + + SetJumpTarget(finishLower); + SetJumpTarget(skipHigher); + break; + + case IROp::FSatMinus1_1: + tempReg = fpr.MapDirtyInTemp(inst.dest, inst.src1); + if (inst.dest != inst.src1) + FMV(32, fpr.R(inst.dest), fpr.R(inst.src1)); + + // First, set SCRATCH1 = clamp to negative, SCRATCH2 = clamp to positive. + LI(SCRATCH2, -1.0f); + FMV(FMv::W, FMv::X, tempReg, SCRATCH2); + FLT(32, SCRATCH1, fpr.R(inst.src1), tempReg); + FNEG(32, tempReg, tempReg); + FLT(32, SCRATCH2, tempReg, fpr.R(inst.src1)); + + // But we can actually do one branch, using sign-injection to keep the original sign. + OR(SCRATCH1, SCRATCH1, SCRATCH2); + + skipLower = BEQ(SCRATCH1, R_ZERO); + FSGNJ(32, fpr.R(inst.dest), tempReg, fpr.R(inst.dest)); + SetJumpTarget(skipLower); + break; + + default: + INVALIDOP; + break; + } +} + +void RiscVJit::CompIR_FCompare(IRInst inst) { + CONDITIONAL_DISABLE; + + constexpr IRRegIndex IRREG_VFPUL_CC = IRREG_VFPU_CTRL_BASE + VFPU_CTRL_CC; + + switch (inst.op) { + case IROp::FCmp: + switch (inst.dest) { + case IRFpCompareMode::False: + gpr.SetImm(IRREG_FPCOND, 0); + break; + + case IRFpCompareMode::EitherUnordered: + fpr.MapInIn(inst.src1, inst.src2); + gpr.MapReg(IRREG_FPCOND, MIPSMap::NOINIT | MIPSMap::MARK_NORM32); + FCLASS(32, SCRATCH1, fpr.R(inst.src1)); + FCLASS(32, SCRATCH2, fpr.R(inst.src2)); + OR(SCRATCH1, SCRATCH1, SCRATCH2); + // NAN is 0x100 or 0x200. + ANDI(SCRATCH1, SCRATCH1, 0x300); + SNEZ(gpr.R(IRREG_FPCOND), SCRATCH1); + break; + + case IRFpCompareMode::EqualOrdered: + fpr.MapInIn(inst.src1, inst.src2); + gpr.MapReg(IRREG_FPCOND, MIPSMap::NOINIT | MIPSMap::MARK_NORM32); + FEQ(32, gpr.R(IRREG_FPCOND), fpr.R(inst.src1), fpr.R(inst.src2)); + break; + + case IRFpCompareMode::EqualUnordered: + fpr.MapInIn(inst.src1, inst.src2); + gpr.MapReg(IRREG_FPCOND, MIPSMap::NOINIT | MIPSMap::MARK_NORM32); + FEQ(32, gpr.R(IRREG_FPCOND), fpr.R(inst.src1), fpr.R(inst.src2)); + + // Now let's just OR in the unordered check. + FCLASS(32, SCRATCH1, fpr.R(inst.src1)); + FCLASS(32, SCRATCH2, fpr.R(inst.src2)); + OR(SCRATCH1, SCRATCH1, SCRATCH2); + // NAN is 0x100 or 0x200. + ANDI(SCRATCH1, SCRATCH1, 0x300); + SNEZ(SCRATCH1, SCRATCH1); + OR(gpr.R(IRREG_FPCOND), gpr.R(IRREG_FPCOND), SCRATCH1); + break; + + case IRFpCompareMode::LessEqualOrdered: + fpr.MapInIn(inst.src1, inst.src2); + gpr.MapReg(IRREG_FPCOND, MIPSMap::NOINIT | MIPSMap::MARK_NORM32); + FLE(32, gpr.R(IRREG_FPCOND), fpr.R(inst.src1), fpr.R(inst.src2)); + break; + + case IRFpCompareMode::LessEqualUnordered: + fpr.MapInIn(inst.src1, inst.src2); + gpr.MapReg(IRREG_FPCOND, MIPSMap::NOINIT | MIPSMap::MARK_NORM32); + FLT(32, gpr.R(IRREG_FPCOND), fpr.R(inst.src2), fpr.R(inst.src1)); + SEQZ(gpr.R(IRREG_FPCOND), gpr.R(IRREG_FPCOND)); + break; + + case IRFpCompareMode::LessOrdered: + fpr.MapInIn(inst.src1, inst.src2); + gpr.MapReg(IRREG_FPCOND, MIPSMap::NOINIT | MIPSMap::MARK_NORM32); + FLT(32, gpr.R(IRREG_FPCOND), fpr.R(inst.src1), fpr.R(inst.src2)); + break; + + case IRFpCompareMode::LessUnordered: + fpr.MapInIn(inst.src1, inst.src2); + gpr.MapReg(IRREG_FPCOND, MIPSMap::NOINIT | MIPSMap::MARK_NORM32); + FLE(32, gpr.R(IRREG_FPCOND), fpr.R(inst.src2), fpr.R(inst.src1)); + SEQZ(gpr.R(IRREG_FPCOND), gpr.R(IRREG_FPCOND)); + break; + } + break; + + case IROp::FCmovVfpuCC: + gpr.MapReg(IRREG_VFPUL_CC); + fpr.MapDirtyIn(inst.dest, inst.src1, false); + if ((inst.src2 & 0xF) == 0) { + ANDI(SCRATCH1, gpr.R(IRREG_VFPUL_CC), 1); + } else if (cpu_info.RiscV_Zbs) { + BEXTI(SCRATCH1, gpr.R(IRREG_VFPUL_CC), inst.src2 & 0xF); + } else { + SRLI(SCRATCH1, gpr.R(IRREG_VFPUL_CC), inst.src2 & 0xF); + ANDI(SCRATCH1, SCRATCH1, 1); + } + if ((inst.src2 >> 7) & 1) { + FixupBranch skip = BEQ(SCRATCH1, R_ZERO); + FMV(32, fpr.R(inst.dest), fpr.R(inst.src1)); + SetJumpTarget(skip); + } else { + FixupBranch skip = BNE(SCRATCH1, R_ZERO); + FMV(32, fpr.R(inst.dest), fpr.R(inst.src1)); + SetJumpTarget(skip); + } + break; + + case IROp::FCmpVfpuBit: + gpr.MapReg(IRREG_VFPUL_CC, MIPSMap::DIRTY); + + switch (VCondition(inst.dest & 0xF)) { + case VC_EQ: + fpr.MapInIn(inst.src1, inst.src2); + FEQ(32, SCRATCH1, fpr.R(inst.src1), fpr.R(inst.src2)); + break; + case VC_NE: + fpr.MapInIn(inst.src1, inst.src2); + // We could almost negate FEQ, except NAN != NAN. + // Anything != NAN is false and NAN != NAN is within that, so we only check one side. + FCLASS(32, SCRATCH2, fpr.R(inst.src2)); + // NAN is 0x100 or 0x200. + ANDI(SCRATCH2, SCRATCH2, 0x300); + SNEZ(SCRATCH2, SCRATCH2); + + FEQ(32, SCRATCH1, fpr.R(inst.src1), fpr.R(inst.src2)); + SEQZ(SCRATCH1, SCRATCH1); + // Just OR in whether that side was a NAN so it's always not equal. + OR(SCRATCH1, SCRATCH1, SCRATCH2); + break; + case VC_LT: + fpr.MapInIn(inst.src1, inst.src2); + FLT(32, SCRATCH1, fpr.R(inst.src1), fpr.R(inst.src2)); + break; + case VC_LE: + fpr.MapInIn(inst.src1, inst.src2); + FLE(32, SCRATCH1, fpr.R(inst.src1), fpr.R(inst.src2)); + break; + case VC_GT: + fpr.MapInIn(inst.src1, inst.src2); + FLT(32, SCRATCH1, fpr.R(inst.src2), fpr.R(inst.src1)); + break; + case VC_GE: + fpr.MapInIn(inst.src1, inst.src2); + FLE(32, SCRATCH1, fpr.R(inst.src2), fpr.R(inst.src1)); + break; + case VC_EZ: + case VC_NZ: + fpr.MapReg(inst.src1); + // Zero is either 0x10 or 0x08. + FCLASS(32, SCRATCH1, fpr.R(inst.src1)); + ANDI(SCRATCH1, SCRATCH1, 0x18); + if ((inst.dest & 4) == 0) + SNEZ(SCRATCH1, SCRATCH1); + else + SEQZ(SCRATCH1, SCRATCH1); + break; + case VC_EN: + case VC_NN: + fpr.MapReg(inst.src1); + // NAN is either 0x100 or 0x200. + FCLASS(32, SCRATCH1, fpr.R(inst.src1)); + ANDI(SCRATCH1, SCRATCH1, 0x300); + if ((inst.dest & 4) == 0) + SNEZ(SCRATCH1, SCRATCH1); + else + SEQZ(SCRATCH1, SCRATCH1); + break; + case VC_EI: + case VC_NI: + fpr.MapReg(inst.src1); + // Infinity is either 0x80 or 0x01. + FCLASS(32, SCRATCH1, fpr.R(inst.src1)); + ANDI(SCRATCH1, SCRATCH1, 0x81); + if ((inst.dest & 4) == 0) + SNEZ(SCRATCH1, SCRATCH1); + else + SEQZ(SCRATCH1, SCRATCH1); + break; + case VC_ES: + case VC_NS: + fpr.MapReg(inst.src1); + // Infinity is either 0x80 or 0x01, NAN is either 0x100 or 0x200. + FCLASS(32, SCRATCH1, fpr.R(inst.src1)); + ANDI(SCRATCH1, SCRATCH1, 0x381); + if ((inst.dest & 4) == 0) + SNEZ(SCRATCH1, SCRATCH1); + else + SEQZ(SCRATCH1, SCRATCH1); + break; + case VC_TR: + LI(SCRATCH1, 1); + break; + case VC_FL: + LI(SCRATCH1, 0); + break; + } + + ANDI(gpr.R(IRREG_VFPUL_CC), gpr.R(IRREG_VFPUL_CC), ~(1 << (inst.dest >> 4))); + if ((inst.dest >> 4) != 0) + SLLI(SCRATCH1, SCRATCH1, inst.dest >> 4); + OR(gpr.R(IRREG_VFPUL_CC), gpr.R(IRREG_VFPUL_CC), SCRATCH1); + break; + + case IROp::FCmpVfpuAggregate: + gpr.MapReg(IRREG_VFPUL_CC, MIPSMap::DIRTY); + ANDI(SCRATCH1, gpr.R(IRREG_VFPUL_CC), inst.dest); + // This is the "any bit", easy. + SNEZ(SCRATCH2, SCRATCH1); + // To compare to inst.dest for "all", let's simply subtract it and compare to zero. + ADDI(SCRATCH1, SCRATCH1, -inst.dest); + SEQZ(SCRATCH1, SCRATCH1); + // Now we combine those together. + SLLI(SCRATCH1, SCRATCH1, 5); + SLLI(SCRATCH2, SCRATCH2, 4); + OR(SCRATCH1, SCRATCH1, SCRATCH2); + + // Reject those any/all bits and replace them with our own. + ANDI(gpr.R(IRREG_VFPUL_CC), gpr.R(IRREG_VFPUL_CC), ~0x30); + OR(gpr.R(IRREG_VFPUL_CC), gpr.R(IRREG_VFPUL_CC), SCRATCH1); + break; + + default: + INVALIDOP; + break; + } +} + +void RiscVJit::CompIR_RoundingMode(IRInst inst) { + CONDITIONAL_DISABLE; + + switch (inst.op) { + case IROp::RestoreRoundingMode: + RestoreRoundingMode(); + break; + + case IROp::ApplyRoundingMode: + ApplyRoundingMode(); + break; + + case IROp::UpdateRoundingMode: + // We don't need to do anything, instructions allow a "dynamic" rounding mode. + break; + + default: + INVALIDOP; + break; + } +} + +void RiscVJit::CompIR_FSpecial(IRInst inst) { + CONDITIONAL_DISABLE; + +#ifdef __riscv_float_abi_soft +#error Currently hard float is required. +#endif + + auto callFuncF_F = [&](float (*func)(float)){ + gpr.FlushBeforeCall(); + fpr.FlushBeforeCall(); + // It might be in a non-volatile register. + if (fpr.IsMapped(inst.src1)) { + FMV(32, F10, fpr.R(inst.src1)); + } else { + int offset = offsetof(MIPSState, f) + inst.src1 * 4; + FL(32, F10, CTXREG, offset); + } + QuickCallFunction(func); + + fpr.MapReg(inst.dest, MIPSMap::NOINIT); + // If it's already F10, we're done - MapReg doesn't actually overwrite the reg in that case. + if (fpr.R(inst.dest) != F10) { + FMV(32, fpr.R(inst.dest), F10); + } + }; + + RiscVReg tempReg = INVALID_REG; + switch (inst.op) { + case IROp::FSin: + callFuncF_F(&vfpu_sin); + break; + + case IROp::FCos: + callFuncF_F(&vfpu_cos); + break; + + case IROp::FRSqrt: + tempReg = fpr.MapDirtyInTemp(inst.dest, inst.src1); + FSQRT(32, fpr.R(inst.dest), fpr.R(inst.src1)); + + // Ugh, we can't really avoid a temp here. Probably not worth a permanent one. + LI(SCRATCH1, 1.0f); + FMV(FMv::W, FMv::X, tempReg, SCRATCH1); + FDIV(32, fpr.R(inst.dest), tempReg, fpr.R(inst.dest)); + break; + + case IROp::FRecip: + if (inst.dest != inst.src1) { + // This is the easy case. + fpr.MapDirtyIn(inst.dest, inst.src1); + LI(SCRATCH1, 1.0f); + FMV(FMv::W, FMv::X, fpr.R(inst.dest), SCRATCH1); + FDIV(32, fpr.R(inst.dest), fpr.R(inst.dest), fpr.R(inst.src1)); + } else { + tempReg = fpr.MapDirtyInTemp(inst.dest, inst.src1); + LI(SCRATCH1, 1.0f); + FMV(FMv::W, FMv::X, tempReg, SCRATCH1); + FDIV(32, fpr.R(inst.dest), tempReg, fpr.R(inst.src1)); + } + break; + + case IROp::FAsin: + callFuncF_F(&vfpu_asin); + break; + + default: + INVALIDOP; + break; + } +} + +} // namespace MIPSComp diff --git a/Core/MIPS/RiscV/RiscVCompLoadStore.cpp b/Core/MIPS/RiscV/RiscVCompLoadStore.cpp new file mode 100644 index 0000000000..d6a49e6f13 --- /dev/null +++ b/Core/MIPS/RiscV/RiscVCompLoadStore.cpp @@ -0,0 +1,386 @@ +// Copyright (c) 2023- PPSSPP Project. + +// 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, version 2.0 or later versions. + +// 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 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#include "Core/MemMap.h" +#include "Core/MIPS/RiscV/RiscVJit.h" +#include "Core/MIPS/RiscV/RiscVRegCache.h" + +// This file contains compilation for load/store instructions. +// +// All functions should have CONDITIONAL_DISABLE, so we can narrow things down to a file quickly. +// Currently known non working ones should have DISABLE. No flags because that's in IR already. + +// #define CONDITIONAL_DISABLE { CompIR_Generic(inst); return; } +#define CONDITIONAL_DISABLE {} +#define DISABLE { CompIR_Generic(inst); return; } +#define INVALIDOP { _assert_msg_(false, "Invalid IR inst %d", (int)inst.op); CompIR_Generic(inst); return; } + +namespace MIPSComp { + +using namespace RiscVGen; +using namespace RiscVJitConstants; + +void RiscVJit::SetScratch1ToSrc1Address(IRReg src1) { + gpr.MapReg(src1); +#ifdef MASKED_PSP_MEMORY + SLLIW(SCRATCH1, gpr.R(src1), 2); + SRLIW(SCRATCH1, SCRATCH1, 2); + ADD(SCRATCH1, SCRATCH1, MEMBASEREG); +#else + // Clear the top bits to be safe. + if (cpu_info.RiscV_Zba) { + ADD_UW(SCRATCH1, gpr.R(src1), MEMBASEREG); + } else { + _assert_(XLEN == 64); + SLLI(SCRATCH1, gpr.R(src1), 32); + SRLI(SCRATCH1, SCRATCH1, 32); + ADD(SCRATCH1, SCRATCH1, MEMBASEREG); + } +#endif +} + +int32_t RiscVJit::AdjustForAddressOffset(RiscVGen::RiscVReg *reg, int32_t constant, int32_t range) { + if (constant < -2048 || constant + range > 2047) { + LI(SCRATCH2, constant); + ADD(SCRATCH1, *reg, SCRATCH2); + *reg = SCRATCH1; + return 0; + } + return constant; +} + +void RiscVJit::CompIR_Load(IRInst inst) { + CONDITIONAL_DISABLE; + + gpr.SpillLock(inst.dest, inst.src1); + RiscVReg addrReg = INVALID_REG; + if (inst.src1 == MIPS_REG_ZERO) { + // This will get changed by AdjustForAddressOffset. + addrReg = MEMBASEREG; +#ifdef MASKED_PSP_MEMORY + inst.constant &= Memory::MEMVIEW32_MASK; +#endif + } else if (jo.cachePointers || gpr.IsMappedAsPointer(inst.src1)) { + addrReg = gpr.MapRegAsPointer(inst.src1); + } else { + SetScratch1ToSrc1Address(inst.src1); + addrReg = SCRATCH1; + } + // If they're the same, MapReg may subtract MEMBASEREG, so just mark dirty. + if (inst.dest == inst.src1) + gpr.MarkDirty(gpr.R(inst.dest), true); + else + gpr.MapReg(inst.dest, MIPSMap::NOINIT | MIPSMap::MARK_NORM32); + gpr.ReleaseSpillLock(inst.dest, inst.src1); + + s32 imm = AdjustForAddressOffset(&addrReg, inst.constant); + + // TODO: Safe memory? Or enough to have crash handler + validate? + + switch (inst.op) { + case IROp::Load8: + LBU(gpr.R(inst.dest), addrReg, imm); + break; + + case IROp::Load8Ext: + LB(gpr.R(inst.dest), addrReg, imm); + break; + + case IROp::Load16: + LHU(gpr.R(inst.dest), addrReg, imm); + break; + + case IROp::Load16Ext: + LH(gpr.R(inst.dest), addrReg, imm); + break; + + case IROp::Load32: + LW(gpr.R(inst.dest), addrReg, imm); + break; + + case IROp::Load32Linked: + if (inst.dest != MIPS_REG_ZERO) + LW(gpr.R(inst.dest), addrReg, imm); + gpr.SetImm(IRREG_LLBIT, 1); + break; + + default: + INVALIDOP; + break; + } +} + +void RiscVJit::CompIR_LoadShift(IRInst inst) { + CONDITIONAL_DISABLE; + + switch (inst.op) { + case IROp::Load32Left: + case IROp::Load32Right: + // Should not happen if the pass to split is active. + DISABLE; + break; + + default: + INVALIDOP; + break; + } +} + +void RiscVJit::CompIR_FLoad(IRInst inst) { + CONDITIONAL_DISABLE; + + RiscVReg addrReg = INVALID_REG; + if (inst.src1 == MIPS_REG_ZERO) { + // This will get changed by AdjustForAddressOffset. + addrReg = MEMBASEREG; +#ifdef MASKED_PSP_MEMORY + inst.constant &= Memory::MEMVIEW32_MASK; +#endif + } else if (jo.cachePointers || gpr.IsMappedAsPointer(inst.src1)) { + addrReg = gpr.MapRegAsPointer(inst.src1); + } else { + SetScratch1ToSrc1Address(inst.src1); + addrReg = SCRATCH1; + } + + s32 imm = AdjustForAddressOffset(&addrReg, inst.constant); + + // TODO: Safe memory? Or enough to have crash handler + validate? + + switch (inst.op) { + case IROp::LoadFloat: + fpr.MapReg(inst.dest, MIPSMap::NOINIT); + FL(32, fpr.R(inst.dest), addrReg, imm); + break; + + default: + INVALIDOP; + break; + } +} + +void RiscVJit::CompIR_VecLoad(IRInst inst) { + CONDITIONAL_DISABLE; + + RiscVReg addrReg = INVALID_REG; + if (inst.src1 == MIPS_REG_ZERO) { + // This will get changed by AdjustForAddressOffset. + addrReg = MEMBASEREG; +#ifdef MASKED_PSP_MEMORY + inst.constant &= Memory::MEMVIEW32_MASK; +#endif + } else if (jo.cachePointers || gpr.IsMappedAsPointer(inst.src1)) { + addrReg = gpr.MapRegAsPointer(inst.src1); + } else { + SetScratch1ToSrc1Address(inst.src1); + addrReg = SCRATCH1; + } + + // We need to be able to address the whole 16 bytes, so offset of 12. + s32 imm = AdjustForAddressOffset(&addrReg, inst.constant, 12); + + // TODO: Safe memory? Or enough to have crash handler + validate? + + switch (inst.op) { + case IROp::LoadVec4: + for (int i = 0; i < 4; ++i) { + // Spilling is okay. + fpr.MapReg(inst.dest + i, MIPSMap::NOINIT); + FL(32, fpr.R(inst.dest + i), addrReg, imm + 4 * i); + } + break; + + default: + INVALIDOP; + break; + } +} + +void RiscVJit::CompIR_Store(IRInst inst) { + CONDITIONAL_DISABLE; + + gpr.SpillLock(inst.src3, inst.src1); + RiscVReg addrReg = INVALID_REG; + if (inst.src1 == MIPS_REG_ZERO) { + // This will get changed by AdjustForAddressOffset. + addrReg = MEMBASEREG; +#ifdef MASKED_PSP_MEMORY + inst.constant &= Memory::MEMVIEW32_MASK; +#endif + } else if ((jo.cachePointers || gpr.IsMappedAsPointer(inst.src1)) && inst.src3 != inst.src1) { + addrReg = gpr.MapRegAsPointer(inst.src1); + } else { + SetScratch1ToSrc1Address(inst.src1); + addrReg = SCRATCH1; + } + RiscVReg valueReg = gpr.TryMapTempImm(inst.src3); + if (valueReg == INVALID_REG) + valueReg = gpr.MapReg(inst.src3); + gpr.ReleaseSpillLock(inst.src3, inst.src1); + + s32 imm = AdjustForAddressOffset(&addrReg, inst.constant); + + // TODO: Safe memory? Or enough to have crash handler + validate? + + switch (inst.op) { + case IROp::Store8: + SB(valueReg, addrReg, imm); + break; + + case IROp::Store16: + SH(valueReg, addrReg, imm); + break; + + case IROp::Store32: + SW(valueReg, addrReg, imm); + break; + + default: + INVALIDOP; + break; + } +} + +void RiscVJit::CompIR_CondStore(IRInst inst) { + CONDITIONAL_DISABLE; + if (inst.op != IROp::Store32Conditional) + INVALIDOP; + + gpr.SpillLock(IRREG_LLBIT, inst.src3, inst.src1); + RiscVReg addrReg = INVALID_REG; + if (inst.src1 == MIPS_REG_ZERO) { + // This will get changed by AdjustForAddressOffset. + addrReg = MEMBASEREG; +#ifdef MASKED_PSP_MEMORY + inst.constant &= Memory::MEMVIEW32_MASK; +#endif + } else if ((jo.cachePointers || gpr.IsMappedAsPointer(inst.src1)) && inst.src3 != inst.src1) { + addrReg = gpr.MapRegAsPointer(inst.src1); + } else { + SetScratch1ToSrc1Address(inst.src1); + addrReg = SCRATCH1; + } + gpr.MapReg(inst.src3, inst.dest == MIPS_REG_ZERO ? MIPSMap::INIT : MIPSMap::DIRTY); + gpr.MapReg(IRREG_LLBIT); + gpr.ReleaseSpillLock(IRREG_LLBIT, inst.src3, inst.src1); + + s32 imm = AdjustForAddressOffset(&addrReg, inst.constant); + + // TODO: Safe memory? Or enough to have crash handler + validate? + + FixupBranch condFailed = BEQ(gpr.R(IRREG_LLBIT), R_ZERO); + SW(gpr.R(inst.src3), addrReg, imm); + + if (inst.dest != MIPS_REG_ZERO) { + LI(gpr.R(inst.dest), 1); + FixupBranch finish = J(); + + SetJumpTarget(condFailed); + LI(gpr.R(inst.dest), 0); + SetJumpTarget(finish); + } else { + SetJumpTarget(condFailed); + } +} + +void RiscVJit::CompIR_StoreShift(IRInst inst) { + CONDITIONAL_DISABLE; + + switch (inst.op) { + case IROp::Store32Left: + case IROp::Store32Right: + // Should not happen if the pass to split is active. + DISABLE; + break; + + default: + INVALIDOP; + break; + } +} + +void RiscVJit::CompIR_FStore(IRInst inst) { + CONDITIONAL_DISABLE; + + RiscVReg addrReg = INVALID_REG; + if (inst.src1 == MIPS_REG_ZERO) { + // This will get changed by AdjustForAddressOffset. + addrReg = MEMBASEREG; +#ifdef MASKED_PSP_MEMORY + inst.constant &= Memory::MEMVIEW32_MASK; +#endif + } else if (jo.cachePointers || gpr.IsMappedAsPointer(inst.src1)) { + addrReg = gpr.MapRegAsPointer(inst.src1); + } else { + SetScratch1ToSrc1Address(inst.src1); + addrReg = SCRATCH1; + } + + s32 imm = AdjustForAddressOffset(&addrReg, inst.constant); + + // TODO: Safe memory? Or enough to have crash handler + validate? + + switch (inst.op) { + case IROp::StoreFloat: + fpr.MapReg(inst.src3); + FS(32, fpr.R(inst.src3), addrReg, imm); + break; + + default: + INVALIDOP; + break; + } +} + +void RiscVJit::CompIR_VecStore(IRInst inst) { + CONDITIONAL_DISABLE; + + RiscVReg addrReg = INVALID_REG; + if (inst.src1 == MIPS_REG_ZERO) { + // This will get changed by AdjustForAddressOffset. + addrReg = MEMBASEREG; +#ifdef MASKED_PSP_MEMORY + inst.constant &= Memory::MEMVIEW32_MASK; +#endif + } else if (jo.cachePointers || gpr.IsMappedAsPointer(inst.src1)) { + addrReg = gpr.MapRegAsPointer(inst.src1); + } else { + SetScratch1ToSrc1Address(inst.src1); + addrReg = SCRATCH1; + } + + // We need to be able to address the whole 16 bytes, so offset of 12. + s32 imm = AdjustForAddressOffset(&addrReg, inst.constant, 12); + + // TODO: Safe memory? Or enough to have crash handler + validate? + + switch (inst.op) { + case IROp::StoreVec4: + for (int i = 0; i < 4; ++i) { + // Spilling is okay, though not ideal. + fpr.MapReg(inst.src3 + i); + FS(32, fpr.R(inst.src3 + i), addrReg, imm + 4 * i); + } + break; + + default: + INVALIDOP; + break; + } +} + +} // namespace MIPSComp diff --git a/Core/MIPS/RiscV/RiscVCompSystem.cpp b/Core/MIPS/RiscV/RiscVCompSystem.cpp new file mode 100644 index 0000000000..727465fad0 --- /dev/null +++ b/Core/MIPS/RiscV/RiscVCompSystem.cpp @@ -0,0 +1,271 @@ +// Copyright (c) 2023- PPSSPP Project. + +// 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, version 2.0 or later versions. + +// 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 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#include "Common/Profiler/Profiler.h" +#include "Core/Core.h" +#include "Core/HLE/HLE.h" +#include "Core/HLE/ReplaceTables.h" +#include "Core/MemMap.h" +#include "Core/MIPS/RiscV/RiscVJit.h" +#include "Core/MIPS/RiscV/RiscVRegCache.h" + +// This file contains compilation for basic PC/downcount accounting, syscalls, debug funcs, etc. +// +// All functions should have CONDITIONAL_DISABLE, so we can narrow things down to a file quickly. +// Currently known non working ones should have DISABLE. No flags because that's in IR already. + +// #define CONDITIONAL_DISABLE { CompIR_Generic(inst); return; } +#define CONDITIONAL_DISABLE {} +#define DISABLE { CompIR_Generic(inst); return; } +#define INVALIDOP { _assert_msg_(false, "Invalid IR inst %d", (int)inst.op); CompIR_Generic(inst); return; } + +namespace MIPSComp { + +using namespace RiscVGen; +using namespace RiscVJitConstants; + +void RiscVJit::CompIR_Basic(IRInst inst) { + CONDITIONAL_DISABLE; + + switch (inst.op) { + case IROp::SetConst: + // Sign extend all constants. We get 0xFFFFFFFF sometimes, and it's more work to truncate. + // The register only holds 32 bits in the end anyway. + gpr.SetImm(inst.dest, (int32_t)inst.constant); + break; + + case IROp::SetConstF: + fpr.MapReg(inst.dest, MIPSMap::NOINIT); + if (inst.constant == 0) { + FCVT(FConv::S, FConv::W, fpr.R(inst.dest), R_ZERO); + } else { + // TODO: In the future, could use FLI if it's approved. + // Also, is FCVT faster? + LI(SCRATCH1, (int32_t)inst.constant); + FMV(FMv::W, FMv::X, fpr.R(inst.dest), SCRATCH1); + } + break; + + case IROp::Downcount: + if (inst.constant <= 2048) { + ADDI(DOWNCOUNTREG, DOWNCOUNTREG, -(s32)inst.constant); + } else { + LI(SCRATCH1, inst.constant, SCRATCH2); + SUB(DOWNCOUNTREG, DOWNCOUNTREG, SCRATCH1); + } + break; + + case IROp::SetPC: + gpr.MapIn(inst.src1); + MovToPC(gpr.R(inst.src1)); + break; + + case IROp::SetPCConst: + LI(SCRATCH1, inst.constant, SCRATCH2); + MovToPC(SCRATCH1); + break; + + default: + INVALIDOP; + break; + } +} + +void RiscVJit::CompIR_Transfer(IRInst inst) { + CONDITIONAL_DISABLE; + + switch (inst.op) { + case IROp::SetCtrlVFPU: + gpr.SetImm(IRREG_VFPU_CTRL_BASE + inst.dest, (int32_t)inst.constant); + break; + + case IROp::SetCtrlVFPUReg: + gpr.MapDirtyIn(IRREG_VFPU_CTRL_BASE + inst.dest, inst.src1); + MV(gpr.R(IRREG_VFPU_CTRL_BASE + inst.dest), gpr.R(inst.src1)); + gpr.MarkDirty(gpr.R(IRREG_VFPU_CTRL_BASE + inst.dest), gpr.IsNormalized32(inst.src1)); + break; + + case IROp::SetCtrlVFPUFReg: + gpr.MapReg(IRREG_VFPU_CTRL_BASE + inst.dest, MIPSMap::NOINIT | MIPSMap::MARK_NORM32); + fpr.MapReg(inst.src1); + FMV(FMv::X, FMv::W, gpr.R(IRREG_VFPU_CTRL_BASE + inst.dest), fpr.R(inst.src1)); + break; + + case IROp::FpCondToReg: + gpr.MapDirtyIn(inst.dest, IRREG_FPCOND); + MV(gpr.R(inst.dest), gpr.R(IRREG_FPCOND)); + gpr.MarkDirty(gpr.R(inst.dest), gpr.IsNormalized32(IRREG_FPCOND)); + break; + + case IROp::ZeroFpCond: + gpr.SetImm(IRREG_FPCOND, 0); + break; + + case IROp::FpCtrlFromReg: + gpr.MapDirtyIn(IRREG_FPCOND, inst.src1, MapType::AVOID_LOAD_MARK_NORM32); + LI(SCRATCH1, 0x0181FFFF); + AND(SCRATCH1, gpr.R(inst.src1), SCRATCH1); + // Extract the new fpcond value. + if (cpu_info.RiscV_Zbs) { + BEXTI(gpr.R(IRREG_FPCOND), SCRATCH1, 23); + } else { + SRLI(gpr.R(IRREG_FPCOND), SCRATCH1, 23); + ANDI(gpr.R(IRREG_FPCOND), gpr.R(IRREG_FPCOND), 1); + } + SW(SCRATCH1, CTXREG, IRREG_FCR31 * 4); + break; + + case IROp::FpCtrlToReg: + gpr.MapDirtyIn(inst.dest, IRREG_FPCOND, MapType::AVOID_LOAD_MARK_NORM32); + // Load fcr31 and clear the fpcond bit. + LW(SCRATCH1, CTXREG, IRREG_FCR31 * 4); + if (cpu_info.RiscV_Zbs) { + BCLRI(SCRATCH1, SCRATCH1, 23); + } else { + LI(SCRATCH2, ~(1 << 23)); + AND(SCRATCH1, SCRATCH1, SCRATCH2); + } + + // Now get the correct fpcond bit. + ANDI(SCRATCH2, gpr.R(IRREG_FPCOND), 1); + SLLI(SCRATCH2, SCRATCH2, 23); + OR(gpr.R(inst.dest), SCRATCH1, SCRATCH2); + + // Also update mips->fcr31 while we're here. + SW(gpr.R(inst.dest), CTXREG, IRREG_FCR31 * 4); + break; + + case IROp::VfpuCtrlToReg: + gpr.MapDirtyIn(inst.dest, IRREG_VFPU_CTRL_BASE + inst.src1); + MV(gpr.R(inst.dest), gpr.R(IRREG_VFPU_CTRL_BASE + inst.src1)); + gpr.MarkDirty(gpr.R(inst.dest), gpr.IsNormalized32(IRREG_VFPU_CTRL_BASE + inst.src1)); + break; + + case IROp::FMovFromGPR: + fpr.MapReg(inst.dest, MIPSMap::NOINIT); + if (gpr.IsImm(inst.src1) && gpr.GetImm(inst.src1) == 0) { + FCVT(FConv::S, FConv::W, fpr.R(inst.dest), R_ZERO); + } else { + gpr.MapReg(inst.src1); + FMV(FMv::W, FMv::X, fpr.R(inst.dest), gpr.R(inst.src1)); + } + break; + + case IROp::FMovToGPR: + gpr.MapReg(inst.dest, MIPSMap::NOINIT | MIPSMap::MARK_NORM32); + fpr.MapReg(inst.src1); + FMV(FMv::X, FMv::W, gpr.R(inst.dest), fpr.R(inst.src1)); + break; + + default: + INVALIDOP; + break; + } +} + +void RiscVJit::CompIR_System(IRInst inst) { + CONDITIONAL_DISABLE; + + switch (inst.op) { + case IROp::Syscall: + FlushAll(); + SaveStaticRegisters(); + +#ifdef USE_PROFILER + // When profiling, we can't skip CallSyscall, since it times syscalls. + LI(X10, (int32_t)inst.constant); + QuickCallFunction(&CallSyscall); +#else + // Skip the CallSyscall where possible. + { + MIPSOpcode op(inst.constant); + void *quickFunc = GetQuickSyscallFunc(op); + if (quickFunc) { + LI(X10, (uintptr_t)GetSyscallFuncPointer(op)); + QuickCallFunction((const u8 *)quickFunc); + } else { + LI(X10, (int32_t)inst.constant); + QuickCallFunction(&CallSyscall); + } + } +#endif + + LoadStaticRegisters(); + // This is always followed by an ExitToPC, where we check coreState. + break; + + case IROp::CallReplacement: + FlushAll(); + SaveStaticRegisters(); + QuickCallFunction(GetReplacementFunc(inst.constant)->replaceFunc); + LoadStaticRegisters(); + SUB(DOWNCOUNTREG, DOWNCOUNTREG, X10); + break; + + case IROp::Break: + FlushAll(); + // This doesn't naturally have restore/apply around it. + RestoreRoundingMode(true); + SaveStaticRegisters(); + MovFromPC(X10); + QuickCallFunction(&Core_Break); + LoadStaticRegisters(); + ApplyRoundingMode(true); + MovFromPC(SCRATCH1); + ADDI(SCRATCH1, SCRATCH1, 4); + QuickJ(R_RA, dispatcherPCInSCRATCH1_); + break; + + default: + INVALIDOP; + break; + } +} + +void RiscVJit::CompIR_Breakpoint(IRInst inst) { + CONDITIONAL_DISABLE; + + switch (inst.op) { + case IROp::Breakpoint: + case IROp::MemoryCheck: + CompIR_Generic(inst); + break; + + default: + INVALIDOP; + break; + } +} + +void RiscVJit::CompIR_ValidateAddress(IRInst inst) { + CONDITIONAL_DISABLE; + + switch (inst.op) { + case IROp::ValidateAddress8: + case IROp::ValidateAddress16: + case IROp::ValidateAddress32: + case IROp::ValidateAddress128: + CompIR_Generic(inst); + break; + + default: + INVALIDOP; + break; + } +} + +} // namespace MIPSComp diff --git a/Core/MIPS/RiscV/RiscVCompVec.cpp b/Core/MIPS/RiscV/RiscVCompVec.cpp new file mode 100644 index 0000000000..8140f2b856 --- /dev/null +++ b/Core/MIPS/RiscV/RiscVCompVec.cpp @@ -0,0 +1,284 @@ +// Copyright (c) 2023- PPSSPP Project. + +// 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, version 2.0 or later versions. + +// 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 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#include "Core/MemMap.h" +#include "Core/MIPS/RiscV/RiscVJit.h" +#include "Core/MIPS/RiscV/RiscVRegCache.h" + +// This file contains compilation for vector instructions. +// +// All functions should have CONDITIONAL_DISABLE, so we can narrow things down to a file quickly. +// Currently known non working ones should have DISABLE. No flags because that's in IR already. + +// #define CONDITIONAL_DISABLE { CompIR_Generic(inst); return; } +#define CONDITIONAL_DISABLE {} +#define DISABLE { CompIR_Generic(inst); return; } +#define INVALIDOP { _assert_msg_(false, "Invalid IR inst %d", (int)inst.op); CompIR_Generic(inst); return; } + +namespace MIPSComp { + +using namespace RiscVGen; +using namespace RiscVJitConstants; + +void RiscVJit::CompIR_VecAssign(IRInst inst) { + CONDITIONAL_DISABLE; + + switch (inst.op) { + case IROp::Vec4Init: + for (int i = 0; i < 4; ++i) + fpr.SpillLock(inst.dest + i); + for (int i = 0; i < 4; ++i) + fpr.MapReg(inst.dest + i, MIPSMap::NOINIT); + for (int i = 0; i < 4; ++i) + fpr.ReleaseSpillLock(inst.dest + i); + + // TODO: Check if FCVT/FMV/FL is better. + switch ((Vec4Init)inst.src1) { + case Vec4Init::AllZERO: + for (int i = 0; i < 4; ++i) + FCVT(FConv::S, FConv::W, fpr.R(inst.dest + i), R_ZERO); + break; + + case Vec4Init::AllONE: + LI(SCRATCH1, 1.0f); + FMV(FMv::W, FMv::X, fpr.R(inst.dest), SCRATCH1); + for (int i = 1; i < 4; ++i) + FMV(32, fpr.R(inst.dest + i), fpr.R(inst.dest)); + break; + + case Vec4Init::AllMinusONE: + LI(SCRATCH1, -1.0f); + FMV(FMv::W, FMv::X, fpr.R(inst.dest), SCRATCH1); + for (int i = 1; i < 4; ++i) + FMV(32, fpr.R(inst.dest + i), fpr.R(inst.dest)); + break; + + case Vec4Init::Set_1000: + LI(SCRATCH1, 1.0f); + for (int i = 0; i < 4; ++i) { + if (i == 0) + FMV(FMv::W, FMv::X, fpr.R(inst.dest + i), SCRATCH1); + else + FCVT(FConv::S, FConv::W, fpr.R(inst.dest + i), R_ZERO); + } + break; + + case Vec4Init::Set_0100: + LI(SCRATCH1, 1.0f); + for (int i = 0; i < 4; ++i) { + if (i == 1) + FMV(FMv::W, FMv::X, fpr.R(inst.dest + i), SCRATCH1); + else + FCVT(FConv::S, FConv::W, fpr.R(inst.dest + i), R_ZERO); + } + break; + + case Vec4Init::Set_0010: + LI(SCRATCH1, 1.0f); + for (int i = 0; i < 4; ++i) { + if (i == 2) + FMV(FMv::W, FMv::X, fpr.R(inst.dest + i), SCRATCH1); + else + FCVT(FConv::S, FConv::W, fpr.R(inst.dest + i), R_ZERO); + } + break; + + case Vec4Init::Set_0001: + LI(SCRATCH1, 1.0f); + for (int i = 0; i < 4; ++i) { + if (i == 3) + FMV(FMv::W, FMv::X, fpr.R(inst.dest + i), SCRATCH1); + else + FCVT(FConv::S, FConv::W, fpr.R(inst.dest + i), R_ZERO); + } + break; + } + break; + + case IROp::Vec4Shuffle: + fpr.Map4DirtyIn(inst.dest, inst.src1); + for (int i = 0; i < 4; ++i) { + int lane = (inst.src2 >> (i * 2)) & 3; + FMV(32, fpr.R(inst.dest + i), fpr.R(inst.src1 + lane)); + } + break; + + case IROp::Vec4Mov: + fpr.Map4DirtyIn(inst.dest, inst.src1); + for (int i = 0; i < 4; ++i) + FMV(32, fpr.R(inst.dest + i), fpr.R(inst.src1 + i)); + break; + + default: + INVALIDOP; + break; + } +} + +void RiscVJit::CompIR_VecArith(IRInst inst) { + CONDITIONAL_DISABLE; + + switch (inst.op) { + case IROp::Vec4Add: + fpr.Map4DirtyInIn(inst.dest, inst.src1, inst.src2); + for (int i = 0; i < 4; ++i) + FADD(32, fpr.R(inst.dest + i), fpr.R(inst.src1 + i), fpr.R(inst.src2 + i)); + break; + + case IROp::Vec4Sub: + fpr.Map4DirtyInIn(inst.dest, inst.src1, inst.src2); + for (int i = 0; i < 4; ++i) + FSUB(32, fpr.R(inst.dest + i), fpr.R(inst.src1 + i), fpr.R(inst.src2 + i)); + break; + + case IROp::Vec4Mul: + fpr.Map4DirtyInIn(inst.dest, inst.src1, inst.src2); + for (int i = 0; i < 4; ++i) + FMUL(32, fpr.R(inst.dest + i), fpr.R(inst.src1 + i), fpr.R(inst.src2 + i)); + break; + + case IROp::Vec4Div: + fpr.Map4DirtyInIn(inst.dest, inst.src1, inst.src2); + for (int i = 0; i < 4; ++i) + FDIV(32, fpr.R(inst.dest + i), fpr.R(inst.src1 + i), fpr.R(inst.src2 + i)); + break; + + case IROp::Vec4Scale: + fpr.SpillLock(inst.src2); + fpr.MapReg(inst.src2); + fpr.Map4DirtyIn(inst.dest, inst.src1); + fpr.ReleaseSpillLock(inst.src2); + for (int i = 0; i < 4; ++i) + FMUL(32, fpr.R(inst.dest + i), fpr.R(inst.src1 + i), fpr.R(inst.src2)); + break; + + case IROp::Vec4Neg: + fpr.Map4DirtyIn(inst.dest, inst.src1); + for (int i = 0; i < 4; ++i) + FNEG(32, fpr.R(inst.dest + i), fpr.R(inst.src1 + i)); + break; + + case IROp::Vec4Abs: + fpr.Map4DirtyIn(inst.dest, inst.src1); + for (int i = 0; i < 4; ++i) + FABS(32, fpr.R(inst.dest + i), fpr.R(inst.src1 + i)); + break; + + default: + INVALIDOP; + break; + } +} + +void RiscVJit::CompIR_VecHoriz(IRInst inst) { + CONDITIONAL_DISABLE; + + switch (inst.op) { + case IROp::Vec4Dot: + // TODO: Maybe some option to call the slow accurate mode? + fpr.SpillLock(inst.dest); + for (int i = 0; i < 4; ++i) { + fpr.SpillLock(inst.src1 + i); + fpr.SpillLock(inst.src2 + i); + } + for (int i = 0; i < 4; ++i) { + fpr.MapReg(inst.src1 + i); + fpr.MapReg(inst.src2 + i); + } + fpr.MapReg(inst.dest, MIPSMap::NOINIT); + for (int i = 0; i < 4; ++i) { + fpr.ReleaseSpillLock(inst.src1 + i); + fpr.ReleaseSpillLock(inst.src2 + i); + } + fpr.ReleaseSpillLock(inst.dest); + + if ((inst.dest < inst.src1 + 4 && inst.dest >= inst.src1) || (inst.dest < inst.src2 + 4 && inst.dest >= inst.src2)) { + // This means inst.dest overlaps one of src1 or src2. We have to do that one first. + // Technically this may impact -0.0 and such, but dots accurately need to be aligned anyway. + for (int i = 0; i < 4; ++i) { + if (inst.dest == inst.src1 + i || inst.dest == inst.src2 + i) + FMUL(32, fpr.R(inst.dest), fpr.R(inst.src1 + i), fpr.R(inst.src2 + i)); + } + for (int i = 0; i < 4; ++i) { + if (inst.dest != inst.src1 + i && inst.dest != inst.src2 + i) + FMADD(32, fpr.R(inst.dest), fpr.R(inst.src1 + i), fpr.R(inst.src2 + i), fpr.R(inst.dest)); + } + } else { + FMUL(32, fpr.R(inst.dest), fpr.R(inst.src1), fpr.R(inst.src2)); + for (int i = 1; i < 4; ++i) + FMADD(32, fpr.R(inst.dest), fpr.R(inst.src1 + i), fpr.R(inst.src2 + i), fpr.R(inst.dest)); + } + break; + + default: + INVALIDOP; + break; + } +} + +void RiscVJit::CompIR_VecPack(IRInst inst) { + CONDITIONAL_DISABLE; + + switch (inst.op) { + case IROp::Vec2Unpack16To31: + case IROp::Vec2Unpack16To32: + case IROp::Vec4Unpack8To32: + case IROp::Vec4DuplicateUpperBitsAndShift1: + case IROp::Vec4Pack31To8: + case IROp::Vec4Pack32To8: + case IROp::Vec2Pack31To16: + CompIR_Generic(inst); + break; + + case IROp::Vec2Pack32To16: + fpr.MapDirtyInIn(inst.dest, inst.src1, inst.src1 + 1); + FMV(FMv::X, FMv::W, SCRATCH1, fpr.R(inst.src1)); + FMV(FMv::X, FMv::W, SCRATCH2, fpr.R(inst.src1 + 1)); + // Keep in mind, this was sign-extended, so we have to zero the upper. + SLLI(SCRATCH1, SCRATCH1, XLEN - 32); + // Now we just set (SCRATCH2 & 0xFFFF0000) | SCRATCH1. + SRLI(SCRATCH1, SCRATCH1, XLEN - 16); + // Use a wall to mask. We can ignore the upper 32 here. + SRLI(SCRATCH2, SCRATCH2, 16); + SLLI(SCRATCH2, SCRATCH2, 16); + OR(SCRATCH1, SCRATCH1, SCRATCH2); + // Okay, to the floating point register. + FMV(FMv::W, FMv::X, fpr.R(inst.dest), SCRATCH1); + break; + + default: + INVALIDOP; + break; + } +} + +void RiscVJit::CompIR_VecClamp(IRInst inst) { + CONDITIONAL_DISABLE; + + switch (inst.op) { + case IROp::Vec4ClampToZero: + case IROp::Vec2ClampToZero: + CompIR_Generic(inst); + break; + + default: + INVALIDOP; + break; + } +} + +} // namespace MIPSComp diff --git a/Core/MIPS/RiscV/RiscVJit.cpp b/Core/MIPS/RiscV/RiscVJit.cpp new file mode 100644 index 0000000000..04e0c23bf3 --- /dev/null +++ b/Core/MIPS/RiscV/RiscVJit.cpp @@ -0,0 +1,699 @@ +// Copyright (c) 2023- PPSSPP Project. + +// 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, version 2.0 or later versions. + +// 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 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#include "Common/StringUtils.h" +#include "Common/TimeUtil.h" +#include "Core/MemMap.h" +#include "Core/MIPS/MIPSTables.h" +#include "Core/MIPS/RiscV/RiscVJit.h" +#include "Core/MIPS/RiscV/RiscVRegCache.h" +#include "Common/Profiler/Profiler.h" + +namespace MIPSComp { + +using namespace RiscVGen; +using namespace RiscVJitConstants; + +static constexpr bool enableDebug = false; + +static std::map debugSeenNotCompiledIR; +static std::map debugSeenNotCompiled; +double lastDebugLog = 0.0; + +static void LogDebugNotCompiled() { + if (!enableDebug) + return; + + double now = time_now_d(); + if (now < lastDebugLog + 1.0) + return; + lastDebugLog = now; + + int worstIROp = -1; + int worstIRVal = 0; + for (auto it : debugSeenNotCompiledIR) { + if (it.second > worstIRVal) { + worstIRVal = it.second; + worstIROp = it.first; + } + } + debugSeenNotCompiledIR.clear(); + + const char *worstName = nullptr; + int worstVal = 0; + for (auto it : debugSeenNotCompiled) { + if (it.second > worstVal) { + worstVal = it.second; + worstName = it.first; + } + } + debugSeenNotCompiled.clear(); + + if (worstIROp != -1) + WARN_LOG(JIT, "Most not compiled IR op: %s (%d)", GetIRMeta((IROp)worstIROp)->name, worstIRVal); + if (worstName != nullptr) + WARN_LOG(JIT, "Most not compiled op: %s (%d)", worstName, worstVal); +} + +RiscVJit::RiscVJit(MIPSState *mipsState) + : IRJit(mipsState), gpr(mipsState, &jo), fpr(mipsState, &jo), debugInterface_(blocks_, *this) { + // Automatically disable incompatible options. + if (((intptr_t)Memory::base & 0x00000000FFFFFFFFUL) != 0) { + jo.enablePointerify = false; + } + + // Since we store the offset, this is as big as it can be. + // We could shift off one bit to double it, would need to change RiscVAsm. + AllocCodeSpace(1024 * 1024 * 16); + SetAutoCompress(true); + + gpr.Init(this); + fpr.Init(this); + + GenerateFixedCode(jo); +} + +RiscVJit::~RiscVJit() { +} + +void RiscVJit::RunLoopUntil(u64 globalticks) { + if constexpr (enableDebug) { + LogDebugNotCompiled(); + } + + PROFILE_THIS_SCOPE("jit"); + ((void (*)())enterDispatcher_)(); +} + +JitBlockCacheDebugInterface *MIPSComp::RiscVJit::GetBlockCacheDebugInterface() { + return &debugInterface_; +} + +static void NoBlockExits() { + _assert_msg_(false, "Never exited block, invalid IR?"); +} + +bool RiscVJit::CompileTargetBlock(IRBlock *block, int block_num, bool preload) { + if (GetSpaceLeft() < 0x800) + return false; + + // Don't worry, the codespace isn't large enough to overflow offsets. + block->SetTargetOffset((int)GetOffset(GetCodePointer())); + + // TODO: Block linking, checked entries and such. + + gpr.Start(block); + fpr.Start(block); + + for (int i = 0; i < block->GetNumInstructions(); ++i) { + const IRInst &inst = block->GetInstructions()[i]; + gpr.SetIRIndex(i); + fpr.SetIRIndex(i); + + CompileIRInst(inst); + + if (jo.Disabled(JitDisable::REGALLOC_GPR)) + gpr.FlushAll(); + if (jo.Disabled(JitDisable::REGALLOC_FPR)) + fpr.FlushAll(); + + // Safety check, in case we get a bunch of really large jit ops without a lot of branching. + if (GetSpaceLeft() < 0x800) { + return false; + } + } + + // We should've written an exit above. If we didn't, bad things will happen. + if (enableDebug) { + QuickCallFunction(&NoBlockExits); + QuickJ(R_RA, crashHandler_); + } + + FlushIcache(); + + return true; +} + +void RiscVJit::CompileIRInst(IRInst inst) { + switch (inst.op) { + case IROp::Nop: + break; + + case IROp::SetConst: + case IROp::SetConstF: + case IROp::Downcount: + case IROp::SetPC: + case IROp::SetPCConst: + CompIR_Basic(inst); + break; + + case IROp::Add: + case IROp::Sub: + case IROp::AddConst: + case IROp::SubConst: + case IROp::Neg: + CompIR_Arith(inst); + break; + + case IROp::And: + case IROp::Or: + case IROp::Xor: + case IROp::AndConst: + case IROp::OrConst: + case IROp::XorConst: + case IROp::Not: + CompIR_Logic(inst); + break; + + case IROp::Mov: + case IROp::Ext8to32: + case IROp::Ext16to32: + CompIR_Assign(inst); + break; + + case IROp::ReverseBits: + case IROp::BSwap16: + case IROp::BSwap32: + case IROp::Clz: + CompIR_Bits(inst); + break; + + case IROp::Shl: + case IROp::Shr: + case IROp::Sar: + case IROp::Ror: + case IROp::ShlImm: + case IROp::ShrImm: + case IROp::SarImm: + case IROp::RorImm: + CompIR_Shift(inst); + break; + + case IROp::Slt: + case IROp::SltConst: + case IROp::SltU: + case IROp::SltUConst: + CompIR_Compare(inst); + break; + + case IROp::MovZ: + case IROp::MovNZ: + case IROp::Max: + case IROp::Min: + CompIR_CondAssign(inst); + break; + + case IROp::MtLo: + case IROp::MtHi: + case IROp::MfLo: + case IROp::MfHi: + CompIR_HiLo(inst); + break; + + case IROp::Mult: + case IROp::MultU: + case IROp::Madd: + case IROp::MaddU: + case IROp::Msub: + case IROp::MsubU: + CompIR_Mult(inst); + break; + + case IROp::Div: + case IROp::DivU: + CompIR_Div(inst); + break; + + case IROp::Load8: + case IROp::Load8Ext: + case IROp::Load16: + case IROp::Load16Ext: + case IROp::Load32: + case IROp::Load32Linked: + CompIR_Load(inst); + break; + + case IROp::Load32Left: + case IROp::Load32Right: + CompIR_LoadShift(inst); + break; + + case IROp::LoadFloat: + CompIR_FLoad(inst); + break; + + case IROp::LoadVec4: + CompIR_VecLoad(inst); + break; + + case IROp::Store8: + case IROp::Store16: + case IROp::Store32: + CompIR_Store(inst); + break; + + case IROp::Store32Conditional: + CompIR_CondStore(inst); + break; + + case IROp::Store32Left: + case IROp::Store32Right: + CompIR_StoreShift(inst); + break; + + case IROp::StoreFloat: + CompIR_FStore(inst); + break; + + case IROp::StoreVec4: + CompIR_VecStore(inst); + break; + + case IROp::FAdd: + case IROp::FSub: + case IROp::FMul: + case IROp::FDiv: + case IROp::FSqrt: + case IROp::FNeg: + CompIR_FArith(inst); + break; + + case IROp::FMin: + case IROp::FMax: + CompIR_FCondAssign(inst); + break; + + case IROp::FMov: + case IROp::FAbs: + case IROp::FSign: + CompIR_FAssign(inst); + break; + + case IROp::FRound: + case IROp::FTrunc: + case IROp::FCeil: + case IROp::FFloor: + CompIR_FRound(inst); + break; + + case IROp::FCvtWS: + case IROp::FCvtSW: + case IROp::FCvtScaledWS: + case IROp::FCvtScaledSW: + CompIR_FCvt(inst); + break; + + case IROp::FSat0_1: + case IROp::FSatMinus1_1: + CompIR_FSat(inst); + break; + + case IROp::FCmp: + case IROp::FCmovVfpuCC: + case IROp::FCmpVfpuBit: + case IROp::FCmpVfpuAggregate: + CompIR_FCompare(inst); + break; + + case IROp::RestoreRoundingMode: + case IROp::ApplyRoundingMode: + case IROp::UpdateRoundingMode: + CompIR_RoundingMode(inst); + break; + + case IROp::SetCtrlVFPU: + case IROp::SetCtrlVFPUReg: + case IROp::SetCtrlVFPUFReg: + case IROp::FpCondToReg: + case IROp::ZeroFpCond: + case IROp::FpCtrlFromReg: + case IROp::FpCtrlToReg: + case IROp::VfpuCtrlToReg: + case IROp::FMovFromGPR: + case IROp::FMovToGPR: + CompIR_Transfer(inst); + break; + + case IROp::Vec4Init: + case IROp::Vec4Shuffle: + case IROp::Vec4Mov: + CompIR_VecAssign(inst); + break; + + case IROp::Vec4Add: + case IROp::Vec4Sub: + case IROp::Vec4Mul: + case IROp::Vec4Div: + case IROp::Vec4Scale: + case IROp::Vec4Neg: + case IROp::Vec4Abs: + CompIR_VecArith(inst); + break; + + case IROp::Vec4Dot: + CompIR_VecHoriz(inst); + break; + + case IROp::Vec2Unpack16To31: + case IROp::Vec2Unpack16To32: + case IROp::Vec4Unpack8To32: + case IROp::Vec4DuplicateUpperBitsAndShift1: + case IROp::Vec4Pack31To8: + case IROp::Vec4Pack32To8: + case IROp::Vec2Pack31To16: + case IROp::Vec2Pack32To16: + CompIR_VecPack(inst); + break; + + case IROp::Vec4ClampToZero: + case IROp::Vec2ClampToZero: + CompIR_VecClamp(inst); + break; + + case IROp::FSin: + case IROp::FCos: + case IROp::FRSqrt: + case IROp::FRecip: + case IROp::FAsin: + CompIR_FSpecial(inst); + break; + + case IROp::Interpret: + CompIR_Interpret(inst); + break; + + case IROp::Syscall: + case IROp::CallReplacement: + case IROp::Break: + CompIR_System(inst); + break; + + case IROp::Breakpoint: + case IROp::MemoryCheck: + CompIR_Breakpoint(inst); + break; + + case IROp::ValidateAddress8: + case IROp::ValidateAddress16: + case IROp::ValidateAddress32: + case IROp::ValidateAddress128: + CompIR_ValidateAddress(inst); + break; + + case IROp::ExitToConst: + case IROp::ExitToReg: + case IROp::ExitToPC: + CompIR_Exit(inst); + break; + + case IROp::ExitToConstIfEq: + case IROp::ExitToConstIfNeq: + case IROp::ExitToConstIfGtZ: + case IROp::ExitToConstIfGeZ: + case IROp::ExitToConstIfLtZ: + case IROp::ExitToConstIfLeZ: + case IROp::ExitToConstIfFpTrue: + case IROp::ExitToConstIfFpFalse: + CompIR_ExitIf(inst); + break; + + default: + _assert_msg_(false, "Unexpected IR op %d", (int)inst.op); + CompIR_Generic(inst); + break; + } +} + +static u32 DoIRInst(uint64_t value) { + IRInst inst; + memcpy(&inst, &value, sizeof(inst)); + + if constexpr (enableDebug) + debugSeenNotCompiledIR[(uint8_t)inst.op]++; + + return IRInterpret(currentMIPS, &inst, 1); +} + +void RiscVJit::CompIR_Generic(IRInst inst) { + // If we got here, we're going the slow way. + uint64_t value; + memcpy(&value, &inst, sizeof(inst)); + + FlushAll(); + LI(X10, value, SCRATCH2); + SaveStaticRegisters(); + QuickCallFunction(&DoIRInst); + LoadStaticRegisters(); + + // We only need to check the return value if it's a potential exit. + if ((GetIRMeta(inst.op)->flags & IRFLAG_EXIT) != 0) { + // Result in X10 aka SCRATCH1. + _assert_(X10 == SCRATCH1); + if (BInRange(dispatcherPCInSCRATCH1_)) { + BNE(X10, R_ZERO, dispatcherPCInSCRATCH1_); + } else { + FixupBranch skip = BEQ(X10, R_ZERO); + QuickJ(R_RA, dispatcherPCInSCRATCH1_); + SetJumpTarget(skip); + } + } +} + +static void DebugInterpretHit(const char *name) { + if (enableDebug) + debugSeenNotCompiled[name]++; +} + +void RiscVJit::CompIR_Interpret(IRInst inst) { + MIPSOpcode op(inst.constant); + + // IR protects us against this being a branching instruction (well, hopefully.) + FlushAll(); + SaveStaticRegisters(); + if (enableDebug) { + LI(X10, MIPSGetName(op)); + QuickCallFunction(&DebugInterpretHit); + } + LI(X10, (int32_t)inst.constant); + QuickCallFunction((const u8 *)MIPSGetInterpretFunc(op)); + LoadStaticRegisters(); +} + +void RiscVJit::FlushAll() { + gpr.FlushAll(); + fpr.FlushAll(); +} + +bool RiscVJit::DescribeCodePtr(const u8 *ptr, std::string &name) { + // Used in disassembly viewer. + if (ptr == dispatcher_) { + name = "dispatcher"; + } else if (ptr == dispatcherPCInSCRATCH1_) { + name = "dispatcher (PC in SCRATCH1)"; + } else if (ptr == dispatcherNoCheck_) { + name = "dispatcherNoCheck"; + } else if (ptr == saveStaticRegisters_) { + name = "saveStaticRegisters"; + } else if (ptr == loadStaticRegisters_) { + name = "loadStaticRegisters"; + } else if (ptr == enterDispatcher_) { + name = "enterDispatcher"; + } else if (ptr == applyRoundingMode_) { + name = "applyRoundingMode"; + } else if (!IsInSpace(ptr)) { + return false; + } else { + int offset = (int)GetOffset(ptr); + int block_num = -1; + for (int i = 0; i < blocks_.GetNumBlocks(); ++i) { + const auto &b = blocks_.GetBlock(i); + // We allocate linearly. + if (b->GetTargetOffset() <= offset) + block_num = i; + if (b->GetTargetOffset() > offset) + break; + } + + if (block_num == -1) { + name = "(unknown or deleted block)"; + return true; + } + + const IRBlock *block = blocks_.GetBlock(block_num); + if (block) { + u32 start = 0, size = 0; + block->GetRange(start, size); + name = StringFromFormat("(block %d at %08x)", block_num, start); + return true; + } + return false; + } + return true; +} + +bool RiscVJit::CodeInRange(const u8 *ptr) const { + return IsInSpace(ptr); +} + +bool RiscVJit::IsAtDispatchFetch(const u8 *ptr) const { + return ptr == dispatcherFetch_; +} + +const u8 *RiscVJit::GetDispatcher() const { + return dispatcher_; +} + +const u8 *RiscVJit::GetCrashHandler() const { + return crashHandler_; +} + +void RiscVJit::ClearCache() { + IRJit::ClearCache(); + + ClearCodeSpace(jitStartOffset_); + FlushIcacheSection(region + jitStartOffset_, region + region_size - jitStartOffset_); +} + +void RiscVJit::RestoreRoundingMode(bool force) { + FSRMI(Round::NEAREST_EVEN); +} + +void RiscVJit::ApplyRoundingMode(bool force) { + QuickCallFunction(applyRoundingMode_); +} + +void RiscVJit::MovFromPC(RiscVReg r) { + LWU(r, CTXREG, offsetof(MIPSState, pc)); +} + +void RiscVJit::MovToPC(RiscVReg r) { + SW(r, CTXREG, offsetof(MIPSState, pc)); +} + +void RiscVJit::SaveStaticRegisters() { + if (jo.useStaticAlloc) { + QuickCallFunction(saveStaticRegisters_); + } else { + // Inline the single operation + SW(DOWNCOUNTREG, CTXREG, offsetof(MIPSState, downcount)); + } +} + +void RiscVJit::LoadStaticRegisters() { + if (jo.useStaticAlloc) { + QuickCallFunction(loadStaticRegisters_); + } else { + LW(DOWNCOUNTREG, CTXREG, offsetof(MIPSState, downcount)); + } +} + +void RiscVJit::NormalizeSrc1(IRInst inst, RiscVReg *reg, RiscVReg tempReg, bool allowOverlap) { + *reg = NormalizeR(inst.src1, allowOverlap ? 0 : inst.dest, tempReg); +} + +void RiscVJit::NormalizeSrc12(IRInst inst, RiscVReg *lhs, RiscVReg *rhs, RiscVReg lhsTempReg, RiscVReg rhsTempReg, bool allowOverlap) { + *lhs = NormalizeR(inst.src1, allowOverlap ? 0 : inst.dest, lhsTempReg); + *rhs = NormalizeR(inst.src2, allowOverlap ? 0 : inst.dest, rhsTempReg); +} + +RiscVReg RiscVJit::NormalizeR(IRRegIndex rs, IRRegIndex rd, RiscVReg tempReg) { + // For proper compare, we must sign extend so they both match or don't match. + // But don't change pointers, in case one is SP (happens in LittleBigPlanet.) + if (gpr.IsImm(rs) && gpr.GetImm(rs) == 0) { + return R_ZERO; + } else if (gpr.IsMappedAsPointer(rs) || rs == rd) { + return gpr.Normalize32(rs, tempReg); + } else { + return gpr.Normalize32(rs); + } +} + +RiscVBlockCacheDebugInterface::RiscVBlockCacheDebugInterface(IRBlockCache &irBlocks, RiscVJit &jit) + : irBlocks_(irBlocks), jit_(jit) {} + +int RiscVBlockCacheDebugInterface::GetNumBlocks() const { + return irBlocks_.GetNumBlocks(); +} + +int RiscVBlockCacheDebugInterface::GetBlockNumberFromStartAddress(u32 em_address, bool realBlocksOnly) const { + return irBlocks_.GetBlockNumberFromStartAddress(em_address, realBlocksOnly); +} + +void RiscVBlockCacheDebugInterface::GetBlockCodeRange(int blockNum, int *startOffset, int *size) const { + int blockOffset = irBlocks_.GetBlock(blockNum)->GetTargetOffset(); + int endOffset; + // We assume linear allocation. Maybe a bit dangerous, should always be right. + if (blockNum + 1 >= GetNumBlocks()) { + // Last block, get from current code pointer. + endOffset = (int)jit_.GetOffset(jit_.GetCodePointer()); + } else { + endOffset = irBlocks_.GetBlock(blockNum + 1)->GetTargetOffset(); + _assert_msg_(endOffset >= blockOffset, "Next block not sequential, block=%d/%08x, next=%d/%08x", blockNum, blockOffset, blockNum + 1, endOffset); + } + + *startOffset = blockOffset; + *size = endOffset - blockOffset; +} + +JitBlockDebugInfo RiscVBlockCacheDebugInterface::GetBlockDebugInfo(int blockNum) const { + JitBlockDebugInfo debugInfo = irBlocks_.GetBlockDebugInfo(blockNum); + +#if PPSSPP_ARCH(RISCV64) + int blockOffset, codeSize; + GetBlockCodeRange(blockNum, &blockOffset, &codeSize); + debugInfo.targetDisasm = DisassembleRV64(jit_.GetBasePtr() + blockOffset, codeSize); +#endif + return debugInfo; +} + +void RiscVBlockCacheDebugInterface::ComputeStats(BlockCacheStats &bcStats) const { + double totalBloat = 0.0; + double maxBloat = 0.0; + double minBloat = 1000000000.0; + int numBlocks = GetNumBlocks(); + for (int i = 0; i < numBlocks; ++i) { + const IRBlock &b = *irBlocks_.GetBlock(i); + + // RISC-V (jit) size. + int blockOffset, codeSize; + GetBlockCodeRange(i, &blockOffset, &codeSize); + if (codeSize == 0) + continue; + + // MIPS (PSP) size. + u32 origAddr, mipsBytes; + b.GetRange(origAddr, mipsBytes); + + double bloat = (double)codeSize / (double)mipsBytes; + if (bloat < minBloat) { + minBloat = bloat; + bcStats.minBloatBlock = origAddr; + } + if (bloat > maxBloat) { + maxBloat = bloat; + bcStats.maxBloatBlock = origAddr; + } + totalBloat += bloat; + bcStats.bloatMap[bloat] = origAddr; + } + bcStats.numBlocks = numBlocks; + bcStats.minBloat = minBloat; + bcStats.maxBloat = maxBloat; + bcStats.avgBloat = totalBloat / (double)numBlocks; +} + +} // namespace MIPSComp diff --git a/Core/MIPS/RiscV/RiscVJit.h b/Core/MIPS/RiscV/RiscVJit.h new file mode 100644 index 0000000000..74c221fecb --- /dev/null +++ b/Core/MIPS/RiscV/RiscVJit.h @@ -0,0 +1,157 @@ +// Copyright (c) 2023- PPSSPP Project. + +// 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, version 2.0 or later versions. + +// 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 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#pragma once + +#include +#include +#include "Common/RiscVEmitter.h" +#include "Core/MIPS/IR/IRJit.h" +#include "Core/MIPS/JitCommon/JitState.h" +#include "Core/MIPS/JitCommon/JitCommon.h" +#include "Core/MIPS/RiscV/RiscVRegCache.h" +#include "Core/MIPS/RiscV/RiscVRegCacheFPU.h" + +namespace MIPSComp { + +class RiscVJit; + +class RiscVBlockCacheDebugInterface : public JitBlockCacheDebugInterface { +public: + RiscVBlockCacheDebugInterface(IRBlockCache &irBlocks, RiscVJit &jit); + int GetNumBlocks() const; + int GetBlockNumberFromStartAddress(u32 em_address, bool realBlocksOnly = true) const; + JitBlockDebugInfo GetBlockDebugInfo(int blockNum) const; + void ComputeStats(BlockCacheStats &bcStats) const; + +private: + void GetBlockCodeRange(int blockNum, int *startOffset, int *size) const; + + IRBlockCache &irBlocks_; + RiscVJit &jit_; +}; + +class RiscVJit : public RiscVGen::RiscVCodeBlock, public IRJit { +public: + RiscVJit(MIPSState *mipsState); + ~RiscVJit(); + + void RunLoopUntil(u64 globalticks) override; + + bool DescribeCodePtr(const u8 *ptr, std::string &name) override; + bool CodeInRange(const u8 *ptr) const override; + bool IsAtDispatchFetch(const u8 *ptr) const override; + const u8 *GetDispatcher() const override; + const u8 *GetCrashHandler() const override; + + void ClearCache() override; + + JitBlockCacheDebugInterface *GetBlockCacheDebugInterface() override; + +protected: + bool CompileTargetBlock(IRBlock *block, int block_num, bool preload) override; + + void CompileIRInst(IRInst inst); + +private: + void GenerateFixedCode(const JitOptions &jo); + + void RestoreRoundingMode(bool force = false); + void ApplyRoundingMode(bool force = false); + void MovFromPC(RiscVGen::RiscVReg r); + void MovToPC(RiscVGen::RiscVReg r); + + void SaveStaticRegisters(); + void LoadStaticRegisters(); + + // Note: destroys SCRATCH1. + void FlushAll(); + + void CompIR_Arith(IRInst inst); + void CompIR_Assign(IRInst inst); + void CompIR_Basic(IRInst inst); + void CompIR_Bits(IRInst inst); + void CompIR_Breakpoint(IRInst inst); + void CompIR_Compare(IRInst inst); + void CompIR_CondAssign(IRInst inst); + void CompIR_CondStore(IRInst inst); + void CompIR_Div(IRInst inst); + void CompIR_Exit(IRInst inst); + void CompIR_ExitIf(IRInst inst); + void CompIR_FArith(IRInst inst); + void CompIR_FAssign(IRInst inst); + void CompIR_FCompare(IRInst inst); + void CompIR_FCondAssign(IRInst inst); + void CompIR_FCvt(IRInst inst); + void CompIR_FLoad(IRInst inst); + void CompIR_FRound(IRInst inst); + void CompIR_FSat(IRInst inst); + void CompIR_FSpecial(IRInst inst); + void CompIR_FStore(IRInst inst); + void CompIR_Generic(IRInst inst); + void CompIR_HiLo(IRInst inst); + void CompIR_Interpret(IRInst inst); + void CompIR_Load(IRInst inst); + void CompIR_LoadShift(IRInst inst); + void CompIR_Logic(IRInst inst); + void CompIR_Mult(IRInst inst); + void CompIR_RoundingMode(IRInst inst); + void CompIR_Shift(IRInst inst); + void CompIR_Store(IRInst inst); + void CompIR_StoreShift(IRInst inst); + void CompIR_System(IRInst inst); + void CompIR_Transfer(IRInst inst); + void CompIR_VecArith(IRInst inst); + void CompIR_VecAssign(IRInst inst); + void CompIR_VecClamp(IRInst inst); + void CompIR_VecHoriz(IRInst inst); + void CompIR_VecLoad(IRInst inst); + void CompIR_VecPack(IRInst inst); + void CompIR_VecStore(IRInst inst); + void CompIR_ValidateAddress(IRInst inst); + + void SetScratch1ToSrc1Address(IRReg src1); + // Modifies SCRATCH regs. + int32_t AdjustForAddressOffset(RiscVGen::RiscVReg *reg, int32_t constant, int32_t range = 0); + void NormalizeSrc1(IRInst inst, RiscVGen::RiscVReg *reg, RiscVGen::RiscVReg tempReg, bool allowOverlap); + void NormalizeSrc12(IRInst inst, RiscVGen::RiscVReg *lhs, RiscVGen::RiscVReg *rhs, RiscVGen::RiscVReg lhsTempReg, RiscVGen::RiscVReg rhsTempReg, bool allowOverlap); + RiscVGen::RiscVReg NormalizeR(IRRegIndex rs, IRRegIndex rd, RiscVGen::RiscVReg tempReg); + + RiscVRegCache gpr; + RiscVRegCacheFPU fpr; + RiscVBlockCacheDebugInterface debugInterface_; + + const u8 *enterDispatcher_ = nullptr; + + const u8 *outerLoop_ = nullptr; + const u8 *outerLoopPCInSCRATCH1_ = nullptr; + const u8 *dispatcherCheckCoreState_ = nullptr; + const u8 *dispatcherPCInSCRATCH1_ = nullptr; + const u8 *dispatcher_ = nullptr; + const u8 *dispatcherNoCheck_ = nullptr; + const u8 *dispatcherFetch_ = nullptr; + const u8 *applyRoundingMode_ = nullptr; + + const u8 *saveStaticRegisters_ = nullptr; + const u8 *loadStaticRegisters_ = nullptr; + + const u8 *crashHandler_ = nullptr; + + int jitStartOffset_ = 0; +}; + +} // namespace MIPSComp diff --git a/Core/MIPS/RiscV/RiscVRegCache.cpp b/Core/MIPS/RiscV/RiscVRegCache.cpp new file mode 100644 index 0000000000..d37ce3eb23 --- /dev/null +++ b/Core/MIPS/RiscV/RiscVRegCache.cpp @@ -0,0 +1,1102 @@ +// Copyright (c) 2023- PPSSPP Project. + +// 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, version 2.0 or later versions. + +// 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 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#ifndef offsetof +#include +#endif + +#include "Common/CPUDetect.h" +#include "Core/MIPS/IR/IRInst.h" +#include "Core/MIPS/IR/IRAnalysis.h" +#include "Core/MIPS/RiscV/RiscVRegCache.h" +#include "Core/MIPS/JitCommon/JitState.h" +#include "Core/Reporting.h" + +using namespace RiscVGen; +using namespace RiscVJitConstants; + +RiscVRegCache::RiscVRegCache(MIPSState *mipsState, MIPSComp::JitOptions *jo) + : mips_(mipsState), jo_(jo) { +} + +void RiscVRegCache::Init(RiscVEmitter *emitter) { + emit_ = emitter; +} + +void RiscVRegCache::Start(MIPSComp::IRBlock *irBlock) { + if (!initialReady_) { + SetupInitialRegs(); + initialReady_ = true; + } + + memcpy(ar, arInitial_, sizeof(ar)); + memcpy(mr, mrInitial_, sizeof(mr)); + + int numStatics; + const StaticAllocation *statics = GetStaticAllocations(numStatics); + for (int i = 0; i < numStatics; i++) { + ar[statics[i].ar].mipsReg = statics[i].mr; + ar[statics[i].ar].pointerified = statics[i].pointerified && jo_->enablePointerify; + ar[statics[i].ar].normalized32 = false; + mr[statics[i].mr].loc = MIPSLoc::RVREG; + mr[statics[i].mr].reg = statics[i].ar; + mr[statics[i].mr].isStatic = true; + mr[statics[i].mr].spillLock = true; + } + + irBlock_ = irBlock; + irIndex_ = 0; +} + +void RiscVRegCache::SetupInitialRegs() { + for (int i = 0; i < NUM_RVREG; i++) { + arInitial_[i].mipsReg = IRREG_INVALID; + arInitial_[i].isDirty = false; + arInitial_[i].pointerified = false; + arInitial_[i].tempLocked = false; + arInitial_[i].normalized32 = false; + } + for (int i = 0; i < NUM_MIPSREG; i++) { + mrInitial_[i].loc = MIPSLoc::MEM; + mrInitial_[i].reg = INVALID_REG; + mrInitial_[i].imm = -1; + mrInitial_[i].spillLock = false; + mrInitial_[i].isStatic = false; + } + + // Treat R_ZERO a bit specially, but it's basically static alloc too. + arInitial_[R_ZERO].mipsReg = MIPS_REG_ZERO; + arInitial_[R_ZERO].normalized32 = true; + mrInitial_[MIPS_REG_ZERO].loc = MIPSLoc::RVREG_IMM; + mrInitial_[MIPS_REG_ZERO].reg = R_ZERO; + mrInitial_[MIPS_REG_ZERO].imm = 0; + mrInitial_[MIPS_REG_ZERO].isStatic = true; +} + +const RiscVReg *RiscVRegCache::GetMIPSAllocationOrder(int &count) { + // X8 and X9 are the most ideal for static alloc because they can be used with compression. + // Otherwise we stick to saved regs - might not be necessary. + static const RiscVReg allocationOrder[] = { + X8, X9, X12, X13, X14, X15, X5, X6, X7, X16, X17, X18, X19, X20, X21, X22, X23, X28, X29, X30, X31, + }; + static const RiscVReg allocationOrderStaticAlloc[] = { + X12, X13, X14, X15, X5, X6, X7, X16, X17, X21, X22, X23, X28, X29, X30, X31, + }; + + if (jo_->useStaticAlloc) { + count = ARRAY_SIZE(allocationOrderStaticAlloc); + return allocationOrderStaticAlloc; + } else { + count = ARRAY_SIZE(allocationOrder); + return allocationOrder; + } +} + +const RiscVRegCache::StaticAllocation *RiscVRegCache::GetStaticAllocations(int &count) { + static const StaticAllocation allocs[] = { + { MIPS_REG_SP, X8, true }, + { MIPS_REG_V0, X9 }, + { MIPS_REG_V1, X18 }, + { MIPS_REG_A0, X19 }, + { MIPS_REG_RA, X20 }, + }; + + if (jo_->useStaticAlloc) { + count = ARRAY_SIZE(allocs); + return allocs; + } else { + count = 0; + return nullptr; + } +} + +void RiscVRegCache::EmitLoadStaticRegisters() { + int count; + const StaticAllocation *allocs = GetStaticAllocations(count); + for (int i = 0; i < count; i++) { + int offset = GetMipsRegOffset(allocs[i].mr); + if (allocs[i].pointerified && jo_->enablePointerify) { + emit_->LWU(allocs[i].ar, CTXREG, offset); + emit_->ADD(allocs[i].ar, allocs[i].ar, MEMBASEREG); + } else { + emit_->LW(allocs[i].ar, CTXREG, offset); + } + } +} + +void RiscVRegCache::EmitSaveStaticRegisters() { + int count; + const StaticAllocation *allocs = GetStaticAllocations(count); + // This only needs to run once (by Asm) so checks don't need to be fast. + for (int i = 0; i < count; i++) { + int offset = GetMipsRegOffset(allocs[i].mr); + emit_->SW(allocs[i].ar, CTXREG, offset); + } +} + +void RiscVRegCache::FlushBeforeCall() { + // These registers are not preserved by function calls. + for (int i = 5; i <= 7; ++i) { + FlushRiscVReg(RiscVReg(X0 + i)); + } + for (int i = 10; i <= 17; ++i) { + FlushRiscVReg(RiscVReg(X0 + i)); + } + for (int i = 28; i <= 31; ++i) { + FlushRiscVReg(RiscVReg(X0 + i)); + } +} + +bool RiscVRegCache::IsInRAM(IRRegIndex reg) { + _dbg_assert_(IsValidReg(reg)); + return mr[reg].loc == MIPSLoc::MEM; +} + +bool RiscVRegCache::IsMapped(IRRegIndex mipsReg) { + _dbg_assert_(IsValidReg(mipsReg)); + return mr[mipsReg].loc == MIPSLoc::RVREG || mr[mipsReg].loc == MIPSLoc::RVREG_IMM; +} + +bool RiscVRegCache::IsMappedAsPointer(IRRegIndex mipsReg) { + _dbg_assert_(IsValidReg(mipsReg)); + if (mr[mipsReg].loc == MIPSLoc::RVREG) { + return ar[mr[mipsReg].reg].pointerified; + } else if (mr[mipsReg].loc == MIPSLoc::RVREG_IMM) { + if (ar[mr[mipsReg].reg].pointerified) { + ERROR_LOG(JIT, "Really shouldn't be pointerified here"); + } + } else if (mr[mipsReg].loc == MIPSLoc::RVREG_AS_PTR) { + return true; + } + return false; +} + +bool RiscVRegCache::IsMappedAsStaticPointer(IRRegIndex reg) { + if (IsMappedAsPointer(reg)) { + return mr[reg].isStatic; + } + return false; +} + +bool RiscVRegCache::IsNormalized32(IRRegIndex mipsReg) { + _dbg_assert_(IsValidReg(mipsReg)); + if (XLEN == 32) + return true; + if (mr[mipsReg].loc == MIPSLoc::RVREG || mr[mipsReg].loc == MIPSLoc::RVREG_IMM) { + return ar[mr[mipsReg].reg].normalized32; + } + return false; +} + +void RiscVRegCache::MarkDirty(RiscVReg reg, bool andNormalized32) { + // Can't mark X0 dirty. + _dbg_assert_(reg > X0 && reg <= X31); + ar[reg].isDirty = true; + ar[reg].normalized32 = andNormalized32; + // If reg is written to, pointerification is lost. + ar[reg].pointerified = false; + if (ar[reg].mipsReg != IRREG_INVALID) { + RegStatusMIPS &m = mr[ar[reg].mipsReg]; + if (m.loc == MIPSLoc::RVREG_AS_PTR || m.loc == MIPSLoc::RVREG_IMM) { + m.loc = MIPSLoc::RVREG; + m.imm = -1; + } + _dbg_assert_(m.loc == MIPSLoc::RVREG); + } +} + +void RiscVRegCache::MarkPtrDirty(RiscVReg reg) { + // Can't mark X0 dirty. + _dbg_assert_(reg > X0 && reg <= X31); + _dbg_assert_(!ar[reg].normalized32); + ar[reg].isDirty = true; + if (ar[reg].mipsReg != IRREG_INVALID) { + _dbg_assert_(mr[ar[reg].mipsReg].loc == MIPSLoc::RVREG_AS_PTR); + } else { + _dbg_assert_(ar[reg].pointerified); + } +} + +RiscVGen::RiscVReg RiscVRegCache::Normalize32(IRRegIndex mipsReg, RiscVGen::RiscVReg destReg) { + _dbg_assert_(IsValidReg(mipsReg)); + _dbg_assert_(destReg == INVALID_REG || (destReg > X0 && destReg <= X31)); + + RiscVReg reg = mr[mipsReg].reg; + if (XLEN == 32) + return reg; + + switch (mr[mipsReg].loc) { + case MIPSLoc::IMM: + case MIPSLoc::MEM: + _assert_msg_(false, "Cannot normalize an imm or mem"); + return INVALID_REG; + + case MIPSLoc::RVREG: + case MIPSLoc::RVREG_IMM: + if (!ar[mr[mipsReg].reg].normalized32) { + if (destReg == INVALID_REG) { + emit_->SEXT_W(mr[mipsReg].reg, mr[mipsReg].reg); + ar[mr[mipsReg].reg].normalized32 = true; + ar[mr[mipsReg].reg].pointerified = false; + } else { + emit_->SEXT_W(destReg, mr[mipsReg].reg); + } + } else if (destReg != INVALID_REG) { + emit_->SEXT_W(destReg, mr[mipsReg].reg); + } + break; + + case MIPSLoc::RVREG_AS_PTR: + _dbg_assert_(ar[mr[mipsReg].reg].normalized32 == false); + if (destReg == INVALID_REG) { + // If we can pointerify, SEXT_W will be enough. + if (!jo_->enablePointerify) + emit_->SUB(mr[mipsReg].reg, mr[mipsReg].reg, MEMBASEREG); + emit_->SEXT_W(mr[mipsReg].reg, mr[mipsReg].reg); + mr[mipsReg].loc = MIPSLoc::RVREG; + ar[mr[mipsReg].reg].normalized32 = true; + ar[mr[mipsReg].reg].pointerified = false; + } else if (!jo_->enablePointerify) { + emit_->SUB(destReg, mr[mipsReg].reg, MEMBASEREG); + emit_->SEXT_W(destReg, destReg); + } else { + emit_->SEXT_W(destReg, mr[mipsReg].reg); + } + break; + } + + return destReg == INVALID_REG ? reg : destReg; +} + +void RiscVRegCache::SetRegImm(RiscVReg reg, u64 imm) { + _dbg_assert_(reg != R_ZERO || imm == 0); + _dbg_assert_(reg >= X0 && reg <= X31); + // TODO: Could optimize this more for > 32 bit constants. + emit_->LI(reg, imm); + _dbg_assert_(!ar[reg].pointerified); + ar[reg].normalized32 = imm == (u64)(s64)(s32)imm; +} + +void RiscVRegCache::MapRegTo(RiscVReg reg, IRRegIndex mipsReg, MIPSMap mapFlags) { + _dbg_assert_(reg > X0 && reg <= X31); + _dbg_assert_(IsValidReg(mipsReg)); + _dbg_assert_(!mr[mipsReg].isStatic); + if (mr[mipsReg].isStatic) { + ERROR_LOG(JIT, "Cannot MapRegTo static register %d", mipsReg); + return; + } + ar[reg].isDirty = (mapFlags & MIPSMap::DIRTY) == MIPSMap::DIRTY; + if ((mapFlags & MIPSMap::NOINIT) != MIPSMap::NOINIT) { + if (mipsReg == MIPS_REG_ZERO) { + // If we get a request to load the zero register, at least we won't spend + // time on a memory access... + emit_->LI(reg, 0); + + // This way, if we SetImm() it, we'll keep it. + mr[mipsReg].loc = MIPSLoc::RVREG_IMM; + mr[mipsReg].imm = 0; + ar[reg].normalized32 = true; + } else { + switch (mr[mipsReg].loc) { + case MIPSLoc::MEM: + emit_->LW(reg, CTXREG, GetMipsRegOffset(mipsReg)); + mr[mipsReg].loc = MIPSLoc::RVREG; + ar[reg].normalized32 = true; + break; + case MIPSLoc::IMM: + SetRegImm(reg, mr[mipsReg].imm); + // IMM is always dirty. + ar[reg].isDirty = true; + + // If we are mapping dirty, it means we're gonna overwrite. + // So the imm value is no longer valid. + if ((mapFlags & MIPSMap::DIRTY) == MIPSMap::DIRTY) + mr[mipsReg].loc = MIPSLoc::RVREG; + else + mr[mipsReg].loc = MIPSLoc::RVREG_IMM; + break; + case MIPSLoc::RVREG_IMM: + // If it's not dirty, we can keep it. + if (ar[reg].isDirty) + mr[mipsReg].loc = MIPSLoc::RVREG; + break; + default: + _assert_msg_(mr[mipsReg].loc != MIPSLoc::RVREG_AS_PTR, "MapRegTo with a pointer?"); + mr[mipsReg].loc = MIPSLoc::RVREG; + break; + } + } + } else { + _dbg_assert_(mipsReg != MIPS_REG_ZERO); + _dbg_assert_(ar[reg].isDirty); + mr[mipsReg].loc = MIPSLoc::RVREG; + } + ar[reg].mipsReg = mipsReg; + ar[reg].pointerified = false; + if (ar[reg].isDirty) + ar[reg].normalized32 = (mapFlags & MIPSMap::MARK_NORM32) == MIPSMap::MARK_NORM32; + mr[mipsReg].reg = reg; +} + +RiscVReg RiscVRegCache::AllocateReg() { + int allocCount; + const RiscVReg *allocOrder = GetMIPSAllocationOrder(allocCount); + +allocate: + for (int i = 0; i < allocCount; i++) { + RiscVReg reg = allocOrder[i]; + + if (ar[reg].mipsReg == IRREG_INVALID && !ar[reg].tempLocked) { + return reg; + } + } + + // Still nothing. Let's spill a reg and goto 10. + bool clobbered; + RiscVReg bestToSpill = FindBestToSpill(true, &clobbered); + if (bestToSpill == INVALID_REG) { + bestToSpill = FindBestToSpill(false, &clobbered); + } + + if (bestToSpill != INVALID_REG) { + if (clobbered) { + DiscardR(ar[bestToSpill].mipsReg); + } else { + FlushRiscVReg(bestToSpill); + } + // Now one must be free. + goto allocate; + } + + // Uh oh, we have all of them spilllocked.... + ERROR_LOG_REPORT(JIT, "Out of spillable registers near PC %08x", mips_->pc); + _assert_(bestToSpill != INVALID_REG); + return INVALID_REG; +} + +RiscVReg RiscVRegCache::FindBestToSpill(bool unusedOnly, bool *clobbered) { + int allocCount; + const RiscVReg *allocOrder = GetMIPSAllocationOrder(allocCount); + + static const int UNUSED_LOOKAHEAD_OPS = 30; + + IRSituation info; + info.lookaheadCount = UNUSED_LOOKAHEAD_OPS; + info.currentIndex = irIndex_; + info.instructions = irBlock_->GetInstructions(); + info.numInstructions = irBlock_->GetNumInstructions(); + + *clobbered = false; + for (int i = 0; i < allocCount; i++) { + RiscVReg reg = allocOrder[i]; + if (ar[reg].mipsReg != IRREG_INVALID && mr[ar[reg].mipsReg].spillLock) + continue; + if (ar[reg].tempLocked) + continue; + + // As it's in alloc-order, we know it's not static so we don't need to check for that. + IRUsage usage = IRNextGPRUsage(ar[reg].mipsReg, info); + + // Awesome, a clobbered reg. Let's use it. + if (usage == IRUsage::CLOBBERED) { + // TODO: Check HI/LO clobber together if we combine. + bool canClobber = true; + if (canClobber) { + *clobbered = true; + return reg; + } + } + + // Not awesome. A used reg. Let's try to avoid spilling. + if (!unusedOnly || usage == IRUsage::UNUSED) { + // TODO: Use age or something to choose which register to spill? + // TODO: Spill dirty regs first? or opposite? + return reg; + } + } + + return INVALID_REG; +} + +RiscVReg RiscVRegCache::TryMapTempImm(IRRegIndex r) { + _dbg_assert_(IsValidReg(r)); + // If already mapped, no need for a temporary. + if (IsMapped(r)) { + return R(r); + } + + if (mr[r].loc == MIPSLoc::IMM) { + if (mr[r].imm == 0) { + return R_ZERO; + } + + // Try our luck - check for an exact match in another rvreg. + for (int i = 0; i < NUM_MIPSREG; ++i) { + if (mr[i].loc == MIPSLoc::RVREG_IMM && mr[i].imm == mr[r].imm) { + // Awesome, let's just use this reg. + return mr[i].reg; + } + } + } + + return INVALID_REG; +} + +RiscVReg RiscVRegCache::GetAndLockTempR() { + RiscVReg reg = AllocateReg(); + if (reg != INVALID_REG) { + ar[reg].tempLocked = true; + pendingUnlock_ = true; + } + return reg; +} + +RiscVReg RiscVRegCache::MapReg(IRRegIndex mipsReg, MIPSMap mapFlags) { + _dbg_assert_(IsValidReg(mipsReg)); + + // TODO: Optimization to force HI/LO to be combined? + + if (mipsReg == IRREG_INVALID) { + ERROR_LOG(JIT, "Cannot map invalid register"); + return INVALID_REG; + } + + RiscVReg riscvReg = mr[mipsReg].reg; + + if (mr[mipsReg].isStatic) { + _dbg_assert_(riscvReg != INVALID_REG); + if (riscvReg == INVALID_REG) { + ERROR_LOG(JIT, "MapReg on statically mapped reg %d failed - riscvReg got lost", mipsReg); + } + if (mr[mipsReg].loc == MIPSLoc::IMM) { + // Back into the register, with or without the imm value. + // If noinit, the MAP_DIRTY check below will take care of the rest. + if ((mapFlags & MIPSMap::NOINIT) != MIPSMap::NOINIT) { + // This may set normalized32 to true. + SetRegImm(riscvReg, mr[mipsReg].imm); + mr[mipsReg].loc = MIPSLoc::RVREG_IMM; + ar[riscvReg].pointerified = false; + } + if ((mapFlags & MIPSMap::MARK_NORM32) == MIPSMap::MARK_NORM32) + ar[riscvReg].normalized32 = true; + } else if (mr[mipsReg].loc == MIPSLoc::RVREG_AS_PTR) { + // Was mapped as pointer, now we want it mapped as a value, presumably to + // add or subtract stuff to it. + if ((mapFlags & MIPSMap::NOINIT) != MIPSMap::NOINIT) { +#ifdef MASKED_PSP_MEMORY + _dbg_assert_(!ar[riscvReg].isDirty && (mapFlags & MIPSMap::DIRTY) != MIPSMap::DIRTY); +#endif + emit_->SUB(riscvReg, riscvReg, MEMBASEREG); + } + mr[mipsReg].loc = MIPSLoc::RVREG; + ar[riscvReg].normalized32 = false; + } + // Erasing the imm on dirty (necessary since otherwise we will still think it's ML_RVREG_IMM and return + // true for IsImm and calculate crazily wrong things). /unknown + if ((mapFlags & MIPSMap::DIRTY) == MIPSMap::DIRTY) { + // As we are dirty, can't keep RVREG_IMM, we will quickly drift out of sync + mr[mipsReg].loc = MIPSLoc::RVREG; + ar[riscvReg].pointerified = false; + ar[riscvReg].isDirty = true; + ar[riscvReg].normalized32 = (mapFlags & MIPSMap::MARK_NORM32) == MIPSMap::MARK_NORM32; + } else if ((mapFlags & MIPSMap::MARK_NORM32) == MIPSMap::MARK_NORM32) { + ar[riscvReg].normalized32 = true; + } + return mr[mipsReg].reg; + } + + // Let's see if it's already mapped. If so we just need to update the dirty flag. + // We don't need to check for ML_NOINIT because we assume that anyone who maps + // with that flag immediately writes a "known" value to the register. + if (mr[mipsReg].loc == MIPSLoc::RVREG || mr[mipsReg].loc == MIPSLoc::RVREG_IMM) { + _dbg_assert_(riscvReg != INVALID_REG && ar[riscvReg].mipsReg == mipsReg); + if (ar[riscvReg].mipsReg != mipsReg) { + ERROR_LOG_REPORT(JIT, "Register mapping out of sync! %i", mipsReg); + } + if ((mapFlags & MIPSMap::DIRTY) == MIPSMap::DIRTY) { + // Mapping dirty means the old imm value is invalid. + mr[mipsReg].loc = MIPSLoc::RVREG; + ar[riscvReg].isDirty = true; + // If reg is written to, pointerification is lost. + ar[riscvReg].pointerified = false; + ar[riscvReg].normalized32 = (mapFlags & MIPSMap::MARK_NORM32) == MIPSMap::MARK_NORM32; + } else if ((mapFlags & MIPSMap::MARK_NORM32) == MIPSMap::MARK_NORM32) { + ar[riscvReg].normalized32 = true; + } + + return mr[mipsReg].reg; + } else if (mr[mipsReg].loc == MIPSLoc::RVREG_AS_PTR) { + // Was mapped as pointer, now we want it mapped as a value, presumably to + // add or subtract stuff to it. + if ((mapFlags & MIPSMap::NOINIT) != MIPSMap::NOINIT) { +#ifdef MASKED_PSP_MEMORY + _dbg_assert_(!ar[riscvReg].isDirty && (mapFlags & MAP_DIRTY) == 0); +#endif + emit_->SUB(riscvReg, riscvReg, MEMBASEREG); + } + mr[mipsReg].loc = MIPSLoc::RVREG; + if ((mapFlags & MIPSMap::DIRTY) == MIPSMap::DIRTY) { + ar[riscvReg].isDirty = true; + } + // Let's always set this false, the SUB won't normalize. + ar[riscvReg].normalized32 = false; + return mr[mipsReg].reg; + } + + // Okay, not mapped, so we need to allocate an RV register. + RiscVReg reg = AllocateReg(); + if (reg != INVALID_REG) { + // Grab it, and load the value into it (if requested). + MapRegTo(reg, mipsReg, mapFlags); + } + + return reg; +} + +RiscVReg RiscVRegCache::MapRegAsPointer(IRRegIndex reg) { + _dbg_assert_(IsValidRegNoZero(reg)); + + // Already mapped. + if (mr[reg].loc == MIPSLoc::RVREG_AS_PTR) { + return mr[reg].reg; + } + + RiscVReg riscvReg = INVALID_REG; + if (mr[reg].loc != MIPSLoc::RVREG && mr[reg].loc != MIPSLoc::RVREG_IMM) { + riscvReg = MapReg(reg); + } else { + riscvReg = mr[reg].reg; + } + + if (mr[reg].loc == MIPSLoc::RVREG || mr[reg].loc == MIPSLoc::RVREG_IMM) { + // If there was an imm attached, discard it. + mr[reg].loc = MIPSLoc::RVREG; + if (!jo_->enablePointerify) { + // Convert to a pointer by adding the base and clearing off the top bits. + // If SP, we can probably avoid the top bit clear, let's play with that later. + AddMemBase(riscvReg); + mr[reg].loc = MIPSLoc::RVREG_AS_PTR; + } else if (!ar[riscvReg].pointerified) { + AddMemBase(riscvReg); + ar[riscvReg].pointerified = true; + } + ar[riscvReg].normalized32 = false; + } else { + ERROR_LOG(JIT, "MapRegAsPointer : MapReg failed to allocate a register?"); + } + return riscvReg; +} + +void RiscVRegCache::AddMemBase(RiscVGen::RiscVReg reg) { + _assert_(reg >= X0 && reg <= X31); +#ifdef MASKED_PSP_MEMORY + // This destroys the value... + _dbg_assert_(!ar[reg].isDirty); + emit_->SLLIW(reg, reg, 2); + emit_->SRLIW(reg, reg, 2); + emit_->ADD(reg, reg, MEMBASEREG); +#else + // Clear the top bits to be safe. + if (cpu_info.RiscV_Zba) { + emit_->ADD_UW(reg, reg, MEMBASEREG); + } else { + _assert_(XLEN == 64); + emit_->SLLI(reg, reg, 32); + emit_->SRLI(reg, reg, 32); + emit_->ADD(reg, reg, MEMBASEREG); + } +#endif + ar[reg].normalized32 = false; +} + +void RiscVRegCache::MapIn(IRRegIndex rs) { + MapReg(rs); +} + +void RiscVRegCache::MapInIn(IRRegIndex rd, IRRegIndex rs) { + SpillLock(rd, rs); + MapReg(rd); + MapReg(rs); + ReleaseSpillLock(rd, rs); +} + +void RiscVRegCache::MapDirtyIn(IRRegIndex rd, IRRegIndex rs, MapType type) { + SpillLock(rd, rs); + bool load = type == MapType::ALWAYS_LOAD || rd == rs; + MIPSMap norm32 = type == MapType::AVOID_LOAD_MARK_NORM32 ? MIPSMap::MARK_NORM32 : MIPSMap::INIT; + MapReg(rd, (load ? MIPSMap::DIRTY : MIPSMap::NOINIT) | norm32); + MapReg(rs); + ReleaseSpillLock(rd, rs); +} + +void RiscVRegCache::MapDirtyInIn(IRRegIndex rd, IRRegIndex rs, IRRegIndex rt, MapType type) { + SpillLock(rd, rs, rt); + bool load = type == MapType::ALWAYS_LOAD || (rd == rs || rd == rt); + MIPSMap norm32 = type == MapType::AVOID_LOAD_MARK_NORM32 ? MIPSMap::MARK_NORM32 : MIPSMap::INIT; + MapReg(rd, (load ? MIPSMap::DIRTY : MIPSMap::NOINIT) | norm32); + MapReg(rt); + MapReg(rs); + ReleaseSpillLock(rd, rs, rt); +} + +void RiscVRegCache::MapDirtyDirtyIn(IRRegIndex rd1, IRRegIndex rd2, IRRegIndex rs, MapType type) { + SpillLock(rd1, rd2, rs); + bool load1 = type == MapType::ALWAYS_LOAD || rd1 == rs; + bool load2 = type == MapType::ALWAYS_LOAD || rd2 == rs; + MIPSMap norm32 = type == MapType::AVOID_LOAD_MARK_NORM32 ? MIPSMap::MARK_NORM32 : MIPSMap::INIT; + MapReg(rd1, (load1 ? MIPSMap::DIRTY : MIPSMap::NOINIT) | norm32); + MapReg(rd2, (load2 ? MIPSMap::DIRTY : MIPSMap::NOINIT) | norm32); + MapReg(rs); + ReleaseSpillLock(rd1, rd2, rs); +} + +void RiscVRegCache::MapDirtyDirtyInIn(IRRegIndex rd1, IRRegIndex rd2, IRRegIndex rs, IRRegIndex rt, MapType type) { + SpillLock(rd1, rd2, rs, rt); + bool load1 = type == MapType::ALWAYS_LOAD || (rd1 == rs || rd1 == rt); + bool load2 = type == MapType::ALWAYS_LOAD || (rd2 == rs || rd2 == rt); + MIPSMap norm32 = type == MapType::AVOID_LOAD_MARK_NORM32 ? MIPSMap::MARK_NORM32 : MIPSMap::INIT; + MapReg(rd1, (load1 ? MIPSMap::DIRTY : MIPSMap::NOINIT) | norm32); + MapReg(rd2, (load2 ? MIPSMap::DIRTY : MIPSMap::NOINIT) | norm32); + MapReg(rt); + MapReg(rs); + ReleaseSpillLock(rd1, rd2, rs, rt); +} + +void RiscVRegCache::FlushRiscVReg(RiscVReg r) { + _dbg_assert_(r > X0 && r <= X31); + _dbg_assert_(ar[r].mipsReg != MIPS_REG_ZERO); + if (r == INVALID_REG) { + ERROR_LOG(JIT, "FlushRiscVReg called on invalid register %d", r); + return; + } + if (ar[r].mipsReg == IRREG_INVALID) { + // Nothing to do, reg not mapped. + _dbg_assert_(!ar[r].isDirty); + return; + } + _dbg_assert_(!mr[ar[r].mipsReg].isStatic); + if (mr[ar[r].mipsReg].isStatic) { + ERROR_LOG(JIT, "Cannot FlushRiscVReg a statically mapped register"); + return; + } + auto &mreg = mr[ar[r].mipsReg]; + if (mreg.loc == MIPSLoc::RVREG_IMM || ar[r].mipsReg == MIPS_REG_ZERO) { + // We know its immediate value, no need to STR now. + mreg.loc = MIPSLoc::IMM; + mreg.reg = INVALID_REG; + } else { + if (mreg.loc == MIPSLoc::IMM || ar[r].isDirty) { + if (mreg.loc == MIPSLoc::RVREG_AS_PTR) { + // Unpointerify, in case dirty. +#ifdef MASKED_PSP_MEMORY + _dbg_assert_(!ar[r].isDirty); +#endif + emit_->SUB(r, r, MEMBASEREG); + mreg.loc = MIPSLoc::RVREG; + ar[r].normalized32 = false; + } + RiscVReg storeReg = RiscVRegForFlush(ar[r].mipsReg); + if (storeReg != INVALID_REG) + emit_->SW(storeReg, CTXREG, GetMipsRegOffset(ar[r].mipsReg)); + } + mreg.loc = MIPSLoc::MEM; + mreg.reg = INVALID_REG; + mreg.imm = -1; + } + ar[r].isDirty = false; + ar[r].mipsReg = IRREG_INVALID; + ar[r].pointerified = false; +} + +void RiscVRegCache::DiscardR(IRRegIndex mipsReg) { + _dbg_assert_(IsValidRegNoZero(mipsReg)); + if (mr[mipsReg].isStatic) { + // Simply do nothing unless it's an IMM/RVREG_IMM/RVREG_AS_PTR, in case we just switch it over to RVREG, losing the value. + RiscVReg riscvReg = mr[mipsReg].reg; + _dbg_assert_(riscvReg != INVALID_REG); + if (mipsReg == MIPS_REG_ZERO) { + // Shouldn't happen, but in case it does. + mr[mipsReg].loc = MIPSLoc::RVREG_IMM; + mr[mipsReg].reg = R_ZERO; + mr[mipsReg].imm = 0; + } else if (mr[mipsReg].loc == MIPSLoc::RVREG_IMM || mr[mipsReg].loc == MIPSLoc::IMM || mr[mipsReg].loc == MIPSLoc::RVREG_AS_PTR) { + // Ignore the imm value, restore sanity + mr[mipsReg].loc = MIPSLoc::RVREG; + ar[riscvReg].pointerified = false; + ar[riscvReg].isDirty = false; + ar[riscvReg].normalized32 = false; + } + return; + } + const MIPSLoc prevLoc = mr[mipsReg].loc; + if (prevLoc == MIPSLoc::RVREG || prevLoc == MIPSLoc::RVREG_IMM || prevLoc == MIPSLoc::RVREG_AS_PTR) { + RiscVReg riscvReg = mr[mipsReg].reg; + _dbg_assert_(riscvReg != INVALID_REG); + ar[riscvReg].mipsReg = IRREG_INVALID; + ar[riscvReg].pointerified = false; + ar[riscvReg].isDirty = false; + ar[riscvReg].normalized32 = false; + mr[mipsReg].reg = INVALID_REG; + mr[mipsReg].loc = MIPSLoc::MEM; + mr[mipsReg].imm = -1; + } + if (prevLoc == MIPSLoc::IMM && mipsReg != MIPS_REG_ZERO) { + mr[mipsReg].loc = MIPSLoc::MEM; + mr[mipsReg].imm = -1; + } +} + +RiscVReg RiscVRegCache::RiscVRegForFlush(IRRegIndex r) { + _dbg_assert_(IsValidReg(r)); + if (mr[r].isStatic) + return INVALID_REG; // No flushing needed + + switch (mr[r].loc) { + case MIPSLoc::IMM: + if (r == MIPS_REG_ZERO) { + return INVALID_REG; + } + // Zero is super easy. + if (mr[r].imm == 0) { + return R_ZERO; + } + // Could we get lucky? Check for an exact match in another rvreg. + for (int i = 0; i < NUM_MIPSREG; ++i) { + if (mr[i].loc == MIPSLoc::RVREG_IMM && mr[i].imm == mr[r].imm) { + // Awesome, let's just store this reg. + return mr[i].reg; + } + } + return INVALID_REG; + + case MIPSLoc::RVREG: + case MIPSLoc::RVREG_IMM: + if (mr[r].reg == INVALID_REG) { + ERROR_LOG_REPORT(JIT, "RiscVRegForFlush: MipsReg %d had bad riscvReg", r); + return INVALID_REG; + } + // No need to flush if it's zero or not dirty. + if (r == MIPS_REG_ZERO || !ar[mr[r].reg].isDirty) { + return INVALID_REG; + } + // TODO: Lo/hi optimization? + return mr[r].reg; + + case MIPSLoc::RVREG_AS_PTR: + return INVALID_REG; + + case MIPSLoc::MEM: + return INVALID_REG; + + default: + ERROR_LOG_REPORT(JIT, "RiscVRegForFlush: MipsReg %d with invalid location %d", r, (int)mr[r].loc); + return INVALID_REG; + } +} + +void RiscVRegCache::FlushR(IRRegIndex r) { + _dbg_assert_(IsValidRegNoZero(r)); + if (mr[r].isStatic) { + ERROR_LOG(JIT, "Cannot flush static reg %d", r); + return; + } + + switch (mr[r].loc) { + case MIPSLoc::IMM: + // IMM is always "dirty". + // TODO: HI/LO optimization? + if (r != MIPS_REG_ZERO) { + // Try to optimize using a different reg. + RiscVReg storeReg = RiscVRegForFlush(r); + if (storeReg == INVALID_REG) { + SetRegImm(SCRATCH1, mr[r].imm); + storeReg = SCRATCH1; + } + emit_->SW(storeReg, CTXREG, GetMipsRegOffset(r)); + } + break; + + case MIPSLoc::RVREG: + case MIPSLoc::RVREG_IMM: + if (ar[mr[r].reg].isDirty) { + RiscVReg storeReg = RiscVRegForFlush(r); + if (storeReg != INVALID_REG) { + emit_->SW(storeReg, CTXREG, GetMipsRegOffset(r)); + } + ar[mr[r].reg].isDirty = false; + } + ar[mr[r].reg].mipsReg = IRREG_INVALID; + ar[mr[r].reg].pointerified = false; + break; + + case MIPSLoc::RVREG_AS_PTR: + if (ar[mr[r].reg].isDirty) { +#ifdef MASKED_PSP_MEMORY + // This is kinda bad, because we've cleared bits in it. + _dbg_assert_(!ar[mr[r].reg].isDirty); +#endif + emit_->SUB(mr[r].reg, mr[r].reg, MEMBASEREG); + // We set this so RiscVRegForFlush knows it's no longer a pointer. + mr[r].loc = MIPSLoc::RVREG; + RiscVReg storeReg = RiscVRegForFlush(r); + if (storeReg != INVALID_REG) { + emit_->SW(storeReg, CTXREG, GetMipsRegOffset(r)); + } + ar[mr[r].reg].isDirty = false; + } + ar[mr[r].reg].mipsReg = IRREG_INVALID; + break; + + case MIPSLoc::MEM: + // Already there, nothing to do. + break; + + default: + ERROR_LOG_REPORT(JIT, "FlushR: MipsReg %d with invalid location %d", r, (int)mr[r].loc); + break; + } + if (r == MIPS_REG_ZERO) { + mr[r].loc = MIPSLoc::RVREG_IMM; + mr[r].reg = R_ZERO; + mr[r].imm = 0; + } else { + mr[r].loc = MIPSLoc::MEM; + mr[r].reg = INVALID_REG; + mr[r].imm = -1; + } +} + +void RiscVRegCache::FlushAll() { + // Note: make sure not to change the registers when flushing: + // Branching code expects the armreg to retain its value. + + // TODO: HI/LO optimization? + + // Final pass to grab any that were left behind. + for (int i = 1; i < NUM_MIPSREG; i++) { + IRRegIndex mipsReg = IRRegIndex(i); + if (mr[i].isStatic) { + RiscVReg riscvReg = mr[i].reg; + // Cannot leave any IMMs in registers, not even ML_ARMREG_IMM, can confuse the regalloc later if this flush is mid-block + // due to an interpreter fallback that changes the register. + if (mr[i].loc == MIPSLoc::IMM) { + SetRegImm(mr[i].reg, mr[i].imm); + mr[i].loc = MIPSLoc::RVREG; + ar[riscvReg].pointerified = false; + } else if (mr[i].loc == MIPSLoc::RVREG_IMM) { + // The register already contains the immediate. + if (ar[riscvReg].pointerified) { + ERROR_LOG(JIT, "RVREG_IMM but pointerified. Wrong."); + ar[riscvReg].pointerified = false; + } + mr[i].loc = MIPSLoc::RVREG; + } else if (mr[i].loc == MIPSLoc::RVREG_AS_PTR) { +#ifdef MASKED_PSP_MEMORY + _dbg_assert_(!ar[riscvReg].isDirty); +#endif + emit_->SUB(riscvReg, riscvReg, MEMBASEREG); + mr[i].loc = MIPSLoc::RVREG; + } + if (i != MIPS_REG_ZERO && mr[i].reg == INVALID_REG) { + ERROR_LOG(JIT, "RV reg of static %i is invalid", i); + continue; + } + } else if (IsValidRegNoZero(mipsReg)) { + FlushR(mipsReg); + } + } + + int count = 0; + const StaticAllocation *allocs = GetStaticAllocations(count); + for (int i = 0; i < count; i++) { + if (allocs[i].pointerified && !ar[allocs[i].ar].pointerified && jo_->enablePointerify) { + // Re-pointerify + _dbg_assert_(mr[allocs[i].mr].loc == MIPSLoc::RVREG); + AddMemBase(allocs[i].ar); + ar[allocs[i].ar].pointerified = true; + } else if (!allocs[i].pointerified) { + // If this register got pointerified on the way, mark it as not. + // This is so that after save/reload (like in an interpreter fallback), + // it won't be regarded as such, as it may no longer be. + ar[allocs[i].ar].pointerified = false; + } + } + // Sanity check + for (int i = 0; i < NUM_RVREG; i++) { + if (ar[i].mipsReg != IRREG_INVALID && mr[ar[i].mipsReg].isStatic == false) { + ERROR_LOG_REPORT(JIT, "Flush fail: ar[%i].mipsReg=%i", i, ar[i].mipsReg); + } + } +} + +void RiscVRegCache::SetImm(IRRegIndex r, u64 immVal) { + _dbg_assert_(IsValidReg(r)); + if (r == MIPS_REG_ZERO && immVal != 0) { + ERROR_LOG_REPORT(JIT, "Trying to set immediate %08x to r0", (u32)immVal); + return; + } + + if (mr[r].loc == MIPSLoc::RVREG_IMM && mr[r].imm == immVal) { + // Already have that value, let's keep it in the reg. + return; + } + + // TODO: HI/LO optimization? + // All regs on the PSP are 32 bit, but LO we treat as HI:LO so is 64 full bits. + immVal = immVal & 0xFFFFFFFF; + + if (mr[r].isStatic) { + mr[r].loc = MIPSLoc::IMM; + mr[r].imm = immVal; + ar[mr[r].reg].pointerified = false; + ar[mr[r].reg].normalized32 = false; + // We do not change reg to INVALID_REG for obvious reasons.. + } else { + // Zap existing value if cached in a reg + if (mr[r].reg != INVALID_REG) { + ar[mr[r].reg].mipsReg = IRREG_INVALID; + ar[mr[r].reg].isDirty = false; + ar[mr[r].reg].pointerified = false; + ar[mr[r].reg].normalized32 = false; + } + mr[r].loc = MIPSLoc::IMM; + mr[r].imm = immVal; + mr[r].reg = INVALID_REG; + } +} + +bool RiscVRegCache::IsImm(IRRegIndex r) const { + _dbg_assert_(IsValidReg(r)); + if (r == MIPS_REG_ZERO) + return true; + else + return mr[r].loc == MIPSLoc::IMM || mr[r].loc == MIPSLoc::RVREG_IMM; +} + +u64 RiscVRegCache::GetImm(IRRegIndex r) const { + _dbg_assert_(IsValidReg(r)); + if (r == MIPS_REG_ZERO) + return 0; + if (mr[r].loc != MIPSLoc::IMM && mr[r].loc != MIPSLoc::RVREG_IMM) { + ERROR_LOG_REPORT(JIT, "Trying to get imm from non-imm register %i", r); + } + return mr[r].imm; +} + +int RiscVRegCache::GetMipsRegOffset(IRRegIndex r) { + _dbg_assert_(IsValidReg(r)); + return r * 4; +} + +bool RiscVRegCache::IsValidReg(IRRegIndex r) const { + if (r < 0 || r >= NUM_MIPSREG) + return false; + + // See MIPSState for these offsets. + + // Don't allow FPU or VFPU regs here. + if (r >= 32 && r < 32 + 32 + 128) + return false; + // Also disallow VFPU temps. + if (r >= 224 && r < 224 + 16) + return false; + // Don't allow nextPC, etc. since it's probably a mistake. + if (r > IRREG_FPCOND && r != IRREG_LLBIT) + return false; + // Don't allow PC either. + if (r == 241) + return false; + + return true; +} + +bool RiscVRegCache::IsValidRegNoZero(IRRegIndex r) const { + return IsValidReg(r) && r != MIPS_REG_ZERO; +} + +void RiscVRegCache::SpillLock(IRRegIndex r1, IRRegIndex r2, IRRegIndex r3, IRRegIndex r4) { + _dbg_assert_(IsValidReg(r1)); + _dbg_assert_(r2 == IRREG_INVALID || IsValidReg(r2)); + _dbg_assert_(r3 == IRREG_INVALID || IsValidReg(r3)); + _dbg_assert_(r4 == IRREG_INVALID || IsValidReg(r4)); + mr[r1].spillLock = true; + if (r2 != IRREG_INVALID) mr[r2].spillLock = true; + if (r3 != IRREG_INVALID) mr[r3].spillLock = true; + if (r4 != IRREG_INVALID) mr[r4].spillLock = true; + pendingUnlock_ = true; +} + +void RiscVRegCache::ReleaseSpillLocksAndDiscardTemps() { + if (!pendingUnlock_) + return; + + for (int i = 0; i < NUM_MIPSREG; i++) { + if (!mr[i].isStatic) + mr[i].spillLock = false; + } + for (int i = 0; i < NUM_RVREG; i++) { + ar[i].tempLocked = false; + } + + pendingUnlock_ = false; +} + +void RiscVRegCache::ReleaseSpillLock(IRRegIndex r1, IRRegIndex r2, IRRegIndex r3, IRRegIndex r4) { + _dbg_assert_(IsValidReg(r1)); + _dbg_assert_(r2 == IRREG_INVALID || IsValidReg(r2)); + _dbg_assert_(r3 == IRREG_INVALID || IsValidReg(r3)); + _dbg_assert_(r4 == IRREG_INVALID || IsValidReg(r4)); + if (!mr[r1].isStatic) + mr[r1].spillLock = false; + if (r2 != IRREG_INVALID && !mr[r2].isStatic) + mr[r2].spillLock = false; + if (r3 != IRREG_INVALID && !mr[r3].isStatic) + mr[r3].spillLock = false; + if (r4 != IRREG_INVALID && !mr[r4].isStatic) + mr[r4].spillLock = false; +} + +RiscVReg RiscVRegCache::R(IRRegIndex mipsReg) { + _dbg_assert_(IsValidReg(mipsReg)); + _dbg_assert_(mr[mipsReg].loc == MIPSLoc::RVREG || mr[mipsReg].loc == MIPSLoc::RVREG_IMM); + if (mr[mipsReg].loc == MIPSLoc::RVREG || mr[mipsReg].loc == MIPSLoc::RVREG_IMM) { + return mr[mipsReg].reg; + } else { + ERROR_LOG_REPORT(JIT, "Reg %i not in riscv reg", mipsReg); + return INVALID_REG; // BAAAD + } +} + +RiscVReg RiscVRegCache::RPtr(IRRegIndex mipsReg) { + _dbg_assert_(IsValidReg(mipsReg)); + _dbg_assert_(mr[mipsReg].loc == MIPSLoc::RVREG || mr[mipsReg].loc == MIPSLoc::RVREG_IMM || mr[mipsReg].loc == MIPSLoc::RVREG_AS_PTR); + if (mr[mipsReg].loc == MIPSLoc::RVREG_AS_PTR) { + return mr[mipsReg].reg; + } else if (mr[mipsReg].loc == MIPSLoc::RVREG || mr[mipsReg].loc == MIPSLoc::RVREG_IMM) { + int rv = mr[mipsReg].reg; + _dbg_assert_(ar[rv].pointerified); + if (ar[rv].pointerified) { + return mr[mipsReg].reg; + } else { + ERROR_LOG(JIT, "Tried to use a non-pointer register as a pointer"); + return INVALID_REG; + } + } else { + ERROR_LOG_REPORT(JIT, "Reg %i not in riscv reg", mipsReg); + return INVALID_REG; // BAAAD + } +} diff --git a/Core/MIPS/RiscV/RiscVRegCache.h b/Core/MIPS/RiscV/RiscVRegCache.h new file mode 100644 index 0000000000..ffe373c639 --- /dev/null +++ b/Core/MIPS/RiscV/RiscVRegCache.h @@ -0,0 +1,197 @@ +// Copyright (c) 2023- PPSSPP Project. + +// 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, version 2.0 or later versions. + +// 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 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#pragma once + +#include "Common/RiscVEmitter.h" +#include "Core/MIPS/MIPS.h" +#include "Core/MIPS/IR/IRJit.h" + +namespace RiscVJitConstants { + +// Note: we don't support 32-bit or 128-bit CPUs currently. +constexpr int XLEN = 64; + +const RiscVGen::RiscVReg DOWNCOUNTREG = RiscVGen::X24; +const RiscVGen::RiscVReg JITBASEREG = RiscVGen::X25; +const RiscVGen::RiscVReg CTXREG = RiscVGen::X26; +const RiscVGen::RiscVReg MEMBASEREG = RiscVGen::X27; +// TODO: Experiment. X7-X13 are compressed regs. X8/X9 are saved so nice for static alloc, though. +const RiscVGen::RiscVReg SCRATCH1 = RiscVGen::X10; +const RiscVGen::RiscVReg SCRATCH2 = RiscVGen::X11; + +// Have to account for all of them due to temps, etc. +constexpr int TOTAL_MAPPABLE_MIPSREGS = 256; + +enum class MIPSLoc { + IMM, + RVREG, + // In a native reg, but an adjusted pointer (not pointerified - unaligned.) + RVREG_AS_PTR, + // In a native reg, but also has a known immediate value. + RVREG_IMM, + MEM, +}; + +// Initing is the default so the flag is reversed. +enum class MIPSMap { + INIT = 0, + DIRTY = 1, + NOINIT = 2 | DIRTY, + MARK_NORM32 = 4, +}; +static inline MIPSMap operator |(const MIPSMap &lhs, const MIPSMap &rhs) { + return MIPSMap((int)lhs | (int)rhs); +} +static inline MIPSMap operator &(const MIPSMap &lhs, const MIPSMap &rhs) { + return MIPSMap((int)lhs & (int)rhs); +} + +enum class MapType { + AVOID_LOAD, + AVOID_LOAD_MARK_NORM32, + ALWAYS_LOAD, +}; + +} // namespace RiscVJitConstants + +namespace MIPSComp { +struct JitOptions; +} + +// Not using IRReg since this can be -1. +typedef int IRRegIndex; +constexpr IRRegIndex IRREG_INVALID = -1; + +struct RegStatusRiscV { + IRRegIndex mipsReg; // if -1, no mipsreg attached. + bool isDirty; // Should the register be written back? + bool pointerified; // Has added the memory base into the top part of the reg. Note - still usable as 32-bit reg (in only some cases.) + bool tempLocked; // Reserved for a temp register. + bool normalized32; // 32 bits sign extended to XLEN. RISC-V can't always address just the low 32-bits, so this matters. +}; + +struct RegStatusMIPS { + // Where is this MIPS register? + RiscVJitConstants::MIPSLoc loc; + // Data (both or only one may be used, depending on loc.) + u64 imm; + RiscVGen::RiscVReg reg; // reg index + bool spillLock; // if true, this register cannot be spilled. + bool isStatic; // if true, this register will not be written back to ram by the regcache + // If loc == ML_MEM, it's back in its location in the CPU context struct. +}; + +class RiscVRegCache { +public: + RiscVRegCache(MIPSState *mipsState, MIPSComp::JitOptions *jo); + ~RiscVRegCache() {} + + void Init(RiscVGen::RiscVEmitter *emitter); + void Start(MIPSComp::IRBlock *irBlock); + void SetIRIndex(int index) { + irIndex_ = index; + } + + // Protect the arm register containing a MIPS register from spilling, to ensure that + // it's being kept allocated. + void SpillLock(IRRegIndex reg, IRRegIndex reg2 = IRREG_INVALID, IRRegIndex reg3 = IRREG_INVALID, IRRegIndex reg4 = IRREG_INVALID); + void ReleaseSpillLock(IRRegIndex reg, IRRegIndex reg2 = IRREG_INVALID, IRRegIndex reg3 = IRREG_INVALID, IRRegIndex reg4 = IRREG_INVALID); + void ReleaseSpillLocksAndDiscardTemps(); + + void SetImm(IRRegIndex reg, u64 immVal); + bool IsImm(IRRegIndex reg) const; + u64 GetImm(IRRegIndex reg) const; + + // May fail and return INVALID_REG if it needs flushing. + RiscVGen::RiscVReg TryMapTempImm(IRRegIndex); + + // Returns an ARM register containing the requested MIPS register. + RiscVGen::RiscVReg MapReg(IRRegIndex reg, RiscVJitConstants::MIPSMap mapFlags = RiscVJitConstants::MIPSMap::INIT); + RiscVGen::RiscVReg MapRegAsPointer(IRRegIndex reg); + + bool IsMapped(IRRegIndex reg); + bool IsMappedAsPointer(IRRegIndex reg); + bool IsMappedAsStaticPointer(IRRegIndex reg); + bool IsInRAM(IRRegIndex reg); + bool IsNormalized32(IRRegIndex reg); + + void MarkDirty(RiscVGen::RiscVReg reg, bool andNormalized32 = false); + void MarkPtrDirty(RiscVGen::RiscVReg reg); + // Copies to another reg if specified, otherwise same reg. + RiscVGen::RiscVReg Normalize32(IRRegIndex reg, RiscVGen::RiscVReg destReg = RiscVGen::INVALID_REG); + void MapIn(IRRegIndex rs); + void MapInIn(IRRegIndex rd, IRRegIndex rs); + void MapDirtyIn(IRRegIndex rd, IRRegIndex rs, RiscVJitConstants::MapType type = RiscVJitConstants::MapType::AVOID_LOAD); + void MapDirtyInIn(IRRegIndex rd, IRRegIndex rs, IRRegIndex rt, RiscVJitConstants::MapType type = RiscVJitConstants::MapType::AVOID_LOAD); + void MapDirtyDirtyIn(IRRegIndex rd1, IRRegIndex rd2, IRRegIndex rs, RiscVJitConstants::MapType type = RiscVJitConstants::MapType::AVOID_LOAD); + void MapDirtyDirtyInIn(IRRegIndex rd1, IRRegIndex rd2, IRRegIndex rs, IRRegIndex rt, RiscVJitConstants::MapType type = RiscVJitConstants::MapType::AVOID_LOAD); + void FlushBeforeCall(); + void FlushAll(); + void FlushR(IRRegIndex r); + void FlushRiscVReg(RiscVGen::RiscVReg r); + void DiscardR(IRRegIndex r); + + RiscVGen::RiscVReg GetAndLockTempR(); + + RiscVGen::RiscVReg R(IRRegIndex preg); // Returns a cached register, while checking that it's NOT mapped as a pointer + RiscVGen::RiscVReg RPtr(IRRegIndex preg); // Returns a cached register, if it has been mapped as a pointer + + // These are called once on startup to generate functions, that you should then call. + void EmitLoadStaticRegisters(); + void EmitSaveStaticRegisters(); + +private: + struct StaticAllocation { + IRRegIndex mr; + RiscVGen::RiscVReg ar; + bool pointerified; + }; + const StaticAllocation *GetStaticAllocations(int &count); + const RiscVGen::RiscVReg *GetMIPSAllocationOrder(int &count); + void MapRegTo(RiscVGen::RiscVReg reg, IRRegIndex mipsReg, RiscVJitConstants::MIPSMap mapFlags); + RiscVGen::RiscVReg AllocateReg(); + RiscVGen::RiscVReg FindBestToSpill(bool unusedOnly, bool *clobbered); + RiscVGen::RiscVReg RiscVRegForFlush(IRRegIndex r); + void SetRegImm(RiscVGen::RiscVReg reg, u64 imm); + void AddMemBase(RiscVGen::RiscVReg reg); + int GetMipsRegOffset(IRRegIndex r); + + bool IsValidReg(IRRegIndex r) const; + bool IsValidRegNoZero(IRRegIndex r) const; + + void SetupInitialRegs(); + + MIPSState *mips_; + RiscVGen::RiscVEmitter *emit_ = nullptr; + MIPSComp::JitOptions *jo_; + MIPSComp::IRBlock *irBlock_ = nullptr; + int irIndex_ = 0; + + enum { + NUM_RVREG = 32, // 31 actual registers, plus the zero/sp register which is not mappable. + NUM_MIPSREG = RiscVJitConstants::TOTAL_MAPPABLE_MIPSREGS, + }; + + RegStatusRiscV ar[NUM_RVREG]{}; + RegStatusMIPS mr[NUM_MIPSREG]{}; + + bool initialReady_ = false; + bool pendingUnlock_ = false; + RegStatusRiscV arInitial_[NUM_RVREG]; + RegStatusMIPS mrInitial_[NUM_MIPSREG]; +}; diff --git a/Core/MIPS/RiscV/RiscVRegCacheFPU.cpp b/Core/MIPS/RiscV/RiscVRegCacheFPU.cpp new file mode 100644 index 0000000000..c99261b9f9 --- /dev/null +++ b/Core/MIPS/RiscV/RiscVRegCacheFPU.cpp @@ -0,0 +1,450 @@ +// Copyright (c) 2023- PPSSPP Project. + +// 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, version 2.0 or later versions. + +// 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 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#ifndef offsetof +#include +#endif + +#include "Common/CPUDetect.h" +#include "Core/MIPS/IR/IRInst.h" +#include "Core/MIPS/IR/IRAnalysis.h" +#include "Core/MIPS/RiscV/RiscVRegCacheFPU.h" +#include "Core/MIPS/JitCommon/JitState.h" +#include "Core/Reporting.h" + +using namespace RiscVGen; +using namespace RiscVJitConstants; + +RiscVRegCacheFPU::RiscVRegCacheFPU(MIPSState *mipsState, MIPSComp::JitOptions *jo) + : mips_(mipsState), jo_(jo) {} + +void RiscVRegCacheFPU::Init(RiscVEmitter *emitter) { + emit_ = emitter; +} + +void RiscVRegCacheFPU::Start(MIPSComp::IRBlock *irBlock) { + if (!initialReady_) { + SetupInitialRegs(); + initialReady_ = true; + } + + memcpy(ar, arInitial_, sizeof(ar)); + memcpy(mr, mrInitial_, sizeof(mr)); + pendingFlush_ = false; + + irBlock_ = irBlock; + irIndex_ = 0; +} + +void RiscVRegCacheFPU::SetupInitialRegs() { + for (int i = 0; i < NUM_RVFPUREG; i++) { + arInitial_[i].mipsReg = IRREG_INVALID; + arInitial_[i].isDirty = false; + } + for (int i = 0; i < NUM_MIPSFPUREG; i++) { + mrInitial_[i].loc = MIPSLoc::MEM; + mrInitial_[i].reg = (int)INVALID_REG; + mrInitial_[i].spillLock = false; + } +} + +const RiscVReg *RiscVRegCacheFPU::GetMIPSAllocationOrder(int &count) { + // F8 through F15 are used for compression, so they are great. + // TODO: Maybe we could remove some saved regs since we rarely need that many? Or maybe worth it? + static const RiscVReg allocationOrder[] = { + F8, F9, F10, F11, F12, F13, F14, F15, + F0, F1, F2, F3, F4, F5, F6, F7, + F16, F17, F18, F19, F20, F21, F22, F23, F24, F25, F26, F27, F28, F29, F30, F31, + }; + + count = ARRAY_SIZE(allocationOrder); + return allocationOrder; +} + +bool RiscVRegCacheFPU::IsInRAM(IRRegIndex reg) { + _dbg_assert_(IsValidReg(reg)); + return mr[reg].loc == MIPSLoc::MEM; +} + +bool RiscVRegCacheFPU::IsMapped(IRRegIndex mipsReg) { + _dbg_assert_(IsValidReg(mipsReg)); + return mr[mipsReg].loc == MIPSLoc::RVREG; +} + +RiscVReg RiscVRegCacheFPU::MapReg(IRRegIndex mipsReg, MIPSMap mapFlags) { + _dbg_assert_(IsValidReg(mipsReg)); + _dbg_assert_(mr[mipsReg].loc == MIPSLoc::MEM || mr[mipsReg].loc == MIPSLoc::RVREG); + + pendingFlush_ = true; + + // Let's see if it's already mapped. If so we just need to update the dirty flag. + // We don't need to check for NOINIT because we assume that anyone who maps + // with that flag immediately writes a "known" value to the register. + if (mr[mipsReg].loc == MIPSLoc::RVREG) { + _assert_msg_(ar[mr[mipsReg].reg].mipsReg == mipsReg, "GPU mapping out of sync, IR=%i", mipsReg); + if ((mapFlags & MIPSMap::DIRTY) == MIPSMap::DIRTY) { + ar[mr[mipsReg].reg].isDirty = true; + } + return (RiscVReg)(mr[mipsReg].reg + F0); + } + + // Okay, not mapped, so we need to allocate an RV register. + RiscVReg reg = AllocateReg(); + if (reg != INVALID_REG) { + // That means it's free. Grab it, and load the value into it (if requested). + ar[reg - F0].isDirty = (mapFlags & MIPSMap::DIRTY) == MIPSMap::DIRTY; + if ((mapFlags & MIPSMap::NOINIT) != MIPSMap::NOINIT) { + if (mr[mipsReg].loc == MIPSLoc::MEM) { + emit_->FL(32, reg, CTXREG, GetMipsRegOffset(mipsReg)); + } + } + ar[reg - F0].mipsReg = mipsReg; + mr[mipsReg].loc = MIPSLoc::RVREG; + mr[mipsReg].reg = reg - F0; + return reg; + } + + return reg; +} + +RiscVReg RiscVRegCacheFPU::AllocateReg() { + int allocCount = 0; + const RiscVReg *allocOrder = GetMIPSAllocationOrder(allocCount); + +allocate: + for (int i = 0; i < allocCount; i++) { + RiscVReg reg = allocOrder[i]; + + if (ar[reg - F0].mipsReg == IRREG_INVALID) { + return reg; + } + } + + // Still nothing. Let's spill a reg and goto 10. + bool clobbered; + RiscVReg bestToSpill = FindBestToSpill(true, &clobbered); + if (bestToSpill == INVALID_REG) { + bestToSpill = FindBestToSpill(false, &clobbered); + } + + if (bestToSpill != INVALID_REG) { + if (clobbered) { + DiscardR(ar[bestToSpill - F0].mipsReg); + } else { + FlushRiscVReg(bestToSpill); + } + // Now one must be free. + goto allocate; + } + + // Uh oh, we have all of them spilllocked.... + ERROR_LOG_REPORT(JIT, "Out of spillable registers near PC %08x", mips_->pc); + _assert_(bestToSpill != INVALID_REG); + return INVALID_REG; +} + +RiscVReg RiscVRegCacheFPU::FindBestToSpill(bool unusedOnly, bool *clobbered) { + int allocCount = 0; + const RiscVReg *allocOrder = GetMIPSAllocationOrder(allocCount); + + static const int UNUSED_LOOKAHEAD_OPS = 30; + + IRSituation info; + info.lookaheadCount = UNUSED_LOOKAHEAD_OPS; + info.currentIndex = irIndex_; + info.instructions = irBlock_->GetInstructions(); + info.numInstructions = irBlock_->GetNumInstructions(); + + *clobbered = false; + for (int i = 0; i < allocCount; i++) { + RiscVReg reg = allocOrder[i]; + if (ar[reg - F0].mipsReg != IRREG_INVALID && mr[ar[reg - F0].mipsReg].spillLock) + continue; + + // As it's in alloc-order, we know it's not static so we don't need to check for that. + IRUsage usage = IRNextFPRUsage(ar[reg - F0].mipsReg, info); + + // Awesome, a clobbered reg. Let's use it. + if (usage == IRUsage::CLOBBERED) { + *clobbered = true; + return reg; + } + + // Not awesome. A used reg. Let's try to avoid spilling. + if (!unusedOnly || usage == IRUsage::UNUSED) { + // TODO: Use age or something to choose which register to spill? + // TODO: Spill dirty regs first? or opposite? + return reg; + } + } + + return INVALID_REG; +} + +void RiscVRegCacheFPU::MapInIn(IRRegIndex rd, IRRegIndex rs) { + SpillLock(rd, rs); + MapReg(rd); + MapReg(rs); + ReleaseSpillLock(rd, rs); +} + +void RiscVRegCacheFPU::MapDirtyIn(IRRegIndex rd, IRRegIndex rs, bool avoidLoad) { + SpillLock(rd, rs); + bool load = !avoidLoad || rd == rs; + MapReg(rd, load ? MIPSMap::DIRTY : MIPSMap::NOINIT); + MapReg(rs); + ReleaseSpillLock(rd, rs); +} + +void RiscVRegCacheFPU::MapDirtyInIn(IRRegIndex rd, IRRegIndex rs, IRRegIndex rt, bool avoidLoad) { + SpillLock(rd, rs, rt); + bool load = !avoidLoad || (rd == rs || rd == rt); + MapReg(rd, load ? MIPSMap::DIRTY : MIPSMap::NOINIT); + MapReg(rt); + MapReg(rs); + ReleaseSpillLock(rd, rs, rt); +} + +RiscVReg RiscVRegCacheFPU::MapDirtyInTemp(IRRegIndex rd, IRRegIndex rs, bool avoidLoad) { + SpillLock(rd, rs); + bool load = !avoidLoad || rd == rs; + MapReg(rd, load ? MIPSMap::DIRTY : MIPSMap::NOINIT); + MapReg(rs); + RiscVReg temp = AllocateReg(); + ReleaseSpillLock(rd, rs); + return temp; +} + +void RiscVRegCacheFPU::Map4DirtyIn(IRRegIndex rdbase, IRRegIndex rsbase, bool avoidLoad) { + for (int i = 0; i < 4; ++i) + SpillLock(rdbase + i, rsbase + i); + bool load = !avoidLoad || (rdbase < rsbase + 4 && rdbase + 4 > rsbase); + for (int i = 0; i < 4; ++i) + MapReg(rdbase + i, load ? MIPSMap::DIRTY : MIPSMap::NOINIT); + for (int i = 0; i < 4; ++i) + MapReg(rsbase + i); + for (int i = 0; i < 4; ++i) + ReleaseSpillLock(rdbase + i, rsbase + i); +} + +void RiscVRegCacheFPU::Map4DirtyInIn(IRRegIndex rdbase, IRRegIndex rsbase, IRRegIndex rtbase, bool avoidLoad) { + for (int i = 0; i < 4; ++i) + SpillLock(rdbase + i, rsbase + i, rtbase + i); + bool load = !avoidLoad || (rdbase < rsbase + 4 && rdbase + 4 > rsbase) || (rdbase < rtbase + 4 && rdbase + 4 > rtbase); + for (int i = 0; i < 4; ++i) + MapReg(rdbase + i, load ? MIPSMap::DIRTY : MIPSMap::NOINIT); + for (int i = 0; i < 4; ++i) + MapReg(rsbase + i); + for (int i = 0; i < 4; ++i) + MapReg(rtbase + i); + for (int i = 0; i < 4; ++i) + ReleaseSpillLock(rdbase + i, rsbase + i, rtbase + i); +} + +void RiscVRegCacheFPU::FlushRiscVReg(RiscVReg r) { + _dbg_assert_(r >= F0 && r <= F31); + int reg = r - F0; + if (ar[reg].mipsReg == IRREG_INVALID) { + // Nothing to do, reg not mapped. + return; + } + if (ar[reg].isDirty && mr[ar[reg].mipsReg].loc == MIPSLoc::RVREG) { + emit_->FS(32, r, CTXREG, GetMipsRegOffset(ar[reg].mipsReg)); + } + mr[ar[reg].mipsReg].loc = MIPSLoc::MEM; + mr[ar[reg].mipsReg].reg = (int)INVALID_REG; + ar[reg].mipsReg = IRREG_INVALID; + ar[reg].isDirty = false; +} + +void RiscVRegCacheFPU::FlushR(IRRegIndex r) { + _dbg_assert_(IsValidReg(r)); + RiscVReg reg = RiscVRegForFlush(r); + if (reg != INVALID_REG) + FlushRiscVReg(reg); +} + +RiscVReg RiscVRegCacheFPU::RiscVRegForFlush(IRRegIndex r) { + _dbg_assert_(IsValidReg(r)); + switch (mr[r].loc) { + case MIPSLoc::RVREG: + _assert_msg_(mr[r].reg != INVALID_REG, "RiscVRegForFlush: IR %d had bad RiscVReg", r); + if (mr[r].reg == INVALID_REG) { + return INVALID_REG; + } + return (RiscVReg)(F0 + mr[r].reg); + + case MIPSLoc::MEM: + return INVALID_REG; + + default: + _assert_(false); + return INVALID_REG; + } +} + +void RiscVRegCacheFPU::FlushBeforeCall() { + // Note: don't set this false at the end, since we don't flush everything. + if (!pendingFlush_) { + return; + } + + // These registers are not preserved by function calls. + for (int i = 0; i <= 7; ++i) { + FlushRiscVReg(RiscVReg(F0 + i)); + } + for (int i = 10; i <= 17; ++i) { + FlushRiscVReg(RiscVReg(F0 + i)); + } + for (int i = 28; i <= 31; ++i) { + FlushRiscVReg(RiscVReg(F0 + i)); + } +} + +void RiscVRegCacheFPU::FlushAll() { + if (!pendingFlush_) { + // Nothing allocated. FPU regs are not nearly as common as GPR. + return; + } + + int numRVRegs = 0; + const RiscVReg *order = GetMIPSAllocationOrder(numRVRegs); + + for (int i = 0; i < numRVRegs; i++) { + int a = order[i] - F0; + int m = ar[a].mipsReg; + + if (ar[a].isDirty) { + _assert_(m != MIPS_REG_INVALID); + emit_->FS(32, order[i], CTXREG, GetMipsRegOffset(m)); + + mr[m].loc = MIPSLoc::MEM; + mr[m].reg = (int)INVALID_REG; + ar[a].mipsReg = IRREG_INVALID; + ar[a].isDirty = false; + } else { + if (m != IRREG_INVALID) { + mr[m].loc = MIPSLoc::MEM; + mr[m].reg = (int)INVALID_REG; + } + ar[a].mipsReg = IRREG_INVALID; + } + } + + pendingFlush_ = false; +} + +void RiscVRegCacheFPU::DiscardR(IRRegIndex r) { + _dbg_assert_(IsValidReg(r)); + switch (mr[r].loc) { + case MIPSLoc::RVREG: + _assert_(mr[r].reg != INVALID_REG); + if (mr[r].reg != INVALID_REG) { + // Note that we DO NOT write it back here. That's the whole point of Discard. + ar[mr[r].reg].isDirty = false; + ar[mr[r].reg].mipsReg = IRREG_INVALID; + } + break; + + case MIPSLoc::MEM: + // Already there, nothing to do. + break; + + default: + _assert_(false); + break; + } + mr[r].loc = MIPSLoc::MEM; + mr[r].reg = (int)INVALID_REG; + mr[r].spillLock = false; +} + +int RiscVRegCacheFPU::GetMipsRegOffset(IRRegIndex r) { + _assert_(IsValidReg(r)); + // These are offsets within the MIPSState structure. + // IR gives us an index that is already 32 after the state index (skipping GPRs.) + return (32 + r) * 4; +} + +void RiscVRegCacheFPU::SpillLock(IRRegIndex r1, IRRegIndex r2, IRRegIndex r3, IRRegIndex r4) { + _dbg_assert_(IsValidReg(r1)); + _dbg_assert_(r2 == IRREG_INVALID || IsValidReg(r2)); + _dbg_assert_(r3 == IRREG_INVALID || IsValidReg(r3)); + _dbg_assert_(r4 == IRREG_INVALID || IsValidReg(r4)); + mr[r1].spillLock = true; + if (r2 != IRREG_INVALID) + mr[r2].spillLock = true; + if (r3 != IRREG_INVALID) + mr[r3].spillLock = true; + if (r4 != IRREG_INVALID) + mr[r4].spillLock = true; + pendingUnlock_ = true; +} + +void RiscVRegCacheFPU::ReleaseSpillLocksAndDiscardTemps() { + if (!pendingUnlock_) + return; + + for (int i = 0; i < NUM_MIPSFPUREG; i++) { + mr[i].spillLock = false; + } + + pendingUnlock_ = false; +} + +void RiscVRegCacheFPU::ReleaseSpillLock(IRRegIndex r1, IRRegIndex r2, IRRegIndex r3, IRRegIndex r4) { + _dbg_assert_(IsValidReg(r1)); + _dbg_assert_(r2 == IRREG_INVALID || IsValidReg(r2)); + _dbg_assert_(r3 == IRREG_INVALID || IsValidReg(r3)); + _dbg_assert_(r4 == IRREG_INVALID || IsValidReg(r4)); + mr[r1].spillLock = false; + if (r2 != IRREG_INVALID) + mr[r2].spillLock = false; + if (r3 != IRREG_INVALID) + mr[r3].spillLock = false; + if (r4 != IRREG_INVALID) + mr[r4].spillLock = false; +} + +RiscVReg RiscVRegCacheFPU::R(IRRegIndex mipsReg) { + _dbg_assert_(IsValidReg(mipsReg)); + _dbg_assert_(mr[mipsReg].loc == MIPSLoc::RVREG); + if (mr[mipsReg].loc == MIPSLoc::RVREG) { + return (RiscVReg)(mr[mipsReg].reg + F0); + } else { + ERROR_LOG_REPORT(JIT, "Reg %i not in riscv reg", mipsReg); + return INVALID_REG; // BAAAD + } +} + +bool RiscVRegCacheFPU::IsValidReg(IRRegIndex r) const { + if (r < 0 || r >= NUM_MIPSFPUREG) + return false; + + // See MIPSState for these offsets. + int index = r + 32; + + // Allow FPU or VFPU regs here. + if (index >= 32 && index < 32 + 32 + 128) + return true; + // Also allow VFPU temps. + if (index >= 224 && index < 224 + 16) + return true; + + // Nothing else is allowed for the FPU side cache. + return false; +} diff --git a/Core/MIPS/RiscV/RiscVRegCacheFPU.h b/Core/MIPS/RiscV/RiscVRegCacheFPU.h new file mode 100644 index 0000000000..9edda169aa --- /dev/null +++ b/Core/MIPS/RiscV/RiscVRegCacheFPU.h @@ -0,0 +1,111 @@ +// Copyright (c) 2023- PPSSPP Project. + +// 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, version 2.0 or later versions. + +// 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 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#pragma once + +#include "Common/RiscVEmitter.h" +#include "Core/MIPS/MIPS.h" +#include "Core/MIPS/RiscV/RiscVRegCache.h" + +struct FPURegStatusRiscV { + int mipsReg; // if -1, no mipsreg attached. + bool isDirty; // Should the register be written back? +}; + +struct FPURegStatusMIPS { + // Where is this MIPS register? + RiscVJitConstants::MIPSLoc loc; + // Index from F0. + int reg; + + bool spillLock; // if true, this register cannot be spilled. + // If loc == ML_MEM, it's back in its location in the CPU context struct. +}; + +namespace MIPSComp { +struct JitOptions; +} + +class RiscVRegCacheFPU { +public: + RiscVRegCacheFPU(MIPSState *mipsState, MIPSComp::JitOptions *jo); + ~RiscVRegCacheFPU() {} + + void Init(RiscVGen::RiscVEmitter *emitter); + void Start(MIPSComp::IRBlock *irBlock); + void SetIRIndex(int index) { + irIndex_ = index; + } + + // Protect the RISC-V register containing a MIPS register from spilling, to ensure that + // it's being kept allocated. + void SpillLock(IRRegIndex reg, IRRegIndex reg2 = IRREG_INVALID, IRRegIndex reg3 = IRREG_INVALID, IRRegIndex reg4 = IRREG_INVALID); + void ReleaseSpillLock(IRRegIndex reg, IRRegIndex reg2 = IRREG_INVALID, IRRegIndex reg3 = IRREG_INVALID, IRRegIndex reg4 = IRREG_INVALID); + void ReleaseSpillLocksAndDiscardTemps(); + + // Returns a RISC-V register containing the requested MIPS register. + RiscVGen::RiscVReg MapReg(IRRegIndex reg, RiscVJitConstants::MIPSMap mapFlags = RiscVJitConstants::MIPSMap::INIT); + + bool IsMapped(IRRegIndex r); + bool IsInRAM(IRRegIndex r); + + void MapInIn(IRRegIndex rd, IRRegIndex rs); + void MapDirtyIn(IRRegIndex rd, IRRegIndex rs, bool avoidLoad = true); + void MapDirtyInIn(IRRegIndex rd, IRRegIndex rs, IRRegIndex rt, bool avoidLoad = true); + RiscVGen::RiscVReg MapDirtyInTemp(IRRegIndex rd, IRRegIndex rs, bool avoidLoad = true); + void Map4DirtyIn(IRRegIndex rdbase, IRRegIndex rsbase, bool avoidLoad = true); + void Map4DirtyInIn(IRRegIndex rdbase, IRRegIndex rsbase, IRRegIndex rtbase, bool avoidLoad = true); + void FlushBeforeCall(); + void FlushAll(); + void FlushR(IRRegIndex r); + void FlushRiscVReg(RiscVGen::RiscVReg r); + void DiscardR(IRRegIndex r); + + RiscVGen::RiscVReg R(int preg); // Returns a cached register + +private: + const RiscVGen::RiscVReg *GetMIPSAllocationOrder(int &count); + RiscVGen::RiscVReg AllocateReg(); + RiscVGen::RiscVReg FindBestToSpill(bool unusedOnly, bool *clobbered); + RiscVGen::RiscVReg RiscVRegForFlush(IRRegIndex r); + int GetMipsRegOffset(IRRegIndex r); + + bool IsValidReg(IRRegIndex r) const; + + void SetupInitialRegs(); + + MIPSState *mips_; + RiscVGen::RiscVEmitter *emit_ = nullptr; + MIPSComp::JitOptions *jo_; + MIPSComp::IRBlock *irBlock_ = nullptr; + int irIndex_ = 0; + + enum { + // On RiscV, each of the 32 registers are full 128-bit. No sharing of components! + NUM_RVFPUREG = 32, + NUM_MIPSFPUREG = RiscVJitConstants::TOTAL_MAPPABLE_MIPSREGS - 32, + }; + + FPURegStatusRiscV ar[NUM_RVFPUREG]; + FPURegStatusMIPS mr[NUM_MIPSFPUREG]; + + bool pendingFlush_ = false; + bool pendingUnlock_ = false; + bool initialReady_ = false; + FPURegStatusRiscV arInitial_[NUM_RVFPUREG]; + FPURegStatusMIPS mrInitial_[NUM_MIPSFPUREG]; +}; diff --git a/Core/MIPS/fake/FakeJit.cpp b/Core/MIPS/fake/FakeJit.cpp index f833526a66..3e696814c8 100644 --- a/Core/MIPS/fake/FakeJit.cpp +++ b/Core/MIPS/fake/FakeJit.cpp @@ -153,10 +153,6 @@ void FakeJit::Comp_RunBlock(MIPSOpcode op) ERROR_LOG(JIT, "Comp_RunBlock should never be reached!"); } -bool FakeJit::ReplaceJalTo(u32 dest) { - return true; -} - void FakeJit::Comp_ReplacementFunc(MIPSOpcode op) { } diff --git a/Core/MIPS/fake/FakeJit.h b/Core/MIPS/fake/FakeJit.h index 44cfefec05..bce7fe92d3 100644 --- a/Core/MIPS/fake/FakeJit.h +++ b/Core/MIPS/fake/FakeJit.h @@ -60,6 +60,7 @@ public: // Ops void Comp_ITypeMem(MIPSOpcode op) override {} + void Comp_StoreSync(MIPSOpcode op) override {} void Comp_Cache(MIPSOpcode op) override {} void Comp_RelBranch(MIPSOpcode op) override {} @@ -162,8 +163,6 @@ private: void MovFromPC(FakeReg r); void MovToPC(FakeReg r); - bool ReplaceJalTo(u32 dest); - void SaveDowncount(); void RestoreDowncount(); diff --git a/Core/MIPS/x86/CompLoadStore.cpp b/Core/MIPS/x86/CompLoadStore.cpp index feb627d3e8..603e3e367a 100644 --- a/Core/MIPS/x86/CompLoadStore.cpp +++ b/Core/MIPS/x86/CompLoadStore.cpp @@ -44,6 +44,7 @@ // #define CONDITIONAL_DISABLE(flag) { Comp_Generic(op); return; } #define CONDITIONAL_DISABLE(flag) if (jo.Disabled(JitDisable::flag)) { Comp_Generic(op); return; } #define DISABLE { Comp_Generic(op); return; } +#define INVALIDOP { Comp_Generic(op); return; } namespace MIPSComp { using namespace Gen; @@ -405,6 +406,42 @@ namespace MIPSComp { } + void Jit::Comp_StoreSync(MIPSOpcode op) { + CONDITIONAL_DISABLE(LSU); + + int offset = _IMM16; + MIPSGPReg rt = _RT; + MIPSGPReg rs = _RS; + // Note: still does something even if loading to zero. + + CheckMemoryBreakpoint(0, rs, offset); + + FixupBranch skipStore; + FixupBranch finish; + switch (op >> 26) { + case 48: // ll + CompITypeMemRead(op, 32, &XEmitter::MOVZX, safeMemFuncs.readU32); + MOV(8, MDisp(X64JitConstants::CTXREG, -128 + offsetof(MIPSState, llBit)), Imm8(1)); + break; + + case 56: // sc + CMP(8, MDisp(X64JitConstants::CTXREG, -128 + offsetof(MIPSState, llBit)), Imm8(1)); + skipStore = J_CC(CC_NE); + + CompITypeMemWrite(op, 32, safeMemFuncs.writeU32); + MOV(32, gpr.R(rt), Imm32(1)); + finish = J(); + + SetJumpTarget(skipStore); + MOV(32, gpr.R(rt), Imm32(0)); + SetJumpTarget(finish); + break; + + default: + INVALIDOP; + } + } + void Jit::Comp_Cache(MIPSOpcode op) { CONDITIONAL_DISABLE(LSU); diff --git a/Core/MIPS/x86/Jit.h b/Core/MIPS/x86/Jit.h index 2e2f806fd8..0a56cfae5b 100644 --- a/Core/MIPS/x86/Jit.h +++ b/Core/MIPS/x86/Jit.h @@ -69,6 +69,7 @@ public: // Ops void Comp_ITypeMem(MIPSOpcode op) override; + void Comp_StoreSync(MIPSOpcode op) override; void Comp_Cache(MIPSOpcode op) override; void Comp_RelBranch(MIPSOpcode op) override; diff --git a/Core/MemFault.cpp b/Core/MemFault.cpp index fe413d6a26..95a7088ae3 100644 --- a/Core/MemFault.cpp +++ b/Core/MemFault.cpp @@ -263,10 +263,15 @@ bool HandleFault(uintptr_t hostAddress, void *ctx) { #endif Core_ExecException(targetAddr, currentMIPS->pc, ExecExceptionType::JUMP); // Redirect execution to a crash handler that will switch to CoreState::CORE_RUNTIME_ERROR immediately. - context->CTX_PC = (uintptr_t)MIPSComp::jit->GetCrashHandler(); - ERROR_LOG(MEMMAP, "Bad execution access detected, halting: %08x (last known pc %08x, host: %p)", targetAddr, currentMIPS->pc, (void *)hostAddress); - inCrashHandler = false; - return true; + uintptr_t crashHandler = (uintptr_t)MIPSComp::jit->GetCrashHandler(); + if (crashHandler != 0) { + context->CTX_PC = crashHandler; + ERROR_LOG(MEMMAP, "Bad execution access detected, halting: %08x (last known pc %08x, host: %p)", targetAddr, currentMIPS->pc, (void *)hostAddress); + inCrashHandler = false; + return true; + } + + type = MemoryExceptionType::UNKNOWN; } else if (success) { if (info.isMemoryWrite) { type = MemoryExceptionType::WRITE_WORD; @@ -303,8 +308,11 @@ bool HandleFault(uintptr_t hostAddress, void *ctx) { g_lastCrashAddress = codePtr; // Redirect execution to a crash handler that will switch to CoreState::CORE_RUNTIME_ERROR immediately. + uintptr_t crashHandler = 0; if (MIPSComp::jit) - context->CTX_PC = (uintptr_t)MIPSComp::jit->GetCrashHandler(); + crashHandler = (uintptr_t)MIPSComp::jit->GetCrashHandler(); + if (crashHandler != 0) + context->CTX_PC = crashHandler; else handled = false; ERROR_LOG(MEMMAP, "Bad memory access detected! %08x (%p) Stopping emulation. Info:\n%s", guestAddress, (void *)hostAddress, infoString.c_str()); diff --git a/Core/Reporting.cpp b/Core/Reporting.cpp index bedcbf8d85..df6c7a84f5 100644 --- a/Core/Reporting.cpp +++ b/Core/Reporting.cpp @@ -272,7 +272,7 @@ namespace Reporting bool SendReportRequest(const char *uri, const std::string &data, const std::string &mimeType, Buffer *output = NULL) { http::Client http; - http::RequestProgress progress(&pendingMessagesDone); + net::RequestProgress progress(&pendingMessagesDone); Buffer theVoid = Buffer::Void(); http.SetUserAgent(StringFromFormat("PPSSPP/%s", PPSSPP_GIT_VERSION)); diff --git a/Core/RetroAchievements.cpp b/Core/RetroAchievements.cpp index 97ec1efcd8..22b3c3bcee 100644 --- a/Core/RetroAchievements.cpp +++ b/Core/RetroAchievements.cpp @@ -77,6 +77,7 @@ std::string s_game_hash; std::set g_activeChallenges; bool g_isIdentifying = false; bool g_isLoggingIn = false; +int g_loginResult; // rc_client implementation static rc_client_t *g_rcClient; @@ -88,7 +89,7 @@ rc_client_t *GetClient() { } bool IsLoggedIn() { - return rc_client_get_user_info(g_rcClient) != nullptr; + return rc_client_get_user_info(g_rcClient) != nullptr && !g_isLoggingIn; } bool EncoreModeActive() { @@ -128,7 +129,11 @@ bool WarnUserIfChallengeModeActive(const char *message) { } bool IsBlockingExecution() { - return g_isIdentifying || g_isLoggingIn; + if (g_isLoggingIn || g_isIdentifying) { + // Useful for debugging race conditions. + // INFO_LOG(ACHIEVEMENTS, "isLoggingIn: %d isIdentifying: %d", (int)g_isLoggingIn, (int)g_isIdentifying); + } + return g_isLoggingIn || g_isIdentifying; } static u32 GetGameID() { @@ -175,8 +180,9 @@ static void server_call_callback(const rc_api_request_t *request, rc_client_server_callback_t callback, void *callback_data, rc_client_t *client) { // If post data is provided, we need to make a POST request, otherwise, a GET request will suffice. + auto ac = GetI18NCategory(I18NCat::ACHIEVEMENTS); if (request->post_data) { - g_DownloadManager.AsyncPostWithCallback(std::string(request->url), std::string(request->post_data), "application/x-www-form-urlencoded", [=](http::Download &download) { + std::shared_ptr download = g_DownloadManager.AsyncPostWithCallback(std::string(request->url), std::string(request->post_data), "application/x-www-form-urlencoded", http::ProgressBarMode::DELAYED, [=](http::Request &download) { std::string buffer; download.buffer().TakeAll(&buffer); rc_api_server_response_t response{}; @@ -184,9 +190,9 @@ static void server_call_callback(const rc_api_request_t *request, response.body_length = buffer.size(); response.http_status_code = download.ResultCode(); callback(&response, callback_data); - }); + }, ac->T("Contacting RetroAchievements server...")); } else { - g_DownloadManager.StartDownloadWithCallback(std::string(request->url), Path(), [=](http::Download &download) { + std::shared_ptr download = g_DownloadManager.StartDownloadWithCallback(std::string(request->url), Path(), http::ProgressBarMode::DELAYED, [=](http::Request &download) { std::string buffer; download.buffer().TakeAll(&buffer); rc_api_server_response_t response{}; @@ -194,7 +200,7 @@ static void server_call_callback(const rc_api_request_t *request, response.body_length = buffer.size(); response.http_status_code = download.ResultCode(); callback(&response, callback_data); - }); + }, ac->T("Contacting RetroAchievements server...")); } } @@ -243,40 +249,52 @@ static void event_handler_callback(const rc_client_event_t *event, rc_client_t * g_OSD.Show(OSDType::MESSAGE_INFO, ApplySafeSubstitutions(ac->T("%1: Leaderboard attempt started"), event->leaderboard->title), DeNull(event->leaderboard->description), 3.0f); break; case RC_CLIENT_EVENT_LEADERBOARD_FAILED: - NOTICE_LOG(ACHIEVEMENTS, "Leaderboard attempt failed: %s", event->leaderboard->title); + INFO_LOG(ACHIEVEMENTS, "Leaderboard attempt failed: %s", event->leaderboard->title); g_OSD.Show(OSDType::MESSAGE_INFO, ApplySafeSubstitutions(ac->T("%1: Leaderboard attempt failed"), event->leaderboard->title), 3.0f); // A leaderboard attempt has failed. break; case RC_CLIENT_EVENT_LEADERBOARD_SUBMITTED: - NOTICE_LOG(ACHIEVEMENTS, "Leaderboard result submitted: %s", event->leaderboard->title); + INFO_LOG(ACHIEVEMENTS, "Leaderboard result submitted: %s", event->leaderboard->title); g_OSD.Show(OSDType::MESSAGE_SUCCESS, ApplySafeSubstitutions(ac->T("Submitted %1 for %2"), DeNull(event->leaderboard->tracker_value), DeNull(event->leaderboard->title)), DeNull(event->leaderboard->description), 3.0f); System_PostUIMessage("play_sound", "leaderboard_submitted"); break; case RC_CLIENT_EVENT_ACHIEVEMENT_CHALLENGE_INDICATOR_SHOW: - NOTICE_LOG(ACHIEVEMENTS, "Challenge indicator show: %s", event->achievement->title); + INFO_LOG(ACHIEVEMENTS, "Challenge indicator show: %s", event->achievement->title); g_OSD.ShowChallengeIndicator(event->achievement->id, true); g_activeChallenges.insert(event->achievement->id); // A challenge achievement has become active. The handler should show a small version of the achievement icon // to indicate the challenge is active. break; case RC_CLIENT_EVENT_ACHIEVEMENT_CHALLENGE_INDICATOR_HIDE: - NOTICE_LOG(ACHIEVEMENTS, "Challenge indicator hide: %s", event->achievement->title); + INFO_LOG(ACHIEVEMENTS, "Challenge indicator hide: %s", event->achievement->title); g_OSD.ShowChallengeIndicator(event->achievement->id, false); g_activeChallenges.erase(event->achievement->id); // The handler should hide the small version of the achievement icon that was shown by the corresponding RC_CLIENT_EVENT_ACHIEVEMENT_CHALLENGE_INDICATOR_SHOW event. break; case RC_CLIENT_EVENT_ACHIEVEMENT_PROGRESS_INDICATOR_SHOW: - NOTICE_LOG(ACHIEVEMENTS, "Progress indicator show: %s, progress: '%s' (%f)", event->achievement->title, event->achievement->measured_progress, event->achievement->measured_percent); + INFO_LOG(ACHIEVEMENTS, "Progress indicator show: %s, progress: '%s' (%f)", event->achievement->title, event->achievement->measured_progress, event->achievement->measured_percent); // An achievement that tracks progress has changed the amount of progress that has been made. // The handler should show a small version of the achievement icon along with the achievement->measured_progress text (for two seconds). // Only one progress indicator should be shown at a time. // If a progress indicator is already visible, it should be updated with the new icon and text, and the two second timer should be restarted. - g_OSD.ShowAchievementProgress(event->achievement->id, 2.0f); + g_OSD.ShowAchievementProgress(event->achievement->id, true); + break; + case RC_CLIENT_EVENT_ACHIEVEMENT_PROGRESS_INDICATOR_UPDATE: + INFO_LOG(ACHIEVEMENTS, "Progress indicator update: %s, progress: '%s' (%f)", event->achievement->title, event->achievement->measured_progress, event->achievement->measured_percent); + g_OSD.ShowAchievementProgress(event->achievement->id, true); + break; + case RC_CLIENT_EVENT_ACHIEVEMENT_PROGRESS_INDICATOR_HIDE: + INFO_LOG(ACHIEVEMENTS, "Progress indicator hide"); + // An achievement that tracks progress has changed the amount of progress that has been made. + // The handler should show a small version of the achievement icon along with the achievement->measured_progress text (for two seconds). + // Only one progress indicator should be shown at a time. + // If a progress indicator is already visible, it should be updated with the new icon and text, and the two second timer should be restarted. + g_OSD.ShowAchievementProgress(0, false); break; case RC_CLIENT_EVENT_LEADERBOARD_TRACKER_SHOW: - NOTICE_LOG(ACHIEVEMENTS, "Leaderboard tracker show: '%s' (id %d)", event->leaderboard_tracker->display, event->leaderboard_tracker->id); + INFO_LOG(ACHIEVEMENTS, "Leaderboard tracker show: '%s' (id %d)", event->leaderboard_tracker->display, event->leaderboard_tracker->id); // A leaderboard_tracker has become active. The handler should show the tracker text on screen. // Multiple active leaderboards may share a single tracker if they have the same definition and value. // As such, the leaderboard tracker IDs are unique amongst the leaderboard trackers, and have no correlation to the active leaderboard(s). @@ -285,12 +303,12 @@ static void event_handler_callback(const rc_client_event_t *event, rc_client_t * break; case RC_CLIENT_EVENT_LEADERBOARD_TRACKER_HIDE: // A leaderboard_tracker has become inactive. The handler should hide the tracker text from the screen. - NOTICE_LOG(ACHIEVEMENTS, "Leaderboard tracker hide: '%s' (id %d)", event->leaderboard_tracker->display, event->leaderboard_tracker->id); + INFO_LOG(ACHIEVEMENTS, "Leaderboard tracker hide: '%s' (id %d)", event->leaderboard_tracker->display, event->leaderboard_tracker->id); g_OSD.ShowLeaderboardTracker(event->leaderboard_tracker->id, nullptr, false); break; case RC_CLIENT_EVENT_LEADERBOARD_TRACKER_UPDATE: // A leaderboard_tracker value has been updated. The handler should update the tracker text on the screen. - NOTICE_LOG(ACHIEVEMENTS, "Leaderboard tracker update: '%s' (id %d)", event->leaderboard_tracker->display, event->leaderboard_tracker->id); + INFO_LOG(ACHIEVEMENTS, "Leaderboard tracker update: '%s' (id %d)", event->leaderboard_tracker->display, event->leaderboard_tracker->id); g_OSD.ShowLeaderboardTracker(event->leaderboard_tracker->id, event->leaderboard_tracker->display, true); break; case RC_CLIENT_EVENT_RESET: @@ -311,6 +329,7 @@ static void event_handler_callback(const rc_client_event_t *event, rc_client_t * static void login_token_callback(int result, const char *error_message, rc_client_t *client, void *userdata) { switch (result) { case RC_OK: + INFO_LOG(ACHIEVEMENTS, "Successful login by token."); OnAchievementsLoginStateChange(); break; case RC_NO_RESPONSE: @@ -319,15 +338,20 @@ static void login_token_callback(int result, const char *error_message, rc_clien g_OSD.Show(OSDType::MESSAGE_WARNING, di->T("Failed to connect to server, check your internet connection.")); break; } - case RC_INVALID_STATE: case RC_API_FAILURE: + case RC_INVALID_STATE: case RC_MISSING_VALUE: case RC_INVALID_JSON: default: - ERROR_LOG(ACHIEVEMENTS, "Failure logging in via token: %d, %s", result, error_message); + { + auto ac = GetI18NCategory(I18NCat::ACHIEVEMENTS); + ERROR_LOG(ACHIEVEMENTS, "Callback: Failure logging in via token: %d, %s", result, error_message); + g_OSD.Show(OSDType::MESSAGE_WARNING, ac->T("Failed logging in to RetroAchievements")); OnAchievementsLoginStateChange(); break; } + } + g_loginResult = result; g_isLoggingIn = false; } @@ -348,11 +372,26 @@ void Initialize() { // Provide a logging function to simplify debugging rc_client_enable_logging(g_rcClient, RC_CLIENT_LOG_LEVEL_VERBOSE, log_message_callback); - // Disable SSL for now. - rc_client_set_host(g_rcClient, "http://retroachievements.org"); + if (!System_GetPropertyBool(SYSPROP_SUPPORTS_HTTPS)) { + // Disable SSL if not supported by our platform implementation. + rc_client_set_host(g_rcClient, "http://retroachievements.org"); + } rc_client_set_event_handler(g_rcClient, event_handler_callback); + TryLoginByToken(); +} + +bool HasToken() { + return !NativeLoadSecret(RA_TOKEN_SECRET_NAME).empty(); +} + +bool LoginProblems(std::string *errorString) { + // TODO: Set error string. + return g_loginResult != RC_OK; +} + +void TryLoginByToken() { std::string api_token = NativeLoadSecret(RA_TOKEN_SECRET_NAME); if (!api_token.empty()) { g_isLoggingIn = true; @@ -394,7 +433,8 @@ static void login_password_callback(int result, const char *error_message, rc_cl } } - g_OSD.RemoveProgressBar("cheevos_async_login"); + g_OSD.RemoveProgressBar("cheevos_async_login", true, 0.1f); + g_loginResult = RC_OK; // For these, we don't want the "permanence" of the login-by-token failure, this prevents LoginProblems from returning true. g_isLoggingIn = false; } @@ -403,7 +443,7 @@ bool LoginAsync(const char *username, const char *password) { if (IsLoggedIn() || std::strlen(username) == 0 || std::strlen(password) == 0 || IsUsingRAIntegration()) return false; - g_OSD.SetProgressBar("cheevos_async_login", di->T("Logging in..."), 0, 0, 0); + g_OSD.SetProgressBar("cheevos_async_login", di->T("Logging in..."), 0, 0, 0, 0.0f); g_isLoggingIn = true; rc_client_begin_login_with_password(g_rcClient, username, password, &login_password_callback, nullptr); @@ -417,7 +457,7 @@ void Logout() { NativeSaveSecret(RA_TOKEN_SECRET_NAME, ""); g_Config.Save("Achievements logout"); g_activeChallenges.clear(); - + g_loginResult = RC_OK; // Allow trying again OnAchievementsLoginStateChange(); } @@ -540,7 +580,7 @@ bool HasAchievementsOrLeaderboards() { void DownloadImageIfMissing(const std::string &cache_key, std::string &&url) { if (g_iconCache.MarkPending(cache_key)) { INFO_LOG(ACHIEVEMENTS, "Downloading image: %s (%s)", url.c_str(), cache_key.c_str()); - g_DownloadManager.StartDownloadWithCallback(url, Path(), [cache_key](http::Download &download) { + g_DownloadManager.StartDownloadWithCallback(url, Path(), http::ProgressBarMode::NONE, [cache_key](http::Request &download) { if (download.ResultCode() != 200) return; std::string data; @@ -624,12 +664,14 @@ struct FileContext { static BlockDevice *g_blockDevice; +bool IsReadyToStart() { + return !g_isLoggingIn; +} + void SetGame(const Path &path, FileLoader *fileLoader) { - // If we are currently logging in, give it a couple of seconds. This is not ideal, but works. if (g_isLoggingIn) { - for (int i = 0; i < 3000 / 50; i++) { - sleep_ms(50); - } + // IsReadyToStart should have been checked, so we shouldn't be here. + ERROR_LOG(ACHIEVEMENTS, "Still logging in during SetGame - shouldn't happen"); } if (!g_rcClient || !IsLoggedIn()) { diff --git a/Core/RetroAchievements.h b/Core/RetroAchievements.h index 5e524cc79a..12ba7a3e2f 100644 --- a/Core/RetroAchievements.h +++ b/Core/RetroAchievements.h @@ -82,6 +82,10 @@ rc_client_t *GetClient(); void Initialize(); void UpdateSettings(); +bool LoginProblems(std::string *errorString); +bool HasToken(); +void TryLoginByToken(); + /// Called when the system is being shut down. If Shutdown() returns false, the shutdown should be aborted if possible. bool Shutdown(); @@ -102,6 +106,7 @@ bool HasAchievementsOrLeaderboards(); bool LoginAsync(const char *username, const char *password); void Logout(); +bool IsReadyToStart(); void SetGame(const Path &path, FileLoader *fileLoader); void ChangeUMD(const Path &path); // for in-game UMD change void UnloadGame(); // Call when leaving a game. diff --git a/Core/System.cpp b/Core/System.cpp index 6759e9b609..a3c401f109 100644 --- a/Core/System.cpp +++ b/Core/System.cpp @@ -418,6 +418,10 @@ bool PSP_InitStart(const CoreParameter &coreParam, std::string *error_string) { return false; } + if (!Achievements::IsReadyToStart()) { + return false; + } + #if defined(_WIN32) && PPSSPP_ARCH(AMD64) NOTICE_LOG(BOOT, "PPSSPP %s Windows 64 bit", PPSSPP_GIT_VERSION); #elif defined(_WIN32) && !PPSSPP_ARCH(AMD64) @@ -473,11 +477,6 @@ bool PSP_InitStart(const CoreParameter &coreParam, std::string *error_string) { } bool PSP_InitUpdate(std::string *error_string) { - if (Achievements::IsBlockingExecution()) { - // Keep waiting. - return false; - } - if (pspIsInited || !pspIsIniting) { return true; } @@ -487,7 +486,10 @@ bool PSP_InitUpdate(std::string *error_string) { } bool success = !g_CoreParameter.fileToStart.empty(); - *error_string = g_CoreParameter.errorString; + if (!g_CoreParameter.errorString.empty()) { + *error_string = g_CoreParameter.errorString; + } + if (success && gpu == nullptr) { PSP_SetLoading("Starting graphics..."); Draw::DrawContext *draw = g_CoreParameter.graphicsContext ? g_CoreParameter.graphicsContext->GetDrawContext() : nullptr; diff --git a/Core/Util/GameManager.cpp b/Core/Util/GameManager.cpp index 37b3b1dd6a..c8e09e4ab8 100644 --- a/Core/Util/GameManager.cpp +++ b/Core/Util/GameManager.cpp @@ -102,7 +102,7 @@ bool GameManager::DownloadAndInstall(std::string storeFileUrl) { Path filename = GetTempFilename(); const char *acceptMime = "application/zip, application/x-cso, application/x-iso9660-image, application/octet-stream; q=0.9, */*; q=0.8"; - curDownload_ = g_DownloadManager.StartDownload(storeFileUrl, filename, acceptMime); + curDownload_ = g_DownloadManager.StartDownload(storeFileUrl, filename, http::ProgressBarMode::VISIBLE, acceptMime); return true; } diff --git a/Core/Util/GameManager.h b/Core/Util/GameManager.h index 336a38908f..8aff30da05 100644 --- a/Core/Util/GameManager.h +++ b/Core/Util/GameManager.h @@ -91,7 +91,7 @@ private: std::string GetGameID(const Path &path) const; std::string GetPBPGameID(FileLoader *loader) const; std::string GetISOGameID(FileLoader *loader) const; - std::shared_ptr curDownload_; + std::shared_ptr curDownload_; std::shared_ptr installThread_; bool installInProgress_ = false; bool installDonePending_ = false; diff --git a/Core/Util/PortManager.cpp b/Core/Util/PortManager.cpp index e8af736825..cfb81e9dbd 100644 --- a/Core/Util/PortManager.cpp +++ b/Core/Util/PortManager.cpp @@ -49,21 +49,20 @@ std::recursive_mutex upnpLock; std::deque upnpReqs; PortManager::PortManager(): - urls(0), - datas(0), m_InitState(UPNP_INITSTATE_NONE), m_LocalPort(UPNP_LOCAL_PORT_ANY), m_leaseDuration("43200") { - // Since WSAStartup can be used multiple times it should be safe to do this right? - net::Init(); + // Don't call net::Init or similar here, we don't want stuff like that to happen before main. } PortManager::~PortManager() { // FIXME: On Windows it seems using any UPnP functions in this destructor that gets triggered when exiting PPSSPP will resulting to UPNPCOMMAND_HTTP_ERROR due to early WSACleanup (miniupnpc was getting WSANOTINITIALISED internally) +} + +void PortManager::Shutdown() { Clear(); Restore(); Terminate(); - net::Shutdown(); } void PortManager::Terminate() { @@ -502,9 +501,7 @@ int upnpService(const unsigned int timeout) // Cleaning up regardless of g_Config.bEnableUPnP to prevent lingering open ports on the router if (g_PortManager.GetInitState() == UPNP_INITSTATE_DONE) { - g_PortManager.Clear(); - g_PortManager.Restore(); - g_PortManager.Terminate(); + g_PortManager.Shutdown(); } // Should we ingore any leftover UPnP requests? instead of processing it on the next game start diff --git a/Core/Util/PortManager.h b/Core/Util/PortManager.h index 67f99a8fc5..31b9f67f95 100644 --- a/Core/Util/PortManager.h +++ b/Core/Util/PortManager.h @@ -83,9 +83,6 @@ public: // timeout: milliseconds to wait for a router to respond (default = 2000 ms) bool Initialize(const unsigned int timeout = 2000); - // Uninitialize/Reset the state - void Terminate(); - // Get UPnP Initialization status int GetInitState(); @@ -95,18 +92,24 @@ public: // Remove a port mapping (external port) bool Remove(const char* protocol, unsigned short port); + // Call on exit. Does a full shutdown. + void Shutdown(); + +private: + // Retrieves port lists mapped by PPSSPP for current LAN IP & other's applications + bool RefreshPortList(); + // Removes any lingering mapped ports created by PPSSPP (including from previous crashes) bool Clear(); // Restore ports mapped by others that were taken by PPSSPP, better used after Clear() bool Restore(); - // Retrieves port lists mapped by PPSSPP for current LAN IP & other's applications - bool RefreshPortList(); + // Uninitialize/Reset the state + void Terminate(); -protected: - struct UPNPUrls* urls = NULL; - struct IGDdatas* datas = NULL; + struct UPNPUrls* urls = nullptr; + struct IGDdatas* datas = nullptr; int m_InitState = UPNP_INITSTATE_NONE; int m_LocalPort = UPNP_LOCAL_PORT_ANY; diff --git a/Core/WebServer.cpp b/Core/WebServer.cpp index 9db7b0f5fd..a2192984c0 100644 --- a/Core/WebServer.cpp +++ b/Core/WebServer.cpp @@ -64,7 +64,8 @@ static ServerStatus RetrieveStatus() { static bool RegisterServer(int port) { bool success = false; http::Client http; - http::RequestProgress progress; + bool cancelled = false; + net::RequestProgress progress(&cancelled); Buffer theVoid = Buffer::Void(); http.SetUserAgent(StringFromFormat("PPSSPP/%s", PPSSPP_GIT_VERSION)); @@ -159,7 +160,7 @@ static Path LocalFromRemotePath(const std::string &path) { return Path(); } -static void DiscHandler(const http::Request &request, const Path &filename) { +static void DiscHandler(const http::ServerRequest &request, const Path &filename) { s64 sz = File::GetFileSize(filename); std::string range; @@ -211,7 +212,7 @@ static void DiscHandler(const http::Request &request, const Path &filename) { } } -static void HandleListing(const http::Request &request) { +static void HandleListing(const http::ServerRequest &request) { AndroidJNIThreadContext jniContext; request.WriteHttpResponseHeader("1.0", 200, -1, "text/plain"); @@ -230,7 +231,7 @@ static void HandleListing(const http::Request &request) { } } -static bool ServeDebuggerFile(const http::Request &request) { +static bool ServeDebuggerFile(const http::ServerRequest &request) { // Skip the slash at the start of the resource path. const char *filename = request.resource() + 1; if (strstr(filename, "..") != nullptr) @@ -264,13 +265,13 @@ static bool ServeDebuggerFile(const http::Request &request) { return true; } -static void RedirectToDebugger(const http::Request &request) { +static void RedirectToDebugger(const http::ServerRequest &request) { static const std::string payload = "Redirecting to debugger UI...\r\n"; request.WriteHttpResponseHeader("1.0", 301, (int)payload.size(), "text/plain", "Location: /debugger/index.html\r\n"); request.Out()->Push(payload); } -static void HandleFallback(const http::Request &request) { +static void HandleFallback(const http::ServerRequest &request) { AndroidJNIThreadContext jniContext; if (serverFlags & (int)WebServerFlags::DISCS) { @@ -296,7 +297,7 @@ static void HandleFallback(const http::Request &request) { request.Out()->Push(payload); } -static void ForwardDebuggerRequest(const http::Request &request) { +static void ForwardDebuggerRequest(const http::ServerRequest &request) { AndroidJNIThreadContext jniContext; if (serverFlags & (int)WebServerFlags::DEBUGGER) { diff --git a/GPU/Common/DrawEngineCommon.cpp b/GPU/Common/DrawEngineCommon.cpp index bd04fa13f1..de00bf5d6a 100644 --- a/GPU/Common/DrawEngineCommon.cpp +++ b/GPU/Common/DrawEngineCommon.cpp @@ -19,9 +19,9 @@ #include #include "Common/Data/Convert/ColorConv.h" -#include "Common/Math/lin/matrix4x4.h" #include "Common/Profiler/Profiler.h" #include "Common/LogReporting.h" +#include "Common/Math/lin/matrix4x4.h" #include "Core/Config.h" #include "GPU/Common/DrawEngineCommon.h" #include "GPU/Common/SplineCommon.h" @@ -136,21 +136,6 @@ std::string DrawEngineCommon::DebugGetVertexLoaderString(std::string id, DebugSh return dec ? dec->GetString(stringType) : "N/A"; } -struct Plane { - float x, y, z, w; - void Set(float _x, float _y, float _z, float _w) { x = _x; y = _y; z = _z; w = _w; } - float Test(const float f[3]) const { return x * f[0] + y * f[1] + z * f[2] + w; } -}; - -static void PlanesFromMatrix(const float mtx[16], Plane planes[6]) { - planes[0].Set(mtx[3]-mtx[0], mtx[7]-mtx[4], mtx[11]-mtx[8], mtx[15]-mtx[12]); // Right - planes[1].Set(mtx[3]+mtx[0], mtx[7]+mtx[4], mtx[11]+mtx[8], mtx[15]+mtx[12]); // Left - planes[2].Set(mtx[3]+mtx[1], mtx[7]+mtx[5], mtx[11]+mtx[9], mtx[15]+mtx[13]); // Bottom - planes[3].Set(mtx[3]-mtx[1], mtx[7]-mtx[5], mtx[11]-mtx[9], mtx[15]-mtx[13]); // Top - planes[4].Set(mtx[3]+mtx[2], mtx[7]+mtx[6], mtx[11]+mtx[10], mtx[15]+mtx[14]); // Near - planes[5].Set(mtx[3]-mtx[2], mtx[7]-mtx[6], mtx[11]-mtx[10], mtx[15]-mtx[14]); // Far -} - static Vec3f ClipToScreen(const Vec4f& coords) { float xScale = gstate.getViewportXScale(); float xCenter = gstate.getViewportXCenter(); @@ -250,6 +235,52 @@ void DrawEngineCommon::DispatchSubmitImm(GEPrimitiveType prim, TransformedVertex } } +// Gated by DIRTY_CULL_PLANES +void DrawEngineCommon::UpdatePlanes() { + float world[16]; + float view[16]; + float worldview[16]; + float worldviewproj[16]; + ConvertMatrix4x3To4x4(world, gstate.worldMatrix); + ConvertMatrix4x3To4x4(view, gstate.viewMatrix); + // TODO: Create a Matrix4x3ByMatrix4x3, and Matrix4x4ByMatrix4x3? + Matrix4ByMatrix4(worldview, world, view); + Matrix4ByMatrix4(worldviewproj, worldview, gstate.projMatrix); + + // Next, we need to apply viewport, scissor, region, and even offset - but only for X/Y. + // Note that the PSP does not clip against the viewport. + const Vec2f baseOffset = Vec2f(gstate.getOffsetX(), gstate.getOffsetY()); + // Region1 (rate) is used as an X1/Y1 here, matching PSP behavior. + minOffset_ = baseOffset + Vec2f(std::max(gstate.getRegionRateX() - 0x100, gstate.getScissorX1()), std::max(gstate.getRegionRateY() - 0x100, gstate.getScissorY1())) - Vec2f(1.0f, 1.0f); + maxOffset_ = baseOffset + Vec2f(std::min(gstate.getRegionX2(), gstate.getScissorX2()), std::min(gstate.getRegionY2(), gstate.getScissorY2())) + Vec2f(1.0f, 1.0f); + + // Now let's apply the viewport to our scissor/region + offset range. + Vec2f inverseViewportScale = Vec2f(1.0f / gstate.getViewportXScale(), 1.0f / gstate.getViewportYScale()); + Vec2f minViewport = (minOffset_ - Vec2f(gstate.getViewportXCenter(), gstate.getViewportYCenter())) * inverseViewportScale; + Vec2f maxViewport = (maxOffset_ - Vec2f(gstate.getViewportXCenter(), gstate.getViewportYCenter())) * inverseViewportScale; + + Lin::Matrix4x4 applyViewport; + applyViewport.empty(); + // Scale to the viewport's size. + applyViewport.xx = 2.0f / (maxViewport.x - minViewport.x); + applyViewport.yy = 2.0f / (maxViewport.y - minViewport.y); + applyViewport.zz = 1.0f; + applyViewport.ww = 1.0f; + // And offset to the viewport's centers. + applyViewport.wx = -(maxViewport.x + minViewport.x) / (maxViewport.x - minViewport.x); + applyViewport.wy = -(maxViewport.y + minViewport.y) / (maxViewport.y - minViewport.y); + + float mtx[16]; + Matrix4ByMatrix4(mtx, worldviewproj, applyViewport.m); + + planes_[0].Set(mtx[3] - mtx[0], mtx[7] - mtx[4], mtx[11] - mtx[8], mtx[15] - mtx[12]); // Right + planes_[1].Set(mtx[3] + mtx[0], mtx[7] + mtx[4], mtx[11] + mtx[8], mtx[15] + mtx[12]); // Left + planes_[2].Set(mtx[3] + mtx[1], mtx[7] + mtx[5], mtx[11] + mtx[9], mtx[15] + mtx[13]); // Bottom + planes_[3].Set(mtx[3] - mtx[1], mtx[7] - mtx[5], mtx[11] - mtx[9], mtx[15] - mtx[13]); // Top + planes_[4].Set(mtx[3] + mtx[2], mtx[7] + mtx[6], mtx[11] + mtx[10], mtx[15] + mtx[14]); // Near + planes_[5].Set(mtx[3] - mtx[2], mtx[7] - mtx[6], mtx[11] - mtx[10], mtx[15] - mtx[14]); // Far +} + // This code has plenty of potential for optimization. // // It does the simplest and safest test possible: If all points of a bbox is outside a single of @@ -273,7 +304,7 @@ bool DrawEngineCommon::TestBoundingBox(const void *control_points, const void *i verts[i] = vtx[i] * (1.0f / 128.0f); } } else if ((vertType & 0xFFFFFF) == GE_VTYPE_POS_16BIT && !inds) { - const s16 *vtx = (const s16*)control_points; + const s16 *vtx = (const s16 *)control_points; for (int i = 0; i < vertexCount * 3; i++) { verts[i] = vtx[i] * (1.0f / 32768.0f); } @@ -302,70 +333,42 @@ bool DrawEngineCommon::TestBoundingBox(const void *control_points, const void *i } } - Plane planes[6]; + // Due to world matrix updates per "thing", this isn't quite as effective as it could be if we did world transform + // in here as well. Though, it still does cut down on a lot of updates in Tekken 6. + if (gstate_c.IsDirty(DIRTY_CULL_PLANES)) { + UpdatePlanes(); + gpuStats.numPlaneUpdates++; + gstate_c.Clean(DIRTY_CULL_PLANES); + } - float world[16]; - float view[16]; - float worldview[16]; - float worldviewproj[16]; - ConvertMatrix4x3To4x4(world, gstate.worldMatrix); - ConvertMatrix4x3To4x4(view, gstate.viewMatrix); - // TODO: Create a Matrix4x3ByMatrix4x3, and Matrix4x4ByMatrix4x3? - Matrix4ByMatrix4(worldview, world, view); - Matrix4ByMatrix4(worldviewproj, worldview, gstate.projMatrix); - - // Next, we need to apply viewport, scissor, region, and even offset - but only for X/Y. - // Note that the PSP does not clip against the viewport. - const Vec2f baseOffset = Vec2f(gstate.getOffsetX(), gstate.getOffsetY()); - // Region1 (rate) is used as an X1/Y1 here, matching PSP behavior. - Vec2f minOffset = baseOffset + Vec2f(std::max(gstate.getRegionRateX() - 0x100, gstate.getScissorX1()), std::max(gstate.getRegionRateY() - 0x100, gstate.getScissorY1())) - Vec2f(1.0f, 1.0f); - Vec2f maxOffset = baseOffset + Vec2f(std::min(gstate.getRegionX2(), gstate.getScissorX2()), std::min(gstate.getRegionY2(), gstate.getScissorY2())) + Vec2f(1.0f, 1.0f); - - // Now let's apply the viewport to our scissor/region + offset range. - Vec2f inverseViewportScale = Vec2f(1.0f / gstate.getViewportXScale(), 1.0f / gstate.getViewportYScale()); - Vec2f minViewport = (minOffset - Vec2f(gstate.getViewportXCenter(), gstate.getViewportYCenter())) * inverseViewportScale; - Vec2f maxViewport = (maxOffset - Vec2f(gstate.getViewportXCenter(), gstate.getViewportYCenter())) * inverseViewportScale; - - Lin::Matrix4x4 applyViewport; - applyViewport.empty(); - // Scale to the viewport's size. - applyViewport.xx = 2.0f / (maxViewport.x - minViewport.x); - applyViewport.yy = 2.0f / (maxViewport.y - minViewport.y); - applyViewport.zz = 1.0f; - applyViewport.ww = 1.0f; - // And offset to the viewport's centers. - applyViewport.wx = -(maxViewport.x + minViewport.x) / (maxViewport.x - minViewport.x); - applyViewport.wy = -(maxViewport.y + minViewport.y) / (maxViewport.y - minViewport.y); - - float screenBounds[16]; - Matrix4ByMatrix4(screenBounds, worldviewproj, applyViewport.m); - - PlanesFromMatrix(screenBounds, planes); // Note: near/far are not checked without clamp/clip enabled, so we skip those planes. int totalPlanes = gstate.isDepthClampEnabled() ? 6 : 4; for (int plane = 0; plane < totalPlanes; plane++) { int inside = 0; int out = 0; for (int i = 0; i < vertexCount; i++) { - // Here we can test against the frustum planes! - float value = planes[plane].Test(verts + i * 3); + // Test against the frustum planes, and count. + // TODO: We should test 4 vertices at a time using SIMD. + // I guess could also test one vertex against 4 planes at a time, though a lot of waste at the common case of 6. + float value = planes_[plane].Test(verts + i * 3); if (value <= -FLT_EPSILON) out++; else inside++; } + // No vertices inside this one plane? Don't need to draw. if (inside == 0) { // All out - but check for X and Y if the offset was near the cullbox edge. bool outsideEdge = false; if (plane == 1) - outsideEdge = minOffset.x < 1.0f; + outsideEdge = minOffset_.x < 1.0f; if (plane == 2) - outsideEdge = minOffset.y < 1.0f; + outsideEdge = minOffset_.y < 1.0f; else if (plane == 0) - outsideEdge = maxOffset.x >= 4096.0f; + outsideEdge = maxOffset_.x >= 4096.0f; else if (plane == 3) - outsideEdge = maxOffset.y >= 4096.0f; + outsideEdge = maxOffset_.y >= 4096.0f; // Only consider this outside if offset + scissor/region is fully inside the cullbox. if (!outsideEdge) diff --git a/GPU/Common/DrawEngineCommon.h b/GPU/Common/DrawEngineCommon.h index b9be2ee2e4..beb3e27757 100644 --- a/GPU/Common/DrawEngineCommon.h +++ b/GPU/Common/DrawEngineCommon.h @@ -22,6 +22,7 @@ #include "Common/CommonTypes.h" #include "Common/Data/Collections/Hashmaps.h" +#include "GPU/Math3D.h" #include "GPU/GPUState.h" #include "GPU/Common/GPUStateUtils.h" #include "GPU/Common/GPUDebugInterface.h" @@ -68,6 +69,13 @@ public: virtual void SendDataToShader(const SimpleVertex *const *points, int size_u, int size_v, u32 vertType, const Spline::Weight2D &weights) = 0; }; +// Culling plane. +struct Plane { + float x, y, z, w; + void Set(float _x, float _y, float _z, float _w) { x = _x; y = _y; z = _z; w = _w; } + float Test(const float f[3]) const { return x * f[0] + y * f[1] + z * f[2] + w; } +}; + class DrawEngineCommon { public: DrawEngineCommon(); @@ -131,6 +139,7 @@ public: protected: virtual bool UpdateUseHWTessellation(bool enabled) const { return enabled; } + void UpdatePlanes(); int ComputeNumVertsToDecode() const; void DecodeVerts(u8 *dest); @@ -236,4 +245,9 @@ protected: // Hardware tessellation TessellationDataTransfer *tessDataTransfer; + + // Culling + Plane planes_[6]; + Vec2f minOffset_; + Vec2f maxOffset_; }; diff --git a/GPU/Common/ReplacedTexture.cpp b/GPU/Common/ReplacedTexture.cpp index 53668c89c1..8856282dd5 100644 --- a/GPU/Common/ReplacedTexture.cpp +++ b/GPU/Common/ReplacedTexture.cpp @@ -513,6 +513,10 @@ ReplacedTexture::LoadLevelResult ReplacedTexture::LoadLevelData(VFSFileReference bool bc = Draw::DataFormatIsBlockCompressed(*pixelFormat, &blockSize); _dbg_assert_(bc || *pixelFormat == Draw::DataFormat::R8G8B8A8_UNORM); + if (bc && ((level.w & 3) != 0 || (level.h & 3) != 0)) { + WARN_LOG(G3D, "Block compressed replacement texture '%s' not divisible by 4x4 (%dx%d). In D3D11 (only!) we will have to expand (potentially causing glitches).", filename.c_str(), level.w, level.h); + } + data_.resize(numMips); basist::ktx2_transcoder_state transcodeState; // Each thread needs one of these. @@ -546,6 +550,7 @@ ReplacedTexture::LoadLevelResult ReplacedTexture::LoadLevelData(VFSFileReference } transcoder.clear(); vfs_->CloseFile(openFile); + return LoadLevelResult::DONE; // don't read more levels } else if (imageType == ReplacedImageType::DDS) { // TODO: Do better with alphaStatus, it's possible. @@ -562,6 +567,10 @@ ReplacedTexture::LoadLevelResult ReplacedTexture::LoadLevelData(VFSFileReference bool bc = Draw::DataFormatIsBlockCompressed(*pixelFormat, &blockSize); _dbg_assert_(bc); + if (bc && ((level.w & 3) != 0 || (level.h & 3) != 0)) { + WARN_LOG(G3D, "Block compressed replacement texture '%s' not divisible by 4x4 (%dx%d). In D3D11 (only!) we will have to expand (potentially causing glitches).", filename.c_str(), level.w, level.h); + } + data_.resize(numMips); // A DDS File can contain multiple mipmaps. @@ -583,6 +592,7 @@ ReplacedTexture::LoadLevelResult ReplacedTexture::LoadLevelData(VFSFileReference level.fileRef = nullptr; // We only provide a fileref on level 0 if we have mipmaps. } vfs_->CloseFile(openFile); + return LoadLevelResult::DONE; // don't read more levels } else if (imageType == ReplacedImageType::ZIM) { diff --git a/GPU/Common/ShaderCommon.h b/GPU/Common/ShaderCommon.h index 5130242240..95cf3908f2 100644 --- a/GPU/Common/ShaderCommon.h +++ b/GPU/Common/ShaderCommon.h @@ -93,7 +93,7 @@ enum : uint64_t { DIRTY_LIGHT_CONTROL = 1ULL << 38, DIRTY_TEX_ALPHA_MUL = 1ULL << 39, - // Bits 40-43 are free for new uniforms. Then we're really out and need to start merging. + // Bits 40-42 are free for new uniforms. Then we're really out and need to start merging. // Don't forget to update DIRTY_ALL_UNIFORMS when you start using them. DIRTY_BONE_UNIFORMS = 0xFF000000ULL, @@ -101,6 +101,7 @@ enum : uint64_t { DIRTY_ALL_UNIFORMS = 0x0FFFFFFFFFFULL, // Other dirty elements that aren't uniforms + DIRTY_CULL_PLANES = 1ULL << 43, DIRTY_FRAMEBUF = 1ULL << 44, DIRTY_TEXTURE_IMAGE = 1ULL << 45, // Means that the definition of the texture image has changed (address, stride etc), and we need to look up again. DIRTY_TEXTURE_PARAMS = 1ULL << 46, diff --git a/GPU/Common/TextureReplacer.cpp b/GPU/Common/TextureReplacer.cpp index da0f94401c..5e7b471c62 100644 --- a/GPU/Common/TextureReplacer.cpp +++ b/GPU/Common/TextureReplacer.cpp @@ -337,6 +337,9 @@ bool TextureReplacer::LoadIniValues(IniFile &ini, VFSBackend *dir, bool isOverri } } + auto gr = GetI18NCategory(I18NCat::GRAPHICS); + + g_OSD.Show(OSDType::MESSAGE_SUCCESS, gr->T("Texture replacement pack activated")); return true; } diff --git a/GPU/Common/VertexDecoderCommon.cpp b/GPU/Common/VertexDecoderCommon.cpp index 5015d7d0fc..28eb46fcdc 100644 --- a/GPU/Common/VertexDecoderCommon.cpp +++ b/GPU/Common/VertexDecoderCommon.cpp @@ -43,6 +43,8 @@ static const u8 nrmsize[4] = { 0, 3, 6, 12 }, nrmalign[4] = { 0, 1, 2, 4 }; static const u8 possize[4] = { 3, 3, 6, 12 }, posalign[4] = { 1, 1, 2, 4 }; static const u8 wtsize[4] = { 0, 1, 2, 4 }, wtalign[4] = { 0, 1, 2, 4 }; +static constexpr bool validateJit = false; + // When software skinning. This array is only used when non-jitted - when jitted, the matrix // is kept in registers. alignas(16) static float skinMatrix[12]; @@ -340,29 +342,29 @@ void VertexDecoder::Step_TcFloatThrough() const void VertexDecoder::Step_TcU8Prescale() const { float *uv = (float *)(decoded_ + decFmt.uvoff); const u8 *uvdata = (const u8 *)(ptr_ + tcoff); - uv[0] = (float)uvdata[0] * (1.f / 128.f) * gstate_c.uv.uScale + gstate_c.uv.uOff; - uv[1] = (float)uvdata[1] * (1.f / 128.f) * gstate_c.uv.vScale + gstate_c.uv.vOff; + uv[0] = (float)uvdata[0] * (1.f / 128.f) * prescaleUV_->uScale + prescaleUV_->uOff; + uv[1] = (float)uvdata[1] * (1.f / 128.f) * prescaleUV_->vScale + prescaleUV_->vOff; } void VertexDecoder::Step_TcU16Prescale() const { float *uv = (float *)(decoded_ + decFmt.uvoff); const u16_le *uvdata = (const u16_le *)(ptr_ + tcoff); - uv[0] = (float)uvdata[0] * (1.f / 32768.f) * gstate_c.uv.uScale + gstate_c.uv.uOff; - uv[1] = (float)uvdata[1] * (1.f / 32768.f) * gstate_c.uv.vScale + gstate_c.uv.vOff; + uv[0] = (float)uvdata[0] * (1.f / 32768.f) * prescaleUV_->uScale + prescaleUV_->uOff; + uv[1] = (float)uvdata[1] * (1.f / 32768.f) * prescaleUV_->vScale + prescaleUV_->vOff; } void VertexDecoder::Step_TcU16DoublePrescale() const { float *uv = (float *)(decoded_ + decFmt.uvoff); const u16_le *uvdata = (const u16_le *)(ptr_ + tcoff); - uv[0] = (float)uvdata[0] * (1.f / 16384.f) * gstate_c.uv.uScale + gstate_c.uv.uOff; - uv[1] = (float)uvdata[1] * (1.f / 16384.f) * gstate_c.uv.vScale + gstate_c.uv.vOff; + uv[0] = (float)uvdata[0] * (1.f / 16384.f) * prescaleUV_->uScale + prescaleUV_->uOff; + uv[1] = (float)uvdata[1] * (1.f / 16384.f) * prescaleUV_->vScale + prescaleUV_->vOff; } void VertexDecoder::Step_TcFloatPrescale() const { float *uv = (float *)(decoded_ + decFmt.uvoff); const float_le *uvdata = (const float_le *)(ptr_ + tcoff); - uv[0] = uvdata[0] * gstate_c.uv.uScale + gstate_c.uv.uOff; - uv[1] = uvdata[1] * gstate_c.uv.vScale + gstate_c.uv.vOff; + uv[0] = uvdata[0] * prescaleUV_->uScale + prescaleUV_->uOff; + uv[1] = uvdata[1] * prescaleUV_->vScale + prescaleUV_->vOff; } void VertexDecoder::Step_TcU8MorphToFloat() const { @@ -436,8 +438,8 @@ void VertexDecoder::Step_TcU8PrescaleMorph() const { } float *out = (float *)(decoded_ + decFmt.uvoff); - out[0] = uv[0] * gstate_c.uv.uScale + gstate_c.uv.uOff; - out[1] = uv[1] * gstate_c.uv.vScale + gstate_c.uv.vOff; + out[0] = uv[0] * prescaleUV_->uScale + prescaleUV_->uOff; + out[1] = uv[1] * prescaleUV_->vScale + prescaleUV_->vOff; } void VertexDecoder::Step_TcU16PrescaleMorph() const { @@ -451,8 +453,8 @@ void VertexDecoder::Step_TcU16PrescaleMorph() const { } float *out = (float *)(decoded_ + decFmt.uvoff); - out[0] = uv[0] * gstate_c.uv.uScale + gstate_c.uv.uOff; - out[1] = uv[1] * gstate_c.uv.vScale + gstate_c.uv.vOff; + out[0] = uv[0] * prescaleUV_->uScale + prescaleUV_->uOff; + out[1] = uv[1] * prescaleUV_->vScale + prescaleUV_->vOff; } void VertexDecoder::Step_TcU16DoublePrescaleMorph() const { @@ -466,8 +468,8 @@ void VertexDecoder::Step_TcU16DoublePrescaleMorph() const { } float *out = (float *)(decoded_ + decFmt.uvoff); - out[0] = uv[0] * gstate_c.uv.uScale + gstate_c.uv.uOff; - out[1] = uv[1] * gstate_c.uv.vScale + gstate_c.uv.vOff; + out[0] = uv[0] * prescaleUV_->uScale + prescaleUV_->uOff; + out[1] = uv[1] * prescaleUV_->vScale + prescaleUV_->vOff; } void VertexDecoder::Step_TcFloatPrescaleMorph() const { @@ -481,8 +483,8 @@ void VertexDecoder::Step_TcFloatPrescaleMorph() const { } float *out = (float *)(decoded_ + decFmt.uvoff); - out[0] = uv[0] * gstate_c.uv.uScale + gstate_c.uv.uOff; - out[1] = uv[1] * gstate_c.uv.vScale + gstate_c.uv.vOff; + out[0] = uv[0] * prescaleUV_->uScale + prescaleUV_->uOff; + out[1] = uv[1] * prescaleUV_->vScale + prescaleUV_->vOff; } void VertexDecoder::Step_ColorInvalid() const @@ -1297,12 +1299,13 @@ void VertexDecoder::DecodeVerts(u8 *decodedptr, const void *verts, const UVScale return; } - if (jitted_) { + if (jitted_ && !validateJit) { // We've compiled the steps into optimized machine code, so just jump! jitted_(startPtr, decodedptr, count, uvScaleOffset); } else { ptr_ = startPtr; decoded_ = decodedptr; + prescaleUV_ = uvScaleOffset; // Interpret the decode steps for (; count; count--) { for (int i = 0; i < numSteps_; i++) { @@ -1311,6 +1314,127 @@ void VertexDecoder::DecodeVerts(u8 *decodedptr, const void *verts, const UVScale ptr_ += size; decoded_ += stride; } + + if (jitted_ && validateJit) { + CompareToJit(startPtr, decodedptr, indexUpperBound - indexLowerBound + 1, uvScaleOffset); + } + } +} + +static float LargestAbsDiff(Vec4f a, Vec4f b, int n) { + Vec4f delta = a - b; + float largest = 0; + for (int i = 0; i < n; ++ i) { + largest = std::max(largest, fabsf(delta[i])); + } + return largest; +} + +static bool DecodedVertsAreSimilar(const VertexReader &vtx1, const VertexReader &vtx2) { + Vec4f vec1{}, vec2{}; + if (vtx1.hasNormal()) { + vtx1.ReadNrm(vec1.AsArray()); + vtx2.ReadNrm(vec2.AsArray()); + float diff = LargestAbsDiff(vec1, vec2, 3); + if (diff >= 1.0 / 512.0f) { + WARN_LOG(G3D, "Normal diff %f", diff); + return false; + } + } + if (vtx1.hasUV()) { + vtx1.ReadUV(vec1.AsArray()); + vtx2.ReadUV(vec2.AsArray()); + float diff = LargestAbsDiff(vec1, vec2, 2); + if (diff >= 1.0 / 512.0f) { + WARN_LOG(G3D, "UV diff %f", diff); + return false; + } + } + if (vtx1.hasColor0()) { + vtx1.ReadColor0(vec1.AsArray()); + vtx2.ReadColor0(vec2.AsArray()); + float diff = LargestAbsDiff(vec1, vec2, 4); + if (diff >= 1.0 / 255.0f) { + WARN_LOG(G3D, "Color0 diff %f", diff); + return false; + } + } + if (vtx1.hasColor1()) { + vtx1.ReadColor1(vec1.AsArray()); + vtx2.ReadColor1(vec2.AsArray()); + float diff = LargestAbsDiff(vec1, vec2, 4); + if (diff >= 1.0 / 255.0f) { + WARN_LOG(G3D, "Color1 diff %f", diff); + return false; + } + } + vtx1.ReadPos(vec1.AsArray()); + vtx2.ReadPos(vec2.AsArray()); + float diff = LargestAbsDiff(vec1, vec2, 3); + if (diff >= 1.0 / 512.0f) { + WARN_LOG(G3D, "Pos diff %f", diff); + return false; + } + + return true; +} + +void VertexDecoder::CompareToJit(const u8 *startPtr, u8 *decodedptr, int count, const UVScale *uvScaleOffset) const { + std::vector jittedBuffer(decFmt.stride * count); + jitted_(startPtr, &jittedBuffer[0], count, uvScaleOffset); + + VertexReader controlReader(decodedptr, GetDecVtxFmt(), fmt_); + VertexReader jittedReader(&jittedBuffer[0], GetDecVtxFmt(), fmt_); + for (int i = 0; i < count; ++i) { + int off = decFmt.stride * i; + controlReader.Goto(i); + jittedReader.Goto(i); + if (!DecodedVertsAreSimilar(controlReader, jittedReader)) { + char name[512]{}; + ToString(name); + ERROR_LOG(G3D, "Encountered vertexjit mismatch at %d/%d for %s", i, count, name); + if (morphcount > 1) { + printf("Morph:\n"); + for (int j = 0; j < morphcount; ++j) { + printf(" %f\n", gstate_c.morphWeights[j]); + } + } + if (weighttype) { + printf("Bones:\n"); + for (int j = 0; j < nweights; ++j) { + for (int k = 0; k < 4; ++k) { + if (k == 0) + printf(" *"); + else + printf(" "); + printf(" %f,%f,%f\n", gstate.boneMatrix[j * 12 + k * 3 + 0], gstate.boneMatrix[j * 12 + k * 3 + 1], gstate.boneMatrix[j * 12 + k * 3 + 2]); + } + } + } + printf("Src:\n"); + const u8 *s = startPtr + i * size; + for (int j = 0; j < size; ++j) { + int oneoffset = j % onesize_; + if (oneoffset == weightoff && weighttype) + printf(" W:"); + else if (oneoffset == tcoff && tc) + printf(" T:"); + else if (oneoffset == coloff && col) + printf(" C:"); + else if (oneoffset == nrmoff && nrm) + printf(" N:"); + else if (oneoffset == posoff) + printf(" P:"); + printf("%02x ", s[j]); + if (oneoffset == onesize_ - 1) + printf("\n"); + } + printf("Interpreted vertex:\n"); + PrintDecodedVertex(controlReader); + printf("Jit vertex:\n"); + PrintDecodedVertex(jittedReader); + Crash(); + } } } diff --git a/GPU/Common/VertexDecoderCommon.h b/GPU/Common/VertexDecoderCommon.h index c276a3ca99..5e13332f70 100644 --- a/GPU/Common/VertexDecoderCommon.h +++ b/GPU/Common/VertexDecoderCommon.h @@ -436,6 +436,7 @@ public: // Mutable decoder state mutable u8 *decoded_ = nullptr; mutable const u8 *ptr_ = nullptr; + mutable const UVScale *prescaleUV_ = nullptr; JittedVertexDecoder jitted_ = 0; int32_t jittedSize_ = 0; @@ -472,6 +473,9 @@ public: u8 biggest; // in practice, alignment. friend class VertexDecoderJitCache; + +private: + void CompareToJit(const u8 *startPtr, u8 *decodedptr, int count, const UVScale *uvScaleOffset) const; }; diff --git a/GPU/Common/VertexDecoderRiscV.cpp b/GPU/Common/VertexDecoderRiscV.cpp index cbca1892b8..3dc1cf5c97 100644 --- a/GPU/Common/VertexDecoderRiscV.cpp +++ b/GPU/Common/VertexDecoderRiscV.cpp @@ -265,15 +265,15 @@ JittedVertexDecoder VertexDecoderJitCache::Compile(const VertexDecoder &dec, int if ((morphFlags & (1 << (int)MorphValuesIndex::COLOR_4)) != 0) { LI(scratchReg, 255.0f / 15.0f); - FMV(FMv::W, FMv::X, fpScratchReg1, scratchReg); + FMV(FMv::W, FMv::X, fpExtra[0], scratchReg); } if ((morphFlags & (1 << (int)MorphValuesIndex::COLOR_5)) != 0) { LI(scratchReg, 255.0f / 31.0f); - FMV(FMv::W, FMv::X, fpScratchReg2, scratchReg); + FMV(FMv::W, FMv::X, fpExtra[1], scratchReg); } if ((morphFlags & (1 << (int)MorphValuesIndex::COLOR_6)) != 0) { LI(scratchReg, 255.0f / 63.0f); - FMV(FMv::W, FMv::X, fpScratchReg3, scratchReg); + FMV(FMv::W, FMv::X, fpExtra[2], scratchReg); } // Premultiply the values we need and store them so we can reuse. @@ -287,11 +287,11 @@ JittedVertexDecoder VertexDecoderJitCache::Compile(const VertexDecoder &dec, int if ((morphFlags & (1 << (int)MorphValuesIndex::AS_FLOAT)) != 0) FS(32, fpScratchReg1, morphBaseReg, ((int)MorphValuesIndex::AS_FLOAT * 8 + n) * 4); if ((morphFlags & (1 << (int)MorphValuesIndex::COLOR_4)) != 0) - storePremultiply(fpScratchReg1, MorphValuesIndex::COLOR_4, n); + storePremultiply(fpExtra[0], MorphValuesIndex::COLOR_4, n); if ((morphFlags & (1 << (int)MorphValuesIndex::COLOR_5)) != 0) - storePremultiply(fpScratchReg2, MorphValuesIndex::COLOR_5, n); + storePremultiply(fpExtra[1], MorphValuesIndex::COLOR_5, n); if ((morphFlags & (1 << (int)MorphValuesIndex::COLOR_6)) != 0) - storePremultiply(fpScratchReg3, MorphValuesIndex::COLOR_6, n); + storePremultiply(fpExtra[2], MorphValuesIndex::COLOR_6, n); } } else if (dec_->skinInDecode) { LI(morphBaseReg, &skinMatrix[0]); @@ -538,7 +538,7 @@ void VertexDecoderJitCache::Jit_TcU16ThroughToFloat() { LHU(tempReg1, srcReg, dec_->tcoff + 0); LHU(tempReg2, srcReg, dec_->tcoff + 2); - if (cpu_info.RiscV_B) { + if (cpu_info.RiscV_Zbb) { MINU(boundsMinUReg, boundsMinUReg, tempReg1); MAXU(boundsMaxUReg, boundsMaxUReg, tempReg1); MINU(boundsMinVReg, boundsMinVReg, tempReg2); @@ -838,20 +838,20 @@ void VertexDecoderJitCache::Jit_PosFloat() { } void VertexDecoderJitCache::Jit_PosS8Skin() { - Jit_AnyS8ToFloat(dec_->nrmoff); - Jit_WriteMatrixMul(dec_->decFmt.nrmoff, true); + Jit_AnyS8ToFloat(dec_->posoff); + Jit_WriteMatrixMul(dec_->decFmt.posoff, true); } void VertexDecoderJitCache::Jit_PosS16Skin() { - Jit_AnyS16ToFloat(dec_->nrmoff); - Jit_WriteMatrixMul(dec_->decFmt.nrmoff, true); + Jit_AnyS16ToFloat(dec_->posoff); + Jit_WriteMatrixMul(dec_->decFmt.posoff, true); } void VertexDecoderJitCache::Jit_PosFloatSkin() { - FL(32, fpSrc[0], srcReg, dec_->nrmoff + 0); - FL(32, fpSrc[1], srcReg, dec_->nrmoff + 4); - FL(32, fpSrc[2], srcReg, dec_->nrmoff + 8); - Jit_WriteMatrixMul(dec_->decFmt.nrmoff, true); + FL(32, fpSrc[0], srcReg, dec_->posoff + 0); + FL(32, fpSrc[1], srcReg, dec_->posoff + 4); + FL(32, fpSrc[2], srcReg, dec_->posoff + 8); + Jit_WriteMatrixMul(dec_->decFmt.posoff, true); } void VertexDecoderJitCache::Jit_PosS8Through() { @@ -867,8 +867,8 @@ void VertexDecoderJitCache::Jit_PosS16Through() { LH(tempReg2, srcReg, dec_->posoff + 2); // This one, Z, has to be unsigned. LHU(tempReg3, srcReg, dec_->posoff + 4); - FCVT(FConv::S, FConv::WU, fpSrc[0], tempReg1, Round::TOZERO); - FCVT(FConv::S, FConv::WU, fpSrc[1], tempReg2, Round::TOZERO); + FCVT(FConv::S, FConv::W, fpSrc[0], tempReg1, Round::TOZERO); + FCVT(FConv::S, FConv::W, fpSrc[1], tempReg2, Round::TOZERO); FCVT(FConv::S, FConv::WU, fpSrc[2], tempReg3, Round::TOZERO); FS(32, fpSrc[0], dstReg, dec_->decFmt.posoff + 0); FS(32, fpSrc[1], dstReg, dec_->decFmt.posoff + 4); @@ -904,17 +904,17 @@ void VertexDecoderJitCache::Jit_PosFloatMorph() { void VertexDecoderJitCache::Jit_PosS8MorphSkin() { Jit_AnyS8Morph(dec_->posoff, -1); - Jit_WriteMatrixMul(dec_->decFmt.nrmoff, true); + Jit_WriteMatrixMul(dec_->decFmt.posoff, true); } void VertexDecoderJitCache::Jit_PosS16MorphSkin() { Jit_AnyS16Morph(dec_->posoff, -1); - Jit_WriteMatrixMul(dec_->decFmt.nrmoff, true); + Jit_WriteMatrixMul(dec_->decFmt.posoff, true); } void VertexDecoderJitCache::Jit_PosFloatMorphSkin() { Jit_AnyFloatMorph(dec_->posoff, -1); - Jit_WriteMatrixMul(dec_->decFmt.nrmoff, true); + Jit_WriteMatrixMul(dec_->decFmt.posoff, true); } void VertexDecoderJitCache::Jit_Color8888() { @@ -1110,7 +1110,7 @@ void VertexDecoderJitCache::Jit_Color565Morph() { SRLI(tempReg1, tempReg1, 6); FMUL(32, fpSrc[1], fpSrc[1], fpScratchReg4, Round::TOZERO); - FCVT(FConv::S, FConv::WU, fpSrc[0], tempReg1, Round::TOZERO); + FCVT(FConv::S, FConv::WU, fpSrc[2], tempReg1, Round::TOZERO); FMUL(32, fpSrc[2], fpSrc[2], fpScratchReg3, Round::TOZERO); for (int n = 1; n < dec_->morphcount; n++) { @@ -1172,7 +1172,7 @@ void VertexDecoderJitCache::Jit_Color5551Morph() { } void VertexDecoderJitCache::Jit_WriteMorphColor(int outOff, bool checkAlpha) { - if (cpu_info.RiscV_B) { + if (cpu_info.RiscV_Zbb) { LI(scratchReg, 0xFF); FCVT(FConv::WU, FConv::S, tempReg1, fpSrc[0], Round::TOZERO); MAX(tempReg1, tempReg1, R_ZERO); @@ -1389,21 +1389,28 @@ void VertexDecoderJitCache::Jit_AnyFloatMorph(int srcoff, int dstoff) { void VertexDecoderJitCache::Jit_WriteMatrixMul(int dstoff, bool pos) { const RiscVReg fpDst[3] = { fpScratchReg1, fpScratchReg2, fpScratchReg3 }; + // When using morph + skin, we don't keep skinMatrix in a reg. + RiscVReg skinMatrixReg = morphBaseReg; + if (dec_->morphcount > 1) { + LI(scratchReg, &skinMatrix[0]); + skinMatrixReg = scratchReg; + } + // First, take care of the 3x3 portion of the matrix. for (int y = 0; y < 3; ++y) { for (int x = 0; x < 3; ++x) { - FL(32, fpScratchReg4, morphBaseReg, (y * 3 + x) * 4); + FL(32, fpScratchReg4, skinMatrixReg, (y * 3 + x) * 4); if (y == 0) - FMUL(32, fpDst[x], fpSrc[x], fpScratchReg4); + FMUL(32, fpDst[x], fpSrc[y], fpScratchReg4); else - FMADD(32, fpDst[x], fpSrc[x], fpScratchReg4, fpDst[x]); + FMADD(32, fpDst[x], fpSrc[y], fpScratchReg4, fpDst[x]); } } // For normal, z is 0 so we skip. if (pos) { for (int x = 0; x < 3; ++x) - FL(32, fpSrc[x], morphBaseReg, (9 + x) * 4); + FL(32, fpSrc[x], skinMatrixReg, (9 + x) * 4); for (int x = 0; x < 3; ++x) FADD(32, fpDst[x], fpDst[x], fpSrc[x]); } diff --git a/GPU/D3D11/TextureCacheD3D11.cpp b/GPU/D3D11/TextureCacheD3D11.cpp index d658d19a01..465981e673 100644 --- a/GPU/D3D11/TextureCacheD3D11.cpp +++ b/GPU/D3D11/TextureCacheD3D11.cpp @@ -348,6 +348,14 @@ void TextureCacheD3D11::BuildTexture(TexCacheEntry *const entry) { if (th > 16384) th = 16384; + // NOTE: For block-compressed textures, we'll force the size up to the closest 4x4. This is due to an + // unfortunate restriction in D3D11 (and early D3D12). We'll warn about it in the log to give texture pack + // authors notice to fix it. + if (plan.doReplace && Draw::DataFormatIsBlockCompressed(plan.replaced->Format(), nullptr)) { + tw = (tw + 3) & ~3; + th = (th + 3) & ~3; + } + if (plan.depth == 1) { // We don't yet have mip generation, so clamp the number of levels to the ones we can load directly. levels = std::min(plan.levelsToCreate, plan.levelsToLoad); diff --git a/GPU/GPU.h b/GPU/GPU.h index ba5ada12e8..9e9dd6049a 100644 --- a/GPU/GPU.h +++ b/GPU/GPU.h @@ -66,6 +66,7 @@ inline unsigned int toFloat24(float f) { return i >> 8; } +// The ToString function lives in GPUCommonHW.cpp. struct GPUStatistics { void Reset() { ResetFrame(); @@ -84,10 +85,10 @@ struct GPUStatistics { numTextureInvalidations = 0; numTextureInvalidationsByFramebuffer = 0; numTexturesHashed = 0; - numTextureSwitches = 0; numTextureDataBytesHashed = 0; - numShaderSwitches = 0; numFlushes = 0; + numBBOXJumps = 0; + numPlaneUpdates = 0; numTexturesDecoded = 0; numFramebufferEvaluations = 0; numBlockingReadbacks = 0; @@ -114,6 +115,8 @@ struct GPUStatistics { int numListSyncs; int numCachedDrawCalls; int numFlushes; + int numBBOXJumps; + int numPlaneUpdates; int numVertsSubmitted; int numCachedVertsDrawn; int numUncachedVertsDrawn; @@ -122,8 +125,6 @@ struct GPUStatistics { int numTextureInvalidationsByFramebuffer; int numTexturesHashed; int numTextureDataBytesHashed; - int numTextureSwitches; - int numShaderSwitches; int numTexturesDecoded; int numFramebufferEvaluations; int numBlockingReadbacks; diff --git a/GPU/GPUCommon.cpp b/GPU/GPUCommon.cpp index c5257f2413..c92a233a8b 100644 --- a/GPU/GPUCommon.cpp +++ b/GPU/GPUCommon.cpp @@ -931,6 +931,7 @@ void GPUCommon::Execute_BJump(u32 op, u32 diff) { if (!currentList->bboxResult) { // bounding box jump. const u32 target = gstate_c.getRelativeAddress(op & 0x00FFFFFC); + gpuStats.numBBOXJumps++; if (Memory::IsValidAddress(target)) { UpdatePC(currentList->pc, target - 4); currentList->pc = target - 4; // pc will be increased after we return, counteract that diff --git a/GPU/GPUCommonHW.cpp b/GPU/GPUCommonHW.cpp index d9bcae2087..701cfb1651 100644 --- a/GPU/GPUCommonHW.cpp +++ b/GPU/GPUCommonHW.cpp @@ -191,12 +191,12 @@ const CommonCommandTableEntry commonCommandTable[] = { { GE_CMD_ANTIALIASENABLE, FLAG_FLUSHBEFOREONCHANGE }, // Viewport. - { GE_CMD_OFFSETX, FLAG_FLUSHBEFOREONCHANGE, DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_CULLRANGE }, - { GE_CMD_OFFSETY, FLAG_FLUSHBEFOREONCHANGE, DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_CULLRANGE }, - { GE_CMD_VIEWPORTXSCALE, FLAG_FLUSHBEFOREONCHANGE, DIRTY_FRAMEBUF | DIRTY_TEXTURE_PARAMS | DIRTY_CULLRANGE | DIRTY_PROJMATRIX | DIRTY_VIEWPORTSCISSOR_STATE }, - { GE_CMD_VIEWPORTYSCALE, FLAG_FLUSHBEFOREONCHANGE, DIRTY_FRAMEBUF | DIRTY_TEXTURE_PARAMS | DIRTY_CULLRANGE | DIRTY_PROJMATRIX | DIRTY_VIEWPORTSCISSOR_STATE }, - { GE_CMD_VIEWPORTXCENTER, FLAG_FLUSHBEFOREONCHANGE, DIRTY_FRAMEBUF | DIRTY_TEXTURE_PARAMS | DIRTY_CULLRANGE | DIRTY_PROJMATRIX | DIRTY_VIEWPORTSCISSOR_STATE }, - { GE_CMD_VIEWPORTYCENTER, FLAG_FLUSHBEFOREONCHANGE, DIRTY_FRAMEBUF | DIRTY_TEXTURE_PARAMS | DIRTY_CULLRANGE | DIRTY_PROJMATRIX | DIRTY_VIEWPORTSCISSOR_STATE }, + { GE_CMD_OFFSETX, FLAG_FLUSHBEFOREONCHANGE, DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_CULLRANGE | DIRTY_CULL_PLANES }, + { GE_CMD_OFFSETY, FLAG_FLUSHBEFOREONCHANGE, DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_CULLRANGE | DIRTY_CULL_PLANES }, + { GE_CMD_VIEWPORTXSCALE, FLAG_FLUSHBEFOREONCHANGE, DIRTY_FRAMEBUF | DIRTY_TEXTURE_PARAMS | DIRTY_CULLRANGE | DIRTY_PROJMATRIX | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_CULL_PLANES }, + { GE_CMD_VIEWPORTYSCALE, FLAG_FLUSHBEFOREONCHANGE, DIRTY_FRAMEBUF | DIRTY_TEXTURE_PARAMS | DIRTY_CULLRANGE | DIRTY_PROJMATRIX | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_CULL_PLANES }, + { GE_CMD_VIEWPORTXCENTER, FLAG_FLUSHBEFOREONCHANGE, DIRTY_FRAMEBUF | DIRTY_TEXTURE_PARAMS | DIRTY_CULLRANGE | DIRTY_PROJMATRIX | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_CULL_PLANES }, + { GE_CMD_VIEWPORTYCENTER, FLAG_FLUSHBEFOREONCHANGE, DIRTY_FRAMEBUF | DIRTY_TEXTURE_PARAMS | DIRTY_CULLRANGE | DIRTY_PROJMATRIX | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_CULL_PLANES }, { GE_CMD_VIEWPORTZSCALE, FLAG_FLUSHBEFOREONCHANGE, DIRTY_FRAMEBUF | DIRTY_TEXTURE_PARAMS | DIRTY_CULLRANGE | DIRTY_DEPTHRANGE | DIRTY_PROJMATRIX | DIRTY_VIEWPORTSCISSOR_STATE }, { GE_CMD_VIEWPORTZCENTER, FLAG_FLUSHBEFOREONCHANGE, DIRTY_FRAMEBUF | DIRTY_TEXTURE_PARAMS | DIRTY_CULLRANGE | DIRTY_DEPTHRANGE | DIRTY_PROJMATRIX | DIRTY_VIEWPORTSCISSOR_STATE }, { GE_CMD_DEPTHCLAMPENABLE, FLAG_FLUSHBEFOREONCHANGE, DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_CULLRANGE | DIRTY_RASTER_STATE }, @@ -206,12 +206,12 @@ const CommonCommandTableEntry commonCommandTable[] = { { GE_CMD_MAXZ, FLAG_FLUSHBEFOREONCHANGE, DIRTY_DEPTHRANGE | DIRTY_RASTER_STATE | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_CULLRANGE }, // Region - { GE_CMD_REGION1, FLAG_FLUSHBEFOREONCHANGE, DIRTY_FRAMEBUF | DIRTY_TEXTURE_PARAMS | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_CULLRANGE }, - { GE_CMD_REGION2, FLAG_FLUSHBEFOREONCHANGE, DIRTY_FRAMEBUF | DIRTY_TEXTURE_PARAMS | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_CULLRANGE }, + { GE_CMD_REGION1, FLAG_FLUSHBEFOREONCHANGE, DIRTY_FRAMEBUF | DIRTY_TEXTURE_PARAMS | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_CULLRANGE | DIRTY_CULL_PLANES }, + { GE_CMD_REGION2, FLAG_FLUSHBEFOREONCHANGE, DIRTY_FRAMEBUF | DIRTY_TEXTURE_PARAMS | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_CULLRANGE | DIRTY_CULL_PLANES }, // Scissor - { GE_CMD_SCISSOR1, FLAG_FLUSHBEFOREONCHANGE, DIRTY_FRAMEBUF | DIRTY_TEXTURE_PARAMS | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_CULLRANGE }, - { GE_CMD_SCISSOR2, FLAG_FLUSHBEFOREONCHANGE, DIRTY_FRAMEBUF | DIRTY_TEXTURE_PARAMS | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_CULLRANGE }, + { GE_CMD_SCISSOR1, FLAG_FLUSHBEFOREONCHANGE, DIRTY_FRAMEBUF | DIRTY_TEXTURE_PARAMS | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_CULLRANGE | DIRTY_CULL_PLANES }, + { GE_CMD_SCISSOR2, FLAG_FLUSHBEFOREONCHANGE, DIRTY_FRAMEBUF | DIRTY_TEXTURE_PARAMS | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_CULLRANGE | DIRTY_CULL_PLANES }, // Lighting base colors { GE_CMD_AMBIENTCOLOR, FLAG_FLUSHBEFOREONCHANGE, DIRTY_AMBIENT }, @@ -607,6 +607,10 @@ u32 GPUCommonHW::CheckGPUFeatures() const { features |= GPU_USE_VS_RANGE_CULLING; } + if (draw_->GetDeviceCaps().framebufferFetchSupported) { + features |= GPU_USE_FRAMEBUFFER_FETCH; + } + if (draw_->GetShaderLanguageDesc().bitwiseOps) { features |= GPU_USE_LIGHT_UBERSHADER; } @@ -1364,7 +1368,7 @@ void GPUCommonHW::Execute_WorldMtxNum(u32 op, u32 diff) { if (dst[i] != newVal) { Flush(); dst[i] = newVal; - gstate_c.Dirty(DIRTY_WORLDMATRIX); + gstate_c.Dirty(DIRTY_WORLDMATRIX | DIRTY_CULL_PLANES); } if (++i >= end) { break; @@ -1387,7 +1391,7 @@ void GPUCommonHW::Execute_WorldMtxData(u32 op, u32 diff) { if (num < 12 && newVal != ((const u32 *)gstate.worldMatrix)[num]) { Flush(); ((u32 *)gstate.worldMatrix)[num] = newVal; - gstate_c.Dirty(DIRTY_WORLDMATRIX); + gstate_c.Dirty(DIRTY_WORLDMATRIX | DIRTY_CULL_PLANES); } num++; gstate.worldmtxnum = (GE_CMD_WORLDMATRIXNUMBER << 24) | (num & 0x00FFFFFF); @@ -1417,7 +1421,7 @@ void GPUCommonHW::Execute_ViewMtxNum(u32 op, u32 diff) { if (dst[i] != newVal) { Flush(); dst[i] = newVal; - gstate_c.Dirty(DIRTY_VIEWMATRIX); + gstate_c.Dirty(DIRTY_VIEWMATRIX | DIRTY_CULL_PLANES); } if (++i >= end) { break; @@ -1440,7 +1444,7 @@ void GPUCommonHW::Execute_ViewMtxData(u32 op, u32 diff) { if (num < 12 && newVal != ((const u32 *)gstate.viewMatrix)[num]) { Flush(); ((u32 *)gstate.viewMatrix)[num] = newVal; - gstate_c.Dirty(DIRTY_VIEWMATRIX); + gstate_c.Dirty(DIRTY_VIEWMATRIX | DIRTY_CULL_PLANES); } num++; gstate.viewmtxnum = (GE_CMD_VIEWMATRIXNUMBER << 24) | (num & 0x00FFFFFF); @@ -1470,7 +1474,7 @@ void GPUCommonHW::Execute_ProjMtxNum(u32 op, u32 diff) { if (dst[i] != newVal) { Flush(); dst[i] = newVal; - gstate_c.Dirty(DIRTY_PROJMATRIX); + gstate_c.Dirty(DIRTY_PROJMATRIX | DIRTY_CULL_PLANES); } if (++i >= end) { break; @@ -1493,7 +1497,7 @@ void GPUCommonHW::Execute_ProjMtxData(u32 op, u32 diff) { if (num < 16 && newVal != ((const u32 *)gstate.projMatrix)[num]) { Flush(); ((u32 *)gstate.projMatrix)[num] = newVal; - gstate_c.Dirty(DIRTY_PROJMATRIX); + gstate_c.Dirty(DIRTY_PROJMATRIX | DIRTY_CULL_PLANES); } num++; if (num <= 16) @@ -1643,8 +1647,8 @@ size_t GPUCommonHW::FormatGPUStatsCommon(char *buffer, size_t size) { float vertexAverageCycles = gpuStats.numVertsSubmitted > 0 ? (float)gpuStats.vertexGPUCycles / (float)gpuStats.numVertsSubmitted : 0.0f; return snprintf(buffer, size, "DL processing time: %0.2f ms, %d drawsync, %d listsync\n" - "Draw calls: %d, flushes %d, clears %d (cached: %d)\n" - "Num Tracked Vertex Arrays: %d\n" + "Draw calls: %d, flushes %d, clears %d, bbox jumps %d (%d updates)\n" + "Cached draws: %d (tracked: %d)\n" "Vertices: %d cached: %d uncached: %d\n" "FBOs active: %d (evaluations: %d)\n" "Textures: %d, dec: %d, invalidated: %d, hashed: %d kB\n" @@ -1658,6 +1662,8 @@ size_t GPUCommonHW::FormatGPUStatsCommon(char *buffer, size_t size) { gpuStats.numDrawCalls, gpuStats.numFlushes, gpuStats.numClears, + gpuStats.numBBOXJumps, + gpuStats.numPlaneUpdates, gpuStats.numCachedDrawCalls, gpuStats.numTrackedVertexArrays, gpuStats.numVertsSubmitted, diff --git a/GPU/GPUState.cpp b/GPU/GPUState.cpp index 28c13b871a..94bc154c6c 100644 --- a/GPU/GPUState.cpp +++ b/GPU/GPUState.cpp @@ -128,6 +128,8 @@ void GPUgstate::Reset() { memset(gstate.boneMatrix, 0, sizeof(gstate.boneMatrix)); savedContextVersion = 1; + + gstate_c.Dirty(DIRTY_CULL_PLANES); } void GPUgstate::Save(u32_le *ptr) { @@ -258,6 +260,8 @@ void GPUgstate::Restore(const u32_le *ptr) { if (gpu) gpu->ResetMatrices(); + + gstate_c.Dirty(DIRTY_CULL_PLANES); } bool vertTypeIsSkinningEnabled(u32 vertType) { @@ -366,6 +370,9 @@ void GPUStateCache::DoState(PointerWrap &p) { } else { Do(p, savedContextVersion); } + + if (p.GetMode() == PointerWrap::MODE_READ) + gstate_c.Dirty(DIRTY_CULL_PLANES); } static const char *const gpuUseFlagNames[32] = { @@ -375,7 +382,7 @@ static const char *const gpuUseFlagNames[32] = { "GPU_USE_VS_RANGE_CULLING", "GPU_USE_BLEND_MINMAX", "GPU_USE_LOGIC_OP", - "N/A", + "GPU_USE_FRAGMENT_UBERSHADER", "GPU_USE_TEXTURE_NPOT", "GPU_USE_ANISOTROPY", "GPU_USE_CLEAR_RAM_HACK", diff --git a/GPU/Software/Clipper.cpp b/GPU/Software/Clipper.cpp index 2d96b1f24c..ad07285992 100644 --- a/GPU/Software/Clipper.cpp +++ b/GPU/Software/Clipper.cpp @@ -405,7 +405,7 @@ void ProcessTriangle(const ClipVertexData &v0, const ClipVertexData &v1, const C ClipVertexData &subv1 = *Vertices[indices[i + 1]]; ClipVertexData &subv2 = *Vertices[indices[i + 2]]; - if (subv0.OutsideRange() || subv1.OutsideRange() | subv2.OutsideRange()) + if (subv0.OutsideRange() || subv1.OutsideRange() || subv2.OutsideRange()) continue; if (gstate.getShadeMode() == GE_SHADE_FLAT) { diff --git a/GPU/Software/SoftGpu.cpp b/GPU/Software/SoftGpu.cpp index 5f708a2df0..b385680ebe 100644 --- a/GPU/Software/SoftGpu.cpp +++ b/GPU/Software/SoftGpu.cpp @@ -81,7 +81,7 @@ const SoftwareCommandTableEntry softgpuCommandTable[] = { { GE_CMD_VADDR, FLAG_EXECUTE, SoftDirty::NONE, &GPUCommon::Execute_Vaddr }, { GE_CMD_IADDR, FLAG_EXECUTE, SoftDirty::NONE, &GPUCommon::Execute_Iaddr }, { GE_CMD_BJUMP, FLAG_EXECUTE | FLAG_READS_PC | FLAG_WRITES_PC, SoftDirty::NONE, &GPUCommon::Execute_BJump }, - { GE_CMD_BOUNDINGBOX, FLAG_EXECUTE, SoftDirty::NONE, &GPUCommon::Execute_BoundingBox }, + { GE_CMD_BOUNDINGBOX, FLAG_EXECUTE, SoftDirty::NONE, &SoftGPU::Execute_BoundingBox }, { GE_CMD_PRIM, FLAG_EXECUTE, SoftDirty::NONE, &SoftGPU::Execute_Prim }, { GE_CMD_BEZIER, FLAG_EXECUTE, SoftDirty::NONE, &SoftGPU::Execute_Bezier }, @@ -1031,6 +1031,11 @@ void SoftGPU::Execute_FramebufFormat(u32 op, u32 diff) { drawEngine_->transformUnit.Flush("framebuf"); } +void SoftGPU::Execute_BoundingBox(u32 op, u32 diff) { + gstate_c.Dirty(DIRTY_CULL_PLANES); + GPUCommon::Execute_BoundingBox(op, diff); +} + void SoftGPU::Execute_ZbufPtr(u32 op, u32 diff) { // We assume depthbuf.data won't change while we're drawing. if (diff) { @@ -1081,6 +1086,7 @@ void SoftGPU::Execute_WorldMtxData(u32 op, u32 diff) { if (newVal != *target) { *target = newVal; dirtyFlags_ |= SoftDirty::TRANSFORM_MATRIX; + gstate_c.Dirty(DIRTY_CULL_PLANES); } } @@ -1101,6 +1107,7 @@ void SoftGPU::Execute_ViewMtxData(u32 op, u32 diff) { if (newVal != *target) { *target = newVal; dirtyFlags_ |= SoftDirty::TRANSFORM_MATRIX; + gstate_c.Dirty(DIRTY_CULL_PLANES); } } @@ -1121,6 +1128,7 @@ void SoftGPU::Execute_ProjMtxData(u32 op, u32 diff) { if (newVal != *target) { *target = newVal; dirtyFlags_ |= SoftDirty::TRANSFORM_MATRIX; + gstate_c.Dirty(DIRTY_CULL_PLANES); } } diff --git a/GPU/Software/SoftGpu.h b/GPU/Software/SoftGpu.h index 8171c0e6b3..a5c9a17301 100644 --- a/GPU/Software/SoftGpu.h +++ b/GPU/Software/SoftGpu.h @@ -187,6 +187,9 @@ public: // Overridden to change flushing behavior. void Execute_Call(u32 op, u32 diff); + // Overridden for a dirty flag change. + void Execute_BoundingBox(u32 op, u32 diff); + void Execute_WorldMtxNum(u32 op, u32 diff); void Execute_ViewMtxNum(u32 op, u32 diff); void Execute_ProjMtxNum(u32 op, u32 diff); diff --git a/Qt/QtMain.cpp b/Qt/QtMain.cpp index 098ed7e15b..a787d95d6d 100644 --- a/Qt/QtMain.cpp +++ b/Qt/QtMain.cpp @@ -161,6 +161,8 @@ std::string System_GetProperty(SystemProperty prop) { return result; } #endif + case SYSPROP_BUILD_VERSION: + return PPSSPP_GIT_VERSION; default: return ""; } diff --git a/SDL/SDLMain.cpp b/SDL/SDLMain.cpp index e3178aa102..955592a492 100644 --- a/SDL/SDLMain.cpp +++ b/SDL/SDLMain.cpp @@ -431,6 +431,8 @@ std::string System_GetProperty(SystemProperty prop) { } return result; } + case SYSPROP_BUILD_VERSION: + return PPSSPP_GIT_VERSION; default: return ""; } @@ -527,6 +529,10 @@ bool System_GetPropertyBool(SystemProperty prop) { return true; case SYSPROP_SUPPORTS_OPEN_FILE_IN_EDITOR: return true; // FileUtil.cpp: OpenFileInEditor +#ifndef HTTPS_NOT_AVAILABLE + case SYSPROP_SUPPORTS_HTTPS: + return true; +#endif #if PPSSPP_PLATFORM(MAC) case SYSPROP_HAS_FOLDER_BROWSER: case SYSPROP_HAS_FILE_BROWSER: diff --git a/SDL/SDLVulkanGraphicsContext.cpp b/SDL/SDLVulkanGraphicsContext.cpp index b46858068f..270a8f30a1 100644 --- a/SDL/SDLVulkanGraphicsContext.cpp +++ b/SDL/SDLVulkanGraphicsContext.cpp @@ -136,7 +136,7 @@ bool SDLVulkanGraphicsContext::Init(SDL_Window *&window, int x, int y, int w, in return false; } - draw_ = Draw::T3DCreateVulkanContext(vulkan_); + draw_ = Draw::T3DCreateVulkanContext(vulkan_, g_Config.bRenderMultiThreading); SetGPUBackend(GPUBackend::VULKAN); bool success = draw_->CreatePresets(); _assert_(success); @@ -144,7 +144,6 @@ bool SDLVulkanGraphicsContext::Init(SDL_Window *&window, int x, int y, int w, in renderManager_ = (VulkanRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER); renderManager_->SetInflightFrames(g_Config.iInflightFrames); - return true; } diff --git a/Tools/langtool/Cargo.lock b/Tools/langtool/Cargo.lock index 8174dbca03..04b9fb45f4 100644 --- a/Tools/langtool/Cargo.lock +++ b/Tools/langtool/Cargo.lock @@ -106,18 +106,18 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.65" +version = "1.0.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92de25114670a878b1261c79c9f8f729fb97e95bac93f6312f583c60dd6a1dfe" +checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.30" +version = "1.0.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5907a1b7c277254a8b15170f6e7c97cfa60ee7872a3217663bb81151e48184bb" +checksum = "50f3b39ccfb720540debaa0164757101c08ecb8d326b15358ce76a62c7e85965" dependencies = [ "proc-macro2", ] diff --git a/UI/DevScreens.cpp b/UI/DevScreens.cpp index ff6b702d69..3ee20c4f0d 100644 --- a/UI/DevScreens.cpp +++ b/UI/DevScreens.cpp @@ -70,6 +70,7 @@ #include "UI/ControlMappingScreen.h" #include "UI/GameSettingsScreen.h" + #ifdef _WIN32 #include "Common/CommonWindows.h" // Want to avoid including the full header here as it includes d3dx.h @@ -471,42 +472,43 @@ void SystemInfoScreen::CreateTabs() { LinearLayout *deviceSpecs = AddTab("Device Info", si->T("Device Info")); - deviceSpecs->Add(new ItemHeader(si->T("System Information"))); - deviceSpecs->Add(new InfoItem(si->T("System Name", "Name"), System_GetProperty(SYSPROP_NAME))); + CollapsibleSection *systemInfo = deviceSpecs->Add(new CollapsibleSection(si->T("System Information"))); + systemInfo->Add(new InfoItem(si->T("System Name", "Name"), System_GetProperty(SYSPROP_NAME))); #if PPSSPP_PLATFORM(ANDROID) - deviceSpecs->Add(new InfoItem(si->T("System Version"), StringFromInt(System_GetPropertyInt(SYSPROP_SYSTEMVERSION)))); + systemInfo->Add(new InfoItem(si->T("System Version"), StringFromInt(System_GetPropertyInt(SYSPROP_SYSTEMVERSION)))); #endif - deviceSpecs->Add(new InfoItem(si->T("Lang/Region"), System_GetProperty(SYSPROP_LANGREGION))); + systemInfo->Add(new InfoItem(si->T("Lang/Region"), System_GetProperty(SYSPROP_LANGREGION))); std::string board = System_GetProperty(SYSPROP_BOARDNAME); if (!board.empty()) - deviceSpecs->Add(new InfoItem(si->T("Board"), board)); - deviceSpecs->Add(new InfoItem(si->T("ABI"), GetCompilerABI())); + systemInfo->Add(new InfoItem(si->T("Board"), board)); + systemInfo->Add(new InfoItem(si->T("ABI"), GetCompilerABI())); #ifdef _WIN32 if (IsDebuggerPresent()) { - deviceSpecs->Add(new InfoItem(si->T("Debugger Present"), di->T("Yes"))); + systemInfo->Add(new InfoItem(si->T("Debugger Present"), di->T("Yes"))); } #endif - deviceSpecs->Add(new ItemHeader(si->T("CPU Information"))); + CollapsibleSection *cpuInfo = deviceSpecs->Add(new CollapsibleSection(si->T("CPU Information"))); // Don't bother showing the CPU name if we don't have one. if (strcmp(cpu_info.brand_string, "Unknown") != 0) { - deviceSpecs->Add(new InfoItem(si->T("CPU Name", "Name"), cpu_info.brand_string)); + cpuInfo->Add(new InfoItem(si->T("CPU Name", "Name"), cpu_info.brand_string)); } int totalThreads = cpu_info.num_cores * cpu_info.logical_cpu_count; std::string cores = StringFromFormat(si->T("%d (%d per core, %d cores)"), totalThreads, cpu_info.logical_cpu_count, cpu_info.num_cores); - deviceSpecs->Add(new InfoItem(si->T("Threads"), cores)); + cpuInfo->Add(new InfoItem(si->T("Threads"), cores)); #if PPSSPP_PLATFORM(IOS) - deviceSpecs->Add(new InfoItem(si->T("JIT available"), System_GetPropertyBool(SYSPROP_CAN_JIT) ? di->T("Yes") : di->T("No"))); + cpuInfo->Add(new InfoItem(si->T("JIT available"), System_GetPropertyBool(SYSPROP_CAN_JIT) ? di->T("Yes") : di->T("No"))); #endif - deviceSpecs->Add(new ItemHeader(si->T("GPU Information"))); + + CollapsibleSection *gpuInfo = deviceSpecs->Add(new CollapsibleSection(si->T("GPU Information"))); DrawContext *draw = screenManager()->getDrawContext(); const std::string apiNameKey = draw->GetInfoString(InfoField::APINAME); const char *apiName = gr->T(apiNameKey); - deviceSpecs->Add(new InfoItem(si->T("3D API"), apiName)); + gpuInfo->Add(new InfoItem(si->T("3D API"), apiName)); // TODO: Not really vendor, on most APIs it's a device name (GL calls it vendor though). std::string vendorString; @@ -515,22 +517,22 @@ void SystemInfoScreen::CreateTabs() { } else { vendorString = draw->GetInfoString(InfoField::VENDORSTRING); } - deviceSpecs->Add(new InfoItem(si->T("Vendor"), vendorString)); + gpuInfo->Add(new InfoItem(si->T("Vendor"), vendorString)); std::string vendor = draw->GetInfoString(InfoField::VENDOR); if (vendor.size()) - deviceSpecs->Add(new InfoItem(si->T("Vendor (detected)"), vendor)); - deviceSpecs->Add(new InfoItem(si->T("Driver Version"), draw->GetInfoString(InfoField::DRIVER))); + gpuInfo->Add(new InfoItem(si->T("Vendor (detected)"), vendor)); + gpuInfo->Add(new InfoItem(si->T("Driver Version"), draw->GetInfoString(InfoField::DRIVER))); #ifdef _WIN32 if (GetGPUBackend() != GPUBackend::VULKAN) - deviceSpecs->Add(new InfoItem(si->T("Driver Version"), System_GetProperty(SYSPROP_GPUDRIVER_VERSION))); + gpuInfo->Add(new InfoItem(si->T("Driver Version"), System_GetProperty(SYSPROP_GPUDRIVER_VERSION))); #if !PPSSPP_PLATFORM(UWP) if (GetGPUBackend() == GPUBackend::DIRECT3D9) { - deviceSpecs->Add(new InfoItem(si->T("D3DCompiler Version"), StringFromFormat("%d", GetD3DCompilerVersion()))); + gpuInfo->Add(new InfoItem(si->T("D3DCompiler Version"), StringFromFormat("%d", GetD3DCompilerVersion()))); } #endif #endif if (GetGPUBackend() == GPUBackend::OPENGL) { - deviceSpecs->Add(new InfoItem(si->T("Core Context"), gl_extensions.IsCoreContext ? di->T("Active") : di->T("Inactive"))); + gpuInfo->Add(new InfoItem(si->T("Core Context"), gl_extensions.IsCoreContext ? di->T("Active") : di->T("Inactive"))); int highp_int_min = gl_extensions.range[1][5][0]; int highp_int_max = gl_extensions.range[1][5][1]; int highp_float_min = gl_extensions.range[1][2][0]; @@ -538,15 +540,15 @@ void SystemInfoScreen::CreateTabs() { if (highp_int_max != 0) { char temp[512]; snprintf(temp, sizeof(temp), "Highp int range: %d-%d", highp_int_min, highp_int_max); - deviceSpecs->Add(new InfoItem(si->T("High precision int range"), temp)); + gpuInfo->Add(new InfoItem(si->T("High precision int range"), temp)); } if (highp_float_max != 0) { char temp[512]; snprintf(temp, sizeof(temp), "Highp float range: %d-%d", highp_int_min, highp_int_max); - deviceSpecs->Add(new InfoItem(si->T("High precision float range"), temp)); + gpuInfo->Add(new InfoItem(si->T("High precision float range"), temp)); } } - deviceSpecs->Add(new InfoItem(si->T("Depth buffer format"), DataFormatToString(draw->GetDeviceCaps().preferredDepthBufferFormat))); + gpuInfo->Add(new InfoItem(si->T("Depth buffer format"), DataFormatToString(draw->GetDeviceCaps().preferredDepthBufferFormat))); std::string texCompressionFormats; // Simple non-detailed summary of supported tex compression formats. @@ -555,38 +557,38 @@ void SystemInfoScreen::CreateTabs() { if (draw->GetDataFormatSupport(Draw::DataFormat::BC1_RGBA_UNORM_BLOCK)) texCompressionFormats += "BC1-3 "; if (draw->GetDataFormatSupport(Draw::DataFormat::BC4_UNORM_BLOCK)) texCompressionFormats += "BC4-5 "; if (draw->GetDataFormatSupport(Draw::DataFormat::BC7_UNORM_BLOCK)) texCompressionFormats += "BC7 "; - deviceSpecs->Add(new InfoItem(si->T("Compressed texture formats"), texCompressionFormats)); + gpuInfo->Add(new InfoItem(si->T("Compressed texture formats"), texCompressionFormats)); - deviceSpecs->Add(new ItemHeader(si->T("OS Information"))); - deviceSpecs->Add(new InfoItem(si->T("Memory Page Size"), StringFromFormat(si->T("%d bytes"), GetMemoryProtectPageSize()))); - deviceSpecs->Add(new InfoItem(si->T("RW/RX exclusive"), PlatformIsWXExclusive() ? di->T("Active") : di->T("Inactive"))); + CollapsibleSection *osInformation = deviceSpecs->Add(new CollapsibleSection(si->T("OS Information"))); + osInformation->Add(new InfoItem(si->T("Memory Page Size"), StringFromFormat(si->T("%d bytes"), GetMemoryProtectPageSize()))); + osInformation->Add(new InfoItem(si->T("RW/RX exclusive"), PlatformIsWXExclusive() ? di->T("Active") : di->T("Inactive"))); #if PPSSPP_PLATFORM(ANDROID) - deviceSpecs->Add(new InfoItem(si->T("Sustained perf mode"), System_GetPropertyBool(SYSPROP_SUPPORTS_SUSTAINED_PERF_MODE) ? di->T("Supported") : di->T("Unsupported"))); + osInformation->Add(new InfoItem(si->T("Sustained perf mode"), System_GetPropertyBool(SYSPROP_SUPPORTS_SUSTAINED_PERF_MODE) ? di->T("Supported") : di->T("Unsupported"))); #endif const char *build = si->T("Release"); #ifdef _DEBUG build = si->T("Debug"); #endif - deviceSpecs->Add(new InfoItem(si->T("PPSSPP build"), build)); + osInformation->Add(new InfoItem(si->T("PPSSPP build"), build)); - deviceSpecs->Add(new ItemHeader(si->T("Audio Information"))); - deviceSpecs->Add(new InfoItem(si->T("Sample rate"), StringFromFormat(si->T("%d Hz"), System_GetPropertyInt(SYSPROP_AUDIO_SAMPLE_RATE)))); + CollapsibleSection *audioInformation = deviceSpecs->Add(new CollapsibleSection(si->T("Audio Information"))); + audioInformation->Add(new InfoItem(si->T("Sample rate"), StringFromFormat(si->T("%d Hz"), System_GetPropertyInt(SYSPROP_AUDIO_SAMPLE_RATE)))); int framesPerBuffer = System_GetPropertyInt(SYSPROP_AUDIO_FRAMES_PER_BUFFER); if (framesPerBuffer > 0) { - deviceSpecs->Add(new InfoItem(si->T("Frames per buffer"), StringFromFormat("%d", framesPerBuffer))); + audioInformation->Add(new InfoItem(si->T("Frames per buffer"), StringFromFormat("%d", framesPerBuffer))); } #if PPSSPP_PLATFORM(ANDROID) - deviceSpecs->Add(new InfoItem(si->T("Optimal sample rate"), StringFromFormat(si->T("%d Hz"), System_GetPropertyInt(SYSPROP_AUDIO_OPTIMAL_SAMPLE_RATE)))); - deviceSpecs->Add(new InfoItem(si->T("Optimal frames per buffer"), StringFromFormat("%d", System_GetPropertyInt(SYSPROP_AUDIO_OPTIMAL_FRAMES_PER_BUFFER)))); + audioInformation->Add(new InfoItem(si->T("Optimal sample rate"), StringFromFormat(si->T("%d Hz"), System_GetPropertyInt(SYSPROP_AUDIO_OPTIMAL_SAMPLE_RATE)))); + audioInformation->Add(new InfoItem(si->T("Optimal frames per buffer"), StringFromFormat("%d", System_GetPropertyInt(SYSPROP_AUDIO_OPTIMAL_FRAMES_PER_BUFFER)))); #endif - deviceSpecs->Add(new ItemHeader(si->T("Display Information"))); + CollapsibleSection *displayInfo = deviceSpecs->Add(new CollapsibleSection(si->T("Display Information"))); #if PPSSPP_PLATFORM(ANDROID) || PPSSPP_PLATFORM(UWP) - deviceSpecs->Add(new InfoItem(si->T("Native Resolution"), StringFromFormat("%dx%d", + displayInfo->Add(new InfoItem(si->T("Native Resolution"), StringFromFormat("%dx%d", System_GetPropertyInt(SYSPROP_DISPLAY_XRES), System_GetPropertyInt(SYSPROP_DISPLAY_YRES)))); - deviceSpecs->Add(new InfoItem(si->T("UI Resolution"), StringFromFormat("%dx%d (%s: %0.2f)", + displayInfo->Add(new InfoItem(si->T("UI Resolution"), StringFromFormat("%dx%d (%s: %0.2f)", g_display.dp_xres, g_display.dp_yres, si->T("DPI"), @@ -595,10 +597,10 @@ void SystemInfoScreen::CreateTabs() { #if !PPSSPP_PLATFORM(WINDOWS) // Don't show on Windows, since it's always treated as 60 there. - deviceSpecs->Add(new InfoItem(si->T("Refresh rate"), StringFromFormat(si->T("%0.2f Hz"), (float)System_GetPropertyFloat(SYSPROP_DISPLAY_REFRESH_RATE)))); + displayInfo->Add(new InfoItem(si->T("Refresh rate"), StringFromFormat(si->T("%0.2f Hz"), (float)System_GetPropertyFloat(SYSPROP_DISPLAY_REFRESH_RATE)))); #endif - deviceSpecs->Add(new ItemHeader(si->T("Version Information"))); + CollapsibleSection *versionInfo = deviceSpecs->Add(new CollapsibleSection(si->T("Version Information"))); std::string apiVersion; if (GetGPUBackend() == GPUBackend::OPENGL) { if (gl_extensions.IsGLES) { @@ -611,26 +613,26 @@ void SystemInfoScreen::CreateTabs() { if (apiVersion.size() > 30) apiVersion.resize(30); } - deviceSpecs->Add(new InfoItem(si->T("API Version"), apiVersion)); - deviceSpecs->Add(new InfoItem(si->T("Shading Language"), draw->GetInfoString(InfoField::SHADELANGVERSION))); + versionInfo->Add(new InfoItem(si->T("API Version"), apiVersion)); + versionInfo->Add(new InfoItem(si->T("Shading Language"), draw->GetInfoString(InfoField::SHADELANGVERSION))); #if PPSSPP_PLATFORM(ANDROID) std::string moga = System_GetProperty(SYSPROP_MOGA_VERSION); if (moga.empty()) { moga = si->T("(none detected)"); } - deviceSpecs->Add(new InfoItem("Moga", moga)); + versionInfo->Add(new InfoItem("Moga", moga)); #endif if (gstate_c.GetUseFlags()) { // We're in-game, and can determine these. // TODO: Call a static version of GPUCommon::CheckGPUFeatures() and derive them here directly. - deviceSpecs->Add(new ItemHeader(si->T("GPU Flags"))); + CollapsibleSection *gpuFlags = deviceSpecs->Add(new CollapsibleSection(si->T("GPU Flags"))); for (int i = 0; i < 32; i++) { if (gstate_c.Use((1 << i))) { - deviceSpecs->Add(new TextView(GpuUseFlagToString(i), new LayoutParams(FILL_PARENT, WRAP_CONTENT)))->SetFocusable(true); + gpuFlags->Add(new TextView(GpuUseFlagToString(i), new LayoutParams(FILL_PARENT, WRAP_CONTENT)))->SetFocusable(true); } } } @@ -818,19 +820,23 @@ void SystemInfoScreen::CreateTabs() { }); internals->Add(new ItemHeader(si->T("Progress tests"))); internals->Add(new Choice(si->T("30%")))->OnClick.Add([&](UI::EventParams &) { - g_OSD.SetProgressBar("testprogress", "Test Progress", 1, 100, 30); + g_OSD.SetProgressBar("testprogress", "Test Progress", 1, 100, 30, 0.0f); return UI::EVENT_DONE; }); internals->Add(new Choice(si->T("100%")))->OnClick.Add([&](UI::EventParams &) { - g_OSD.SetProgressBar("testprogress", "Test Progress", 1, 100, 100); + g_OSD.SetProgressBar("testprogress", "Test Progress", 1, 100, 100, 1.0f); return UI::EVENT_DONE; }); internals->Add(new Choice(si->T("N/A%")))->OnClick.Add([&](UI::EventParams &) { - g_OSD.SetProgressBar("testprogress", "Test Progress", 0, 0, 0); + g_OSD.SetProgressBar("testprogress", "Test Progress", 0, 0, 0, 0.0f); return UI::EVENT_DONE; }); - internals->Add(new Choice(si->T("Clear")))->OnClick.Add([&](UI::EventParams &) { - g_OSD.RemoveProgressBar("testprogress"); + internals->Add(new Choice(si->T("Success")))->OnClick.Add([&](UI::EventParams &) { + g_OSD.RemoveProgressBar("testprogress", true, 0.5f); + return UI::EVENT_DONE; + }); + internals->Add(new Choice(si->T("Failure")))->OnClick.Add([&](UI::EventParams &) { + g_OSD.RemoveProgressBar("testprogress", false, 0.5f); return UI::EVENT_DONE; }); internals->Add(new ItemHeader(si->T("Achievement tests"))); @@ -998,7 +1004,7 @@ void JitCompareScreen::UpdateDisasm() { snprintf(temp, sizeof(temp), "%i/%i", currentBlock_, blockCacheDebug->GetNumBlocks()); blockName_->SetText(temp); - if (currentBlock_ < 0 || currentBlock_ >= blockCacheDebug->GetNumBlocks()) { + if (currentBlock_ < 0 || !blockCacheDebug || currentBlock_ >= blockCacheDebug->GetNumBlocks()) { leftDisasm_->Add(new TextView(dev->T("No block"))); rightDisasm_->Add(new TextView(dev->T("No block"))); blockStats_->SetText(""); @@ -1062,6 +1068,9 @@ UI::EventReturn JitCompareScreen::OnShowStats(UI::EventParams &e) { } JitBlockCacheDebugInterface *blockCache = MIPSComp::jit->GetBlockCacheDebugInterface(); + if (!blockCache) + return UI::EVENT_DONE; + BlockCacheStats bcStats; blockCache->ComputeStats(bcStats); NOTICE_LOG(JIT, "Num blocks: %i", bcStats.numBlocks); @@ -1335,7 +1344,7 @@ void FrameDumpTestScreen::update() { if (!listing_) { const char *acceptMime = "text/html, */*; q=0.8"; - listing_ = g_DownloadManager.StartDownload(framedumpsBaseUrl, Path(), acceptMime); + listing_ = g_DownloadManager.StartDownload(framedumpsBaseUrl, Path(), http::ProgressBarMode::DELAYED, acceptMime); } if (listing_ && listing_->Done() && files_.empty()) { @@ -1366,4 +1375,4 @@ void FrameDumpTestScreen::update() { } RecreateViews(); } -} +} \ No newline at end of file diff --git a/UI/DevScreens.h b/UI/DevScreens.h index 776c6bb8e8..743b83af08 100644 --- a/UI/DevScreens.h +++ b/UI/DevScreens.h @@ -218,8 +218,8 @@ private: UI::EventReturn OnLoadDump(UI::EventParams &e); std::vector files_; - std::shared_ptr listing_; - std::shared_ptr dumpDownload_; + std::shared_ptr listing_; + std::shared_ptr dumpDownload_; }; void DrawProfile(UIContext &ui); diff --git a/UI/EmuScreen.cpp b/UI/EmuScreen.cpp index 2d309fc6e3..aeeea0be4c 100644 --- a/UI/EmuScreen.cpp +++ b/UI/EmuScreen.cpp @@ -233,6 +233,11 @@ bool EmuScreen::bootAllowStorage(const Path &filename) { void EmuScreen::bootGame(const Path &filename) { auto sc = GetI18NCategory(I18NCat::SCREEN); + if (Achievements::IsBlockingExecution()) { + // Keep waiting. + return; + } + if (PSP_IsRebooting()) return; if (PSP_IsInited()) { @@ -243,14 +248,15 @@ void EmuScreen::bootGame(const Path &filename) { } if (PSP_IsIniting()) { - std::string error_string; + std::string error_string = "(unknown error)"; + bootPending_ = !PSP_InitUpdate(&error_string); if (!bootPending_) { invalid_ = !PSP_IsInited(); if (invalid_) { errorMessage_ = error_string; - ERROR_LOG(BOOT, "%s", errorMessage_.c_str()); + ERROR_LOG(BOOT, "isIniting bootGame error: %s", errorMessage_.c_str()); return; } bootComplete(); @@ -331,7 +337,7 @@ void EmuScreen::bootGame(const Path &filename) { bootPending_ = false; invalid_ = true; errorMessage_ = error_string; - ERROR_LOG(BOOT, "%s", errorMessage_.c_str()); + ERROR_LOG(BOOT, "InitStart bootGame error: %s", errorMessage_.c_str()); } if (PSP_CoreParameter().compat.flags().RequireBufferedRendering && g_Config.bSkipBufferEffects) { @@ -360,8 +366,9 @@ void EmuScreen::bootComplete() { System_Notify(SystemNotification::BOOT_DONE); System_Notify(SystemNotification::DISASSEMBLY); - NOTICE_LOG(BOOT, "Loading %s...", PSP_CoreParameter().fileToStart.c_str()); + NOTICE_LOG(BOOT, "Booted %s...", PSP_CoreParameter().fileToStart.c_str()); if (!Achievements::ChallengeModeActive()) { + // Don't auto-load savestates in challenge mode. autoLoad(); } @@ -764,7 +771,7 @@ void EmuScreen::onVKey(int virtualKeyCode, bool down) { } break; case VIRTKEY_RAPID_FIRE: - __CtrlSetRapidFire(down); + __CtrlSetRapidFire(down, g_Config.iRapidFireInterval); break; case VIRTKEY_MUTE_TOGGLE: if (down) @@ -1075,6 +1082,8 @@ void EmuScreen::update() { } if (bootPending_) { + // Keep trying the boot until bootPending_ is lifted. + // It may be delayed due to RetroAchievements or any other cause. bootGame(gamePath_); } diff --git a/UI/GameSettingsScreen.cpp b/UI/GameSettingsScreen.cpp index bbaf860f1c..08ddcc1410 100644 --- a/UI/GameSettingsScreen.cpp +++ b/UI/GameSettingsScreen.cpp @@ -31,6 +31,7 @@ #include "Common/System/Display.h" // Only to check screen aspect ratio with pixel_yres/pixel_xres #include "Common/System/Request.h" +#include "Common/System/OSD.h" #include "Common/Battery/Battery.h" #include "Common/System/NativeApp.h" #include "Common/Data/Color/RGBAUtil.h" @@ -720,6 +721,7 @@ void GameSettingsScreen::CreateControlsSettings(UI::ViewGroup *controlsSettings) settingInfo_->Show(co->T("AnalogLimiter Tip", "When the analog limiter button is pressed"), e.v); return UI::EVENT_CONTINUE; }); + controlsSettings->Add(new PopupSliderChoice(&g_Config.iRapidFireInterval, 1, 10, 5, "Rapid fire interval", screenManager(), "frames")); #if defined(USING_WIN_UI) || defined(SDL) controlsSettings->Add(new ItemHeader(co->T("Mouse", "Mouse settings"))); CheckBox *mouseControl = controlsSettings->Add(new CheckBox(&g_Config.bMouseControl, co->T("Use Mouse Control"))); @@ -735,6 +737,53 @@ void GameSettingsScreen::CreateControlsSettings(UI::ViewGroup *controlsSettings) } } +// Compound view just like the audio file choosers +class MacAddressChooser : public UI::LinearLayout { +public: + MacAddressChooser(Path gamePath, std::string *value, const std::string &title, ScreenManager *screenManager, UI::LayoutParams *layoutParams = nullptr); +}; + +static constexpr UI::Size ITEM_HEIGHT = 64.f; + +MacAddressChooser::MacAddressChooser(Path gamePath_, std::string *value, const std::string &title, ScreenManager *screenManager, UI::LayoutParams *layoutParams) : UI::LinearLayout(UI::ORIENT_HORIZONTAL, layoutParams) { + using namespace UI; + SetSpacing(5.0f); + if (!layoutParams) { + layoutParams_->width = FILL_PARENT; + layoutParams_->height = ITEM_HEIGHT; + } + auto n = GetI18NCategory(I18NCat::NETWORKING); + + std::string initialValue = *value; + Add(new PopupTextInputChoice(value, title, g_Config.sMACAddress, 17, screenManager, new LinearLayoutParams(1.0f)))->OnChange.Add([=](UI::EventParams &e) { + // Validate the chosen address, and restore to initialValue if bad. + if (g_Config.sMACAddress.size() != 17) { + // TODO: Alert the user + *value = initialValue; + } + return UI::EVENT_DONE; + }); + Add(new Choice(n->T("Randomize"), new LinearLayoutParams(WRAP_CONTENT, ITEM_HEIGHT)))->OnClick.Add([=](UI::EventParams &) { + auto n = GetI18NCategory(I18NCat::NETWORKING); + auto di = GetI18NCategory(I18NCat::DIALOG); + + const char *confirmMessage = n->T("ChangeMacSaveConfirm", "Generate a new MAC address?"); + const char *warningMessage = n->T("ChangeMacSaveWarning", "Some games verify the MAC address when loading savedata, so this may break old saves."); + std::string combined = g_Config.sMACAddress + "\n\n" + std::string(confirmMessage) + "\n\n" + warningMessage; + + auto confirmScreen = new PromptScreen( + gamePath_, + combined, di->T("Yes"), di->T("No"), + [&](bool success) { + if (success) { + g_Config.sMACAddress = CreateRandMAC(); + }} + ); + screenManager->push(confirmScreen); + return UI::EVENT_DONE; + }); +} + void GameSettingsScreen::CreateNetworkingSettings(UI::ViewGroup *networkingSettings) { using namespace UI; @@ -746,7 +795,7 @@ void GameSettingsScreen::CreateNetworkingSettings(UI::ViewGroup *networkingSetti networkingSettings->Add(new Choice(n->T("Open PPSSPP Multiplayer Wiki Page")))->OnClick.Handle(this, &GameSettingsScreen::OnAdhocGuides); networkingSettings->Add(new CheckBox(&g_Config.bEnableWlan, n->T("Enable networking", "Enable networking/wlan (beta)"))); - networkingSettings->Add(new ChoiceWithValueDisplay(&g_Config.sMACAddress, n->T("Change Mac Address"), I18NCat::NONE))->OnClick.Handle(this, &GameSettingsScreen::OnChangeMacAddress); + networkingSettings->Add(new MacAddressChooser(gamePath_, &g_Config.sMACAddress, n->T("Change Mac Address"), screenManager())); static const char* wlanChannels[] = { "Auto", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11" }; auto wlanChannelChoice = networkingSettings->Add(new PopupMultiChoice(&g_Config.iWlanAdhocChannel, n->T("WLAN Channel"), wlanChannels, 0, ARRAY_SIZE(wlanChannels), I18NCat::NETWORKING, screenManager())); for (int i = 0; i < 4; i++) { @@ -772,9 +821,9 @@ void GameSettingsScreen::CreateNetworkingSettings(UI::ViewGroup *networkingSetti networkingSettings->Add(new ItemHeader(n->T("Chat"))); networkingSettings->Add(new CheckBox(&g_Config.bEnableNetworkChat, n->T("Enable network chat", "Enable network chat"))); static const char *chatButtonPositions[] = { "Bottom Left", "Bottom Center", "Bottom Right", "Top Left", "Top Center", "Top Right", "Center Left", "Center Right", "None" }; - networkingSettings->Add(new PopupMultiChoice(&g_Config.iChatButtonPosition, n->T("Chat Button Position"), chatButtonPositions, 0, ARRAY_SIZE(chatButtonPositions), I18NCat::NETWORKING, screenManager()))->SetEnabledPtr(&g_Config.bEnableNetworkChat); + networkingSettings->Add(new PopupMultiChoice(&g_Config.iChatButtonPosition, n->T("Chat Button Position"), chatButtonPositions, 0, ARRAY_SIZE(chatButtonPositions), I18NCat::DIALOG, screenManager()))->SetEnabledPtr(&g_Config.bEnableNetworkChat); static const char *chatScreenPositions[] = { "Bottom Left", "Bottom Center", "Bottom Right", "Top Left", "Top Center", "Top Right" }; - networkingSettings->Add(new PopupMultiChoice(&g_Config.iChatScreenPosition, n->T("Chat Screen Position"), chatScreenPositions, 0, ARRAY_SIZE(chatScreenPositions), I18NCat::NETWORKING, screenManager()))->SetEnabledPtr(&g_Config.bEnableNetworkChat); + networkingSettings->Add(new PopupMultiChoice(&g_Config.iChatScreenPosition, n->T("Chat Screen Position"), chatScreenPositions, 0, ARRAY_SIZE(chatScreenPositions), I18NCat::DIALOG, screenManager()))->SetEnabledPtr(&g_Config.bEnableNetworkChat); #if (!defined(MOBILE_DEVICE) && !defined(USING_QT_UI)) || defined(USING_QT_UI) || PPSSPP_PLATFORM(ANDROID) // Missing only iOS? networkingSettings->Add(new ItemHeader(n->T("QuickChat", "Quick Chat"))); @@ -850,6 +899,7 @@ void GameSettingsScreen::CreateSystemSettings(UI::ViewGroup *systemSettings) { auto di = GetI18NCategory(I18NCat::DIALOG); auto vr = GetI18NCategory(I18NCat::VR); auto th = GetI18NCategory(I18NCat::THEMES); + auto psps = GetI18NCategory(I18NCat::PSPSETTINGS); // TODO: Should move more into this section. systemSettings->Add(new ItemHeader(sy->T("RetroAchievements"))); auto retro = systemSettings->Add(new Choice(sy->T("RetroAchievements"))); @@ -1068,6 +1118,10 @@ void GameSettingsScreen::CreateSystemSettings(UI::ViewGroup *systemSettings) { }); systemSettings->Add(new ItemHeader(sy->T("PSP Settings"))); + + // The ordering here is simply mapping directly to PSP_SYSTEMPARAM_LANGUAGE_*. + static const char *defaultLanguages[] = { "Auto", "Japanese", "English", "French", "Spanish", "German", "Italian", "Dutch", "Portuguese", "Russian", "Korean", "Chinese (traditional)", "Chinese (simplified)" }; + systemSettings->Add(new PopupMultiChoice(&g_Config.iLanguage, psps->T("Game language"), defaultLanguages, -1, ARRAY_SIZE(defaultLanguages), I18NCat::PSPSETTINGS, screenManager())); static const char *models[] = { "PSP-1000", "PSP-2000/3000" }; systemSettings->Add(new PopupMultiChoice(&g_Config.iPSPModel, sy->T("PSP Model"), models, 0, ARRAY_SIZE(models), I18NCat::SYSTEM, screenManager()))->SetEnabled(!PSP_IsInited()); systemSettings->Add(new PopupTextInputChoice(&g_Config.sNickName, sy->T("Change Nickname"), "", 32, screenManager())); @@ -1367,45 +1421,18 @@ void GameSettingsScreen::TriggerRestart(const char *why) { System_RestartApp(param); } -void GameSettingsScreen::CallbackRenderingBackend(bool yes) { - // If the user ends up deciding not to restart, set the config back to the current backend - // so it doesn't get switched by accident. - if (yes) { - TriggerRestart("GameSettingsScreen::RenderingBackendYes"); - } else { - g_Config.iGPUBackend = (int)GetGPUBackend(); - } -} - -void GameSettingsScreen::CallbackRenderingDevice(bool yes) { - // If the user ends up deciding not to restart, set the config back to the current backend - // so it doesn't get switched by accident. - if (yes) { - TriggerRestart("GameSettingsScreen::RenderingDeviceYes"); - } else { - std::string *deviceNameSetting = GPUDeviceNameSetting(); - if (deviceNameSetting) - *deviceNameSetting = GetGPUBackendDevice(); - // Needed to redraw the setting. - RecreateViews(); - } -} - -void GameSettingsScreen::CallbackInflightFrames(bool yes) { - if (yes) { - TriggerRestart("GameSettingsScreen::InflightFramesYes"); - } else { - g_Config.iInflightFrames = prevInflightFrames_; - } -} - UI::EventReturn GameSettingsScreen::OnRenderingBackend(UI::EventParams &e) { auto di = GetI18NCategory(I18NCat::DIALOG); // It only makes sense to show the restart prompt if the backend was actually changed. if (g_Config.iGPUBackend != (int)GetGPUBackend()) { - screenManager()->push(new PromptScreen(gamePath_, di->T("ChangingGPUBackends", "Changing GPU backends requires PPSSPP to restart. Restart now?"), di->T("Yes"), di->T("No"), - std::bind(&GameSettingsScreen::CallbackRenderingBackend, this, std::placeholders::_1))); + screenManager()->push(new PromptScreen(gamePath_, di->T("Changing this setting requires PPSSPP to restart."), di->T("Restart"), di->T("Cancel"), [=](bool yes) { + if (yes) { + TriggerRestart("GameSettingsScreen::RenderingBackendYes"); + } else { + g_Config.iGPUBackend = (int)GetGPUBackend(); + } + })); } return UI::EVENT_DONE; } @@ -1416,8 +1443,19 @@ UI::EventReturn GameSettingsScreen::OnRenderingDevice(UI::EventParams &e) { // It only makes sense to show the restart prompt if the device was actually changed. std::string *deviceNameSetting = GPUDeviceNameSetting(); if (deviceNameSetting && *deviceNameSetting != GetGPUBackendDevice()) { - screenManager()->push(new PromptScreen(gamePath_, di->T("ChangingGPUBackends", "Changing GPU backends requires PPSSPP to restart. Restart now?"), di->T("Yes"), di->T("No"), - std::bind(&GameSettingsScreen::CallbackRenderingDevice, this, std::placeholders::_1))); + screenManager()->push(new PromptScreen(gamePath_, di->T("Changing this setting requires PPSSPP to restart."), di->T("Restart"), di->T("Cancel"), [=](bool yes) { + // If the user ends up deciding not to restart, set the config back to the current backend + // so it doesn't get switched by accident. + if (yes) { + TriggerRestart("GameSettingsScreen::RenderingDeviceYes"); + } else { + std::string *deviceNameSetting = GPUDeviceNameSetting(); + if (deviceNameSetting) + *deviceNameSetting = GetGPUBackendDevice(); + // Needed to redraw the setting. + RecreateViews(); + } + })); } return UI::EVENT_DONE; } @@ -1425,8 +1463,13 @@ UI::EventReturn GameSettingsScreen::OnRenderingDevice(UI::EventParams &e) { UI::EventReturn GameSettingsScreen::OnInflightFramesChoice(UI::EventParams &e) { auto di = GetI18NCategory(I18NCat::DIALOG); if (g_Config.iInflightFrames != prevInflightFrames_) { - screenManager()->push(new PromptScreen(gamePath_, di->T("ChangingInflightFrames", "Changing graphics command buffering requires PPSSPP to restart. Restart now?"), di->T("Yes"), di->T("No"), - std::bind(&GameSettingsScreen::CallbackInflightFrames, this, std::placeholders::_1))); + screenManager()->push(new PromptScreen(gamePath_, di->T("Changing this setting requires PPSSPP to restart."), di->T("Restart"), di->T("Cancel"), [=](bool yes) { + if (yes) { + TriggerRestart("GameSettingsScreen::InflightFramesYes"); + } else { + g_Config.iInflightFrames = prevInflightFrames_; + } + })); } return UI::EVENT_DONE; } @@ -1518,27 +1561,6 @@ UI::EventReturn GameSettingsScreen::OnChangeproAdhocServerAddress(UI::EventParam return UI::EVENT_DONE; } -UI::EventReturn GameSettingsScreen::OnChangeMacAddress(UI::EventParams &e) { - auto n = GetI18NCategory(I18NCat::NETWORKING); - auto di = GetI18NCategory(I18NCat::DIALOG); - - const char *confirmMessage = n->T("ChangeMacSaveConfirm", "Generate a new MAC address?"); - const char *warningMessage = n->T("ChangeMacSaveWarning", "Some games verify the MAC address when loading savedata, so this may break old saves."); - std::string combined = std::string(confirmMessage) + "\n\n" + warningMessage; - - auto confirmScreen = new PromptScreen( - gamePath_, - combined, di->T("Yes"), di->T("No"), - [&](bool success) { - if (success) { - g_Config.sMACAddress = CreateRandMAC(); - }} - ); - screenManager()->push(confirmScreen); - - return UI::EVENT_DONE; -} - UI::EventReturn GameSettingsScreen::OnTextureShader(UI::EventParams &e) { auto gr = GetI18NCategory(I18NCat::GRAPHICS); auto shaderScreen = new TextureShaderScreen(gr->T("Texture Shader")); @@ -1642,6 +1664,15 @@ void DeveloperToolsScreen::CreateViews() { cpuTests->SetEnabled(TestsAvailable()); #endif + + if (g_Config.iGPUBackend == (int)GPUBackend::VULKAN) { + list->Add(new CheckBox(&g_Config.bRenderMultiThreading, dev->T("Multi-threaded rendering"), ""))->OnClick.Add([](UI::EventParams &e) { + // TODO: Not translating yet. Will combine with other translations of settings that need restart. + g_OSD.Show(OSDType::MESSAGE_WARNING, "Restart required"); + return UI::EVENT_DONE; + }); + } + // For now, we only implement GPU driver tests for Vulkan and OpenGL. This is simply // because the D3D drivers are generally solid enough to not need this type of investigation. if (g_Config.iGPUBackend == (int)GPUBackend::VULKAN || g_Config.iGPUBackend == (int)GPUBackend::OPENGL) { diff --git a/UI/GameSettingsScreen.h b/UI/GameSettingsScreen.h index 97a31234ff..3658874397 100644 --- a/UI/GameSettingsScreen.h +++ b/UI/GameSettingsScreen.h @@ -38,9 +38,6 @@ public: protected: void CallbackRestoreDefaults(bool yes); - void CallbackRenderingBackend(bool yes); - void CallbackRenderingDevice(bool yes); - void CallbackInflightFrames(bool yes); void CallbackMemstickFolder(bool yes); void dialogFinished(const Screen *dialog, DialogResult result) override; @@ -80,7 +77,6 @@ private: UI::EventReturn OnControlMapping(UI::EventParams &e); UI::EventReturn OnCalibrateAnalogs(UI::EventParams &e); UI::EventReturn OnTouchControlLayout(UI::EventParams &e); - UI::EventReturn OnTiltTypeChange(UI::EventParams &e); UI::EventReturn OnTiltCustomize(UI::EventParams &e); // Global settings handlers @@ -96,14 +92,12 @@ private: UI::EventReturn OnChangeQuickChat4(UI::EventParams &e); UI::EventReturn OnChangeNickname(UI::EventParams &e); UI::EventReturn OnChangeproAdhocServerAddress(UI::EventParams &e); - UI::EventReturn OnChangeMacAddress(UI::EventParams &e); UI::EventReturn OnChangeBackground(UI::EventParams &e); UI::EventReturn OnFullscreenChange(UI::EventParams &e); UI::EventReturn OnFullscreenMultiChange(UI::EventParams &e); UI::EventReturn OnResolutionChange(UI::EventParams &e); UI::EventReturn OnHwScaleChange(UI::EventParams &e); UI::EventReturn OnRestoreDefaultSettings(UI::EventParams &e); - UI::EventReturn OnRenderingMode(UI::EventParams &e); UI::EventReturn OnRenderingBackend(UI::EventParams &e); UI::EventReturn OnRenderingDevice(UI::EventParams &e); UI::EventReturn OnInflightFramesChoice(UI::EventParams &e); diff --git a/UI/GamepadEmu.cpp b/UI/GamepadEmu.cpp index a71942c718..40524e02aa 100644 --- a/UI/GamepadEmu.cpp +++ b/UI/GamepadEmu.cpp @@ -264,7 +264,7 @@ void CustomButton::Update() { } bool PSPButton::IsDown() { - return (__CtrlPeekButtons() & pspButtonBit_) != 0; + return (__CtrlPeekButtonsVisual() & pspButtonBit_) != 0; } PSPDpad::PSPDpad(ImageID arrowIndex, const char *key, ImageID arrowDownIndex, ImageID overlayIndex, float scale, float spacing, UI::LayoutParams *layoutParams) diff --git a/UI/MiscScreens.cpp b/UI/MiscScreens.cpp index 5046c09ce1..0f928332e2 100644 --- a/UI/MiscScreens.cpp +++ b/UI/MiscScreens.cpp @@ -542,8 +542,14 @@ void PromptScreen::CreateViews() { Choice *yesButton = rightColumnItems->Add(new Choice(yesButtonText_)); yesButton->OnClick.Handle(this, &PromptScreen::OnYes); root_->SetDefaultFocusView(yesButton); - if (!noButtonText_.empty()) + if (!noButtonText_.empty()) { rightColumnItems->Add(new Choice(noButtonText_))->OnClick.Handle(this, &PromptScreen::OnNo); + } else { + // This is an information screen, not a question. + // Sneak in the version of PPSSPP in the corner, for debug-reporting user screenshots. + std::string version = System_GetProperty(SYSPROP_BUILD_VERSION); + root_->Add(new TextView(version, 0, true, new AnchorLayoutParams(10.0f, NONE, NONE, 10.0f))); + } } UI::EventReturn PromptScreen::OnYes(UI::EventParams &e) { @@ -671,18 +677,9 @@ void NewLanguageScreen::OnCompleted(DialogResult result) { iniLoadedSuccessfully = g_i18nrepo.LoadIni(g_Config.sLanguageIni, langOverridePath); if (iniLoadedSuccessfully) { - // Dunno what else to do here. - auto &langValuesMapping = g_Config.GetLangValuesMapping(); - - auto iter = langValuesMapping.find(code); - if (iter == langValuesMapping.end()) { - // Fallback to English - g_Config.iLanguage = PSP_SYSTEMPARAM_LANGUAGE_ENGLISH; - } else { - g_Config.iLanguage = iter->second.second; - } RecreateViews(); } else { + // Failed to load the language ini. Shouldn't really happen, but let's just switch back to the old language. g_Config.sLanguageIni = oldLang; } } diff --git a/UI/NativeApp.cpp b/UI/NativeApp.cpp index 593f986f33..d79e599ea8 100644 --- a/UI/NativeApp.cpp +++ b/UI/NativeApp.cpp @@ -103,6 +103,7 @@ #include "Core/HLE/proAdhoc.h" #include "Core/HW/MemoryStick.h" #include "Core/Util/GameManager.h" +#include "Core/Util/PortManager.h" #include "Core/Util/AudioFormat.h" #include "Core/WebServer.h" #include "Core/TiltEventProcessor.h" @@ -1431,6 +1432,8 @@ void NativeShutdown() { System_ExitApp(); #endif + g_PortManager.Shutdown(); + net::Shutdown(); g_Discord.Shutdown(); diff --git a/UI/OnScreenDisplay.cpp b/UI/OnScreenDisplay.cpp index fec95d5365..a8bc84c441 100644 --- a/UI/OnScreenDisplay.cpp +++ b/UI/OnScreenDisplay.cpp @@ -202,7 +202,7 @@ static void RenderOSDProgressBar(UIContext &dc, const OnScreenDisplay::ProgressB static void MeasureLeaderboardTracker(UIContext &dc, const std::string &text, float *width, float *height) { dc.MeasureText(dc.GetFontStyle(), 1.0f, 1.0f, text.c_str(), width, height); - *width += 10.0f; + *width += 16.0f; *height += 10.0f; } @@ -214,7 +214,7 @@ static void RenderLeaderboardTracker(UIContext &dc, const Bounds &bounds, const dc.FillRect(background, bounds); dc.SetFontStyle(dc.theme->uiFont); dc.SetFontScale(1.0f, 1.0f); - dc.DrawTextShadowRect(text.c_str(), bounds.Inset(5.0f, 5.0f), colorAlpha(0xFFFFFFFF, alpha), ALIGN_VCENTER | ALIGN_LEFT); + dc.DrawTextShadowRect(text.c_str(), bounds.Inset(5.0f, 5.0f), colorAlpha(0xFFFFFFFF, alpha), ALIGN_VCENTER | ALIGN_HCENTER); } void OnScreenMessagesView::Draw(UIContext &dc) { @@ -292,13 +292,9 @@ void OnScreenMessagesView::Draw(UIContext &dc) { } } - // Get height - float w, h; - dc.MeasureText(dc.theme->uiFont, 1.0f, 1.0f, "Wg", &w, &h); - y = 10.0f; - // Then draw them all. + // Draw the progress bars. const std::vector bars = g_OSD.ProgressBars(); for (auto &bar : bars) { float tw, th; @@ -306,11 +302,14 @@ void OnScreenMessagesView::Draw(UIContext &dc) { Bounds b(0.0f, y, tw, th); b.x = (bounds_.w - b.w) * 0.5f; - float alpha = Clamp((float)(bar.endTime - now) * 4.0f, 0.0f, 1.0f); + float enterAlpha = saturatef((float)(now - bar.startTime) * 4.0f); + float leaveAlpha = saturatef((float)(bar.endTime - now) * 4.0f); + float alpha = std::min(enterAlpha, leaveAlpha); RenderOSDProgressBar(dc, bar, b, 0, alpha); y += (b.h + 4.0f) * alpha; // including alpha here gets us smooth animations. } + // Draw the rest of the top-center messages. const std::vector entries = g_OSD.Entries(); for (const auto &entry : entries) { dc.SetFontScale(1.0f, 1.0f); @@ -365,28 +364,6 @@ void OnScreenMessagesView::Draw(UIContext &dc) { RenderOSDEntry(dc, entry, b, h1, align, alpha); y += (b.h * scale + 4.0f) * alpha; // including alpha here gets us smooth animations. } - - // Thin bar at the top of the screen. - // TODO: Remove and replace with "proper" progress bars. - std::vector progress = g_DownloadManager.GetCurrentProgress(); - if (!progress.empty()) { - static const uint32_t colors[4] = { - 0xFFFFFFFF, - 0xFFCCCCCC, - 0xFFAAAAAA, - 0xFF777777, - }; - - dc.Begin(); - int h = 5; - for (size_t i = 0; i < progress.size(); i++) { - float barWidth = 10 + (dc.GetBounds().w - 10) * progress[i]; - Bounds bounds(0, h * i, barWidth, h); - UI::Drawable solid(colors[i & 3]); - dc.FillRect(solid, bounds); - } - dc.Flush(); - } } std::string OnScreenMessagesView::DescribeText() const { diff --git a/UI/RemoteISOScreen.cpp b/UI/RemoteISOScreen.cpp index 5436750cc5..b80df30021 100644 --- a/UI/RemoteISOScreen.cpp +++ b/UI/RemoteISOScreen.cpp @@ -139,7 +139,7 @@ bool RemoteISOConnectScreen::FindServer(std::string &resultHost, int &resultPort } SetStatus("Loading game list from [URL]...", host, port); - http::RequestProgress progress(&scanCancelled); + net::RequestProgress progress(&scanCancelled); code = http.GET(http::RequestParams(subdir.c_str()), &result, &progress); http.Disconnect(); @@ -193,7 +193,7 @@ bool RemoteISOConnectScreen::FindServer(std::string &resultHost, int &resultPort SetStatus("Looking for peers...", "", 0); if (http.Resolve(REPORT_HOSTNAME, REPORT_PORT)) { if (http.Connect(2, 20.0, &scanCancelled)) { - http::RequestProgress progress(&scanCancelled); + net::RequestProgress progress(&scanCancelled); code = http.GET(http::RequestParams("/match/list"), &result, &progress); http.Disconnect(); } diff --git a/UI/RetroAchievementScreens.cpp b/UI/RetroAchievementScreens.cpp index 143a49c238..8bc67c8d44 100644 --- a/UI/RetroAchievementScreens.cpp +++ b/UI/RetroAchievementScreens.cpp @@ -34,6 +34,10 @@ AudioFileChooser::AudioFileChooser(std::string *value, const std::string &title, layoutParams_->width = FILL_PARENT; layoutParams_->height = ITEM_HEIGHT; } + Add(new Choice(ImageID("I_PLAY"), new LinearLayoutParams(ITEM_HEIGHT, ITEM_HEIGHT)))->OnClick.Add([=](UI::EventParams &) { + g_BackgroundAudio.SFX().Play(sound_, 0.6f); + return UI::EVENT_DONE; + }); Add(new FileChooserChoice(value, title, BrowseFileType::SOUND_EFFECT, new LinearLayoutParams(1.0f)))->OnChange.Add([=](UI::EventParams &e) { // TODO: Check the file format here. // Need to forward the event out. @@ -50,10 +54,6 @@ AudioFileChooser::AudioFileChooser(std::string *value, const std::string &title, } return UI::EVENT_DONE; }); - Add(new Choice(ImageID("I_ARROW_RIGHT"), new LinearLayoutParams(ITEM_HEIGHT, ITEM_HEIGHT)))->OnClick.Add([=](UI::EventParams &) { - g_BackgroundAudio.SFX().Play(sound_, 0.6f); - return UI::EVENT_DONE; - }); Add(new Choice(ImageID("I_TRASHCAN"), new LinearLayoutParams(ITEM_HEIGHT, ITEM_HEIGHT)))->OnClick.Add([=](UI::EventParams &) { g_BackgroundAudio.SFX().UpdateSample(sound, nullptr); value->clear(); @@ -290,7 +290,20 @@ void RetroAchievementsSettingsScreen::CreateAccountTab(UI::ViewGroup *viewGroup) return UI::EVENT_DONE; }); } else { - if (System_GetPropertyBool(SYSPROP_HAS_LOGIN_DIALOG)) { + std::string errorMessage; + if (Achievements::LoginProblems(&errorMessage)) { + viewGroup->Add(new NoticeView(NoticeLevel::WARN, ac->T("Failed logging in to RetroAchievements"), errorMessage)); + if (Achievements::HasToken()) { + viewGroup->Add(new Choice(di->T("Retry")))->OnClick.Add([=](UI::EventParams &) -> UI::EventReturn { + Achievements::TryLoginByToken(); + return UI::EVENT_DONE; + }); + } + viewGroup->Add(new Choice(di->T("Log out")))->OnClick.Add([=](UI::EventParams &) -> UI::EventReturn { + Achievements::Logout(); + return UI::EVENT_DONE; + }); + } else if (System_GetPropertyBool(SYSPROP_HAS_LOGIN_DIALOG)) { viewGroup->Add(new Choice(ac->T("Log in")))->OnClick.Add([=](UI::EventParams &) -> UI::EventReturn { System_AskUsernamePassword(ac->T("Log in"), [](const std::string &value, int) { std::vector parts; @@ -662,14 +675,14 @@ void AchievementView::Click() { #ifdef _DEBUG static int type = 0; type++; - type = type % 4; + type = type % 5; switch (type) { case 0: g_OSD.ShowAchievementUnlocked(achievement_->id); break; - case 1: g_OSD.ShowAchievementProgress(achievement_->id, 2.0f); break; - case 2: g_OSD.ShowChallengeIndicator(achievement_->id, true); break; - case 3: g_OSD.ShowChallengeIndicator(achievement_->id, false); break; + case 1: g_OSD.ShowAchievementProgress(achievement_->id, true); break; + case 2: g_OSD.ShowAchievementProgress(achievement_->id, false); break; + case 3: g_OSD.ShowChallengeIndicator(achievement_->id, true); break; + case 4: g_OSD.ShowChallengeIndicator(achievement_->id, false); break; } - #endif } diff --git a/UI/Store.cpp b/UI/Store.cpp index 2682b171cb..7a7f91a09a 100644 --- a/UI/Store.cpp +++ b/UI/Store.cpp @@ -35,7 +35,12 @@ #include "UI/EmuScreen.h" #include "UI/Store.h" -const std::string storeBaseUrl = "http://store.ppsspp.org/"; +const char *storeBaseUrlHttp = "http://store.ppsspp.org/"; +const char *storeBaseUrlHttps = "https://store.ppsspp.org/"; + +static std::string StoreBaseUrl() { + return System_GetPropertyBool(SYSPROP_SUPPORTS_HTTPS) ? storeBaseUrlHttps : storeBaseUrlHttp; +} // baseUrl is assumed to have a trailing slash, and not contain any subdirectories. std::string ResolveUrl(std::string baseUrl, std::string url) { @@ -53,16 +58,20 @@ std::string ResolveUrl(std::string baseUrl, std::string url) { class HttpImageFileView : public UI::View { public: - HttpImageFileView(http::Downloader *downloader, const std::string &path, UI::ImageSizeMode sizeMode = UI::IS_DEFAULT, bool useIconCache = true, UI::LayoutParams *layoutParams = nullptr) - : UI::View(layoutParams), path_(path), sizeMode_(sizeMode), downloader_(downloader), useIconCache_(useIconCache) { + HttpImageFileView(http::RequestManager *requestManager, const std::string &path, UI::ImageSizeMode sizeMode = UI::IS_DEFAULT, bool useIconCache = true, UI::LayoutParams *layoutParams = nullptr) + : UI::View(layoutParams), path_(path), sizeMode_(sizeMode), requestManager_(requestManager), useIconCache_(useIconCache) { if (useIconCache && g_iconCache.MarkPending(path_)) { const char *acceptMime = "image/png, image/jpeg, image/*; q=0.9, */*; q=0.8"; - downloader_->StartDownloadWithCallback(path_, Path(), [&](http::Download &download) { + requestManager_->StartDownloadWithCallback(path_, Path(), http::ProgressBarMode::DELAYED, [&](http::Request &download) { if (download.ResultCode() == 200) { std::string data; download.buffer().TakeAll(&data); - g_iconCache.InsertIcon(path_, IconFormat::PNG, std::move(data)); + if (!data.empty()) { + g_iconCache.InsertIcon(path_, IconFormat::PNG, std::move(data)); + } else { + g_iconCache.Cancel(path_); + } } else { g_iconCache.Cancel(path_); } @@ -89,15 +98,15 @@ public: const std::string &GetFilename() const { return path_; } private: - void DownloadCompletedCallback(http::Download &download); + void DownloadCompletedCallback(http::Request &download); bool canFocus_ = false; bool useIconCache_ = false; std::string path_; // or cache key uint32_t color_ = 0xFFFFFFFF; UI::ImageSizeMode sizeMode_; - http::Downloader *downloader_; - std::shared_ptr download_; + http::RequestManager *requestManager_; + std::shared_ptr download_; std::string textureData_; std::unique_ptr texture_; @@ -146,7 +155,7 @@ void HttpImageFileView::SetFilename(std::string filename) { } } -void HttpImageFileView::DownloadCompletedCallback(http::Download &download) { +void HttpImageFileView::DownloadCompletedCallback(http::Request &download) { if (download.IsCancelled()) { // We were probably destroyed. Can't touch "this" (heh). return; @@ -165,8 +174,7 @@ void HttpImageFileView::Draw(UIContext &dc) { if (!texture_ && !textureFailed_ && !path_.empty() && !download_) { auto cb = std::bind(&HttpImageFileView::DownloadCompletedCallback, this, std::placeholders::_1); const char *acceptMime = "image/png, image/jpeg, image/*; q=0.9, */*; q=0.8"; - download_ = downloader_->StartDownloadWithCallback(path_, Path(), cb, acceptMime); - download_->SetHidden(true); + requestManager_->StartDownloadWithCallback(path_, Path(), http::ProgressBarMode::NONE, cb, acceptMime); } if (!textureData_.empty()) { @@ -276,7 +284,7 @@ void ProductView::CreateViews() { Clear(); if (!entry_.iconURL.empty()) { - Add(new HttpImageFileView(&g_DownloadManager, ResolveUrl(storeBaseUrl, entry_.iconURL), IS_FIXED))->SetFixedSize(144, 88); + Add(new HttpImageFileView(&g_DownloadManager, ResolveUrl(StoreBaseUrl(), entry_.iconURL), IS_FIXED))->SetFixedSize(144, 88); } Add(new TextView(entry_.name)); Add(new TextView(entry_.author)); @@ -347,7 +355,7 @@ void ProductView::Update() { std::string ProductView::DownloadURL() { if (entry_.downloadURL.empty()) { // Construct the URL. - return storeBaseUrl + "files/" + entry_.file + ".zip"; + return StoreBaseUrl() + "files/" + entry_.file + ".zip"; } else { // Use the provided URL, for external hosting. return entry_.downloadURL; @@ -402,9 +410,9 @@ StoreScreen::StoreScreen() { lang_ = g_Config.sLanguageIni; loading_ = true; - std::string indexPath = storeBaseUrl + "index.json"; + std::string indexPath = StoreBaseUrl() + "index.json"; const char *acceptMime = "application/json, */*; q=0.8"; - listing_ = g_DownloadManager.StartDownload(indexPath, Path(), acceptMime); + listing_ = g_DownloadManager.StartDownload(indexPath, Path(), http::ProgressBarMode::DELAYED, acceptMime); } StoreScreen::~StoreScreen() { diff --git a/UI/Store.h b/UI/Store.h index 0c4677d511..3c0be4668d 100644 --- a/UI/Store.h +++ b/UI/Store.h @@ -78,8 +78,8 @@ private: std::string GetTranslatedString(const json::JsonGet json, std::string key, const char *fallback = nullptr) const; - std::shared_ptr listing_; - std::shared_ptr image_; + std::shared_ptr listing_; + std::shared_ptr image_; // TODO: Replace with a PathBrowser or similar. Though that one only supports // local filesystems at the moment. diff --git a/UI/Theme.cpp b/UI/Theme.cpp index 9119259e7c..3e4b6193a2 100644 --- a/UI/Theme.cpp +++ b/UI/Theme.cpp @@ -191,12 +191,12 @@ void UpdateTheme(UIContext *ctx) { #if defined(USING_WIN_UI) || PPSSPP_PLATFORM(UWP) || defined(USING_QT_UI) ui_theme.uiFont = UI::FontStyle(FontID("UBUNTU24"), g_Config.sFont.c_str(), 22); - ui_theme.uiFontSmall = UI::FontStyle(FontID("UBUNTU24"), g_Config.sFont.c_str(), 15); - ui_theme.uiFontSmaller = UI::FontStyle(FontID("UBUNTU24"), g_Config.sFont.c_str(), 12); + ui_theme.uiFontSmall = UI::FontStyle(FontID("UBUNTU24"), g_Config.sFont.c_str(), 17); + ui_theme.uiFontSmaller = UI::FontStyle(FontID("UBUNTU24"), g_Config.sFont.c_str(), 13); #else ui_theme.uiFont = UI::FontStyle(FontID("UBUNTU24"), "", 20); - ui_theme.uiFontSmall = UI::FontStyle(FontID("UBUNTU24"), "", 14); - ui_theme.uiFontSmaller = UI::FontStyle(FontID("UBUNTU24"), "", 11); + ui_theme.uiFontSmall = UI::FontStyle(FontID("UBUNTU24"), "", 15); + ui_theme.uiFontSmaller = UI::FontStyle(FontID("UBUNTU24"), "", 12); #endif ui_theme.checkOn = ImageID("I_CHECKEDBOX"); diff --git a/UWP/CommonUWP/CommonUWP.vcxproj b/UWP/CommonUWP/CommonUWP.vcxproj index e5a72c7d32..3e9878bf65 100644 --- a/UWP/CommonUWP/CommonUWP.vcxproj +++ b/UWP/CommonUWP/CommonUWP.vcxproj @@ -114,6 +114,8 @@ + + @@ -259,6 +261,7 @@ + @@ -273,6 +276,8 @@ + + @@ -398,6 +403,7 @@ + diff --git a/UWP/CommonUWP/CommonUWP.vcxproj.filters b/UWP/CommonUWP/CommonUWP.vcxproj.filters index 2dfc1ae397..2eedf9e8c6 100644 --- a/UWP/CommonUWP/CommonUWP.vcxproj.filters +++ b/UWP/CommonUWP/CommonUWP.vcxproj.filters @@ -94,6 +94,9 @@ {8b92cc15-8e3e-45b1-ba80-ad39b1a9cdc8} + + {403c3154-5e8a-4da1-ba82-8f69bef25e14} + @@ -444,6 +447,15 @@ System + + Net + + + Net + + + ext\naett + @@ -841,6 +853,15 @@ System + + Net + + + Net + + + ext\naett + diff --git a/UWP/CoreUWP/CoreUWP.vcxproj b/UWP/CoreUWP/CoreUWP.vcxproj index ca53e7d496..5af1ee85d0 100644 --- a/UWP/CoreUWP/CoreUWP.vcxproj +++ b/UWP/CoreUWP/CoreUWP.vcxproj @@ -302,6 +302,7 @@ + @@ -561,6 +562,7 @@ + diff --git a/UWP/CoreUWP/CoreUWP.vcxproj.filters b/UWP/CoreUWP/CoreUWP.vcxproj.filters index f6ccb537f0..363504bba7 100644 --- a/UWP/CoreUWP/CoreUWP.vcxproj.filters +++ b/UWP/CoreUWP/CoreUWP.vcxproj.filters @@ -645,6 +645,9 @@ MIPS\IR + + MIPS\IR + MIPS\IR @@ -1667,6 +1670,9 @@ MIPS\IR + + MIPS\IR + MIPS\IR diff --git a/UWP/PPSSPP_UWPMain.cpp b/UWP/PPSSPP_UWPMain.cpp index 3c4be6773d..01c6bec6bd 100644 --- a/UWP/PPSSPP_UWPMain.cpp +++ b/UWP/PPSSPP_UWPMain.cpp @@ -377,6 +377,8 @@ std::string System_GetProperty(SystemProperty prop) { return ""; case SYSPROP_GPUDRIVER_VERSION: return ""; + case SYSPROP_BUILD_VERSION: + return PPSSPP_GIT_VERSION; default: return ""; } diff --git a/UWP/UWP.vcxproj b/UWP/UWP.vcxproj index 9785be097a..55789fece1 100644 --- a/UWP/UWP.vcxproj +++ b/UWP/UWP.vcxproj @@ -859,6 +859,9 @@ true + + true + true diff --git a/UWP/UWP.vcxproj.filters b/UWP/UWP.vcxproj.filters index 79e7debc42..e4f984a3cb 100644 --- a/UWP/UWP.vcxproj.filters +++ b/UWP/UWP.vcxproj.filters @@ -363,6 +363,9 @@ Content\shaders + + Content\shaders + Content\shaders diff --git a/Windows/Debugger/BreakpointWindow.cpp b/Windows/Debugger/BreakpointWindow.cpp index 1a1b1a1d21..d26c5f6c29 100644 --- a/Windows/Debugger/BreakpointWindow.cpp +++ b/Windows/Debugger/BreakpointWindow.cpp @@ -11,7 +11,8 @@ INT_PTR CALLBACK BreakpointWindow::StaticDlgFunc(HWND hWnd, UINT iMsg, WPARAM wP if (iMsg == WM_INITDIALOG) { SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR)lParam); thiz = (BreakpointWindow *)lParam; - } else { + } + else { thiz = (BreakpointWindow *)GetWindowLongPtr(hWnd, GWLP_USERDATA); } @@ -40,15 +41,15 @@ INT_PTR BreakpointWindow::DlgFunc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lP EnableWindow(GetDlgItem(hwnd, IDC_BREAKPOINT_ONCHANGE), memory); EnableWindow(GetDlgItem(hwnd, IDC_BREAKPOINT_SIZE), memory); EnableWindow(GetDlgItem(hwnd, IDC_BREAKPOINT_LOG_FORMAT), log); - + if (address != -1) { snprintf(str, sizeof(str), "0x%08X", address); - SetWindowTextA(GetDlgItem(hwnd,IDC_BREAKPOINT_ADDRESS),str); + SetWindowTextA(GetDlgItem(hwnd, IDC_BREAKPOINT_ADDRESS), str); } snprintf(str, sizeof(str), "0x%08X", size); - SetWindowTextA(GetDlgItem(hwnd, IDC_BREAKPOINT_SIZE),str); - + SetWindowTextA(GetDlgItem(hwnd, IDC_BREAKPOINT_SIZE), str); + SetWindowTextW(GetDlgItem(hwnd, IDC_BREAKPOINT_CONDITION), ConvertUTF8ToWString(condition).c_str()); SetWindowTextW(GetDlgItem(hwnd, IDC_BREAKPOINT_LOG_FORMAT), ConvertUTF8ToWString(logFormat).c_str()); return TRUE; @@ -121,7 +122,7 @@ INT_PTR BreakpointWindow::DlgFunc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lP bool BreakpointWindow::fetchDialogData(HWND hwnd) { - char str[256],errorMessage[512]; + char str[256], errorMessage[512]; PostfixExpression exp; memory = GetCheckState(hwnd, IDC_BREAKPOINT_MEMORY); @@ -132,36 +133,36 @@ bool BreakpointWindow::fetchDialogData(HWND hwnd) onChange = GetCheckState(hwnd, IDC_BREAKPOINT_ONCHANGE); // parse address - GetWindowTextA(GetDlgItem(hwnd,IDC_BREAKPOINT_ADDRESS),str,256); - if (cpu->initExpression(str,exp) == false) + GetWindowTextA(GetDlgItem(hwnd, IDC_BREAKPOINT_ADDRESS), str, 256); + if (cpu->initExpression(str, exp) == false) { snprintf(errorMessage, sizeof(errorMessage), "Invalid expression \"%s\": %s", str, getExpressionError()); - MessageBoxA(hwnd,errorMessage,"Error",MB_OK); + MessageBoxA(hwnd, errorMessage, "Error", MB_OK); return false; } - if (cpu->parseExpression(exp,address) == false) + if (cpu->parseExpression(exp, address) == false) { snprintf(errorMessage, sizeof(errorMessage), "Invalid expression \"%s\": %s", str, getExpressionError()); - MessageBoxA(hwnd,errorMessage,"Error",MB_OK); + MessageBoxA(hwnd, errorMessage, "Error", MB_OK); return false; } if (memory) { // parse size - GetWindowTextA(GetDlgItem(hwnd,IDC_BREAKPOINT_SIZE),str,256); - if (cpu->initExpression(str,exp) == false) + GetWindowTextA(GetDlgItem(hwnd, IDC_BREAKPOINT_SIZE), str, 256); + if (cpu->initExpression(str, exp) == false) { snprintf(errorMessage, sizeof(errorMessage), "Invalid expression \"%s\": %s", str, getExpressionError()); - MessageBoxA(hwnd,errorMessage,"Error",MB_OK); + MessageBoxA(hwnd, errorMessage, "Error", MB_OK); return false; } - if (cpu->parseExpression(exp,size) == false) + if (cpu->parseExpression(exp, size) == false) { snprintf(errorMessage, sizeof(errorMessage), "Invalid expression \"%s\": %s", str, getExpressionError()); - MessageBoxA(hwnd,errorMessage,"Error",MB_OK); + MessageBoxA(hwnd, errorMessage, "Error", MB_OK); return false; } } @@ -176,7 +177,7 @@ bool BreakpointWindow::fetchDialogData(HWND hwnd) if (cpu->initExpression(condition.c_str(), compiledCondition) == false) { snprintf(errorMessage, sizeof(errorMessage), "Invalid expression \"%s\": %s", condition.c_str(), getExpressionError()); - MessageBoxA(hwnd,errorMessage,"Error",MB_OK); + MessageBoxA(hwnd, errorMessage, "Error", MB_OK); return false; } } @@ -229,16 +230,17 @@ void BreakpointWindow::addBreakpoint() { } CBreakPoints::ChangeMemCheckLogFormat(address, address + size, logFormat); - } else { + } + else { // add breakpoint - CBreakPoints::AddBreakPoint(address,false); + CBreakPoints::AddBreakPoint(address, false); if (!condition.empty()) { BreakPointCond cond; cond.debug = cpu; cond.expressionString = condition; cond.expression = compiledCondition; - CBreakPoints::ChangeBreakPointAddCond(address,cond); + CBreakPoints::ChangeBreakPointAddCond(address, cond); } CBreakPoints::ChangeBreakPoint(address, result); @@ -257,11 +259,12 @@ void BreakpointWindow::loadFromMemcheck(const MemCheck &memcheck) { enabled = (memcheck.result & BREAK_ACTION_PAUSE) != 0; address = memcheck.start; - size = memcheck.end-address; + size = memcheck.end - address; if (memcheck.hasCondition) { condition = memcheck.condition.expressionString; - } else { + } + else { condition.clear(); } @@ -278,7 +281,8 @@ void BreakpointWindow::loadFromBreakpoint(const BreakPoint& breakpoint) { if (breakpoint.hasCond) { condition = breakpoint.cond.expressionString; - } else { + } + else { condition.clear(); } diff --git a/Windows/Debugger/CtrlDisAsmView.cpp b/Windows/Debugger/CtrlDisAsmView.cpp index 89529d06e6..946a131a1c 100644 --- a/Windows/Debugger/CtrlDisAsmView.cpp +++ b/Windows/Debugger/CtrlDisAsmView.cpp @@ -18,6 +18,7 @@ #include "Windows/Debugger/Debugger_MemoryDlg.h" #include "Windows/Debugger/DebuggerShared.h" #include "Windows/Debugger/BreakpointWindow.h" +#include "Windows/Debugger/EditSymbolsWindow.h" #include "Windows/main.h" #include "Common/CommonWindows.h" @@ -978,6 +979,16 @@ void CtrlDisAsmView::onMouseUp(WPARAM wParam, LPARAM lParam, int button) NopInstructions(selectRangeStart, selectRangeEnd); redraw(); break; + case ID_DISASM_EDITSYMBOLS: + { + EditSymbolsWindow esw(wnd, debugger); + if (esw.exec()) { + esw.eval(); + SendMessage(GetParent(wnd), WM_DEB_MAPLOADED, 0, 0); + redraw(); + } + } + break; case ID_DISASM_SETPCTOHERE: debugger->setPC(curAddress); redraw(); diff --git a/Windows/Debugger/EditSymbolsWindow.cpp b/Windows/Debugger/EditSymbolsWindow.cpp new file mode 100644 index 0000000000..607b67e3d5 --- /dev/null +++ b/Windows/Debugger/EditSymbolsWindow.cpp @@ -0,0 +1,192 @@ +#include "EditSymbolsWindow.h" +#include "../resource.h" + + +bool EditSymbolsWindow::GetCheckState(HWND hwnd, int dlgItem) { + return SendMessage(GetDlgItem(hwnd, dlgItem), BM_GETCHECK, 0, 0) != 0; +} + +bool EditSymbolsWindow::fetchDialogData(HWND hwnd) +{ + char str[256], errorMessage[512]; + PostfixExpression exp; + + scan_ = GetCheckState(hwnd, IDC_EDITSYMBOLS_SCAN); + + // Parse the address + GetWindowTextA(GetDlgItem(hwnd, IDC_EDITSYMBOLS_ADDRESS), str, 256); + + if (cpu->initExpression(str, exp) == false) + { + snprintf(errorMessage, sizeof(errorMessage), "Invalid expression \"%s\": %s", str, getExpressionError()); + MessageBoxA(hwnd, errorMessage, "Error", MB_OK); + return false; + } + if (cpu->parseExpression(exp, address_) == false) + { + snprintf(errorMessage, sizeof(errorMessage), "Invalid expression \"%s\": %s", str, getExpressionError()); + MessageBoxA(hwnd, errorMessage, "Error", MB_OK); + return false; + } + + // Parse the size + GetWindowTextA(GetDlgItem(hwnd, IDC_EDITSYMBOLS_SIZE), str, 256); + + if (cpu->initExpression(str, exp) == false) + { + snprintf(errorMessage, sizeof(errorMessage), "Invalid expression \"%s\": %s", str, getExpressionError()); + MessageBoxA(hwnd, errorMessage, "Error", MB_OK); + return false; + } + if (cpu->parseExpression(exp, size_) == false) + { + snprintf(errorMessage, sizeof(errorMessage), "Invalid expression \"%s\": %s", str, getExpressionError()); + MessageBoxA(hwnd, errorMessage, "Error", MB_OK); + return false; + } + + // Now let's validate the range + if (!Memory::IsValidRange(address_, size_)) { + MessageBoxA(hwnd, "Invalid range", "Error", MB_OK); + return false; + } + + return true; +} + +INT_PTR CALLBACK EditSymbolsWindow::StaticDlgFunc(HWND hWnd, UINT iMsg, WPARAM wParam, LPARAM lParam) { + EditSymbolsWindow *thiz; + if (iMsg == WM_INITDIALOG) { + SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR)lParam); + thiz = (EditSymbolsWindow *)lParam; + } + else { + thiz = (EditSymbolsWindow *)GetWindowLongPtr(hWnd, GWLP_USERDATA); + } + + if (!thiz) + return FALSE; + return thiz->DlgFunc(hWnd, iMsg, wParam, lParam); +} + +INT_PTR EditSymbolsWindow::DlgFunc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam) +{ + char str[128]; + + switch (iMsg) + { + case WM_INITDIALOG: + + // Set the radiobutton values + SendMessage(GetDlgItem(hwnd, IDC_EDITSYMBOLS_SCAN), BM_SETCHECK, scan_ ? BST_CHECKED : BST_UNCHECKED, 0); + SendMessage(GetDlgItem(hwnd, IDC_EDITSYMBOLS_REMOVE), BM_SETCHECK, scan_ ? BST_UNCHECKED : BST_CHECKED, 0); + + // Set the text in the textboxes + if (address_ != -1) { + snprintf(str, sizeof(str), "0x%08X", address_); + SetWindowTextA(GetDlgItem(hwnd, IDC_EDITSYMBOLS_ADDRESS), str); + } + snprintf(str, sizeof(str), "0x%08X", size_); + SetWindowTextA(GetDlgItem(hwnd, IDC_EDITSYMBOLS_SIZE), str); + + return TRUE; + case WM_COMMAND: + switch (LOWORD(wParam)) + { + case IDC_EDITSYMBOLS_SCAN: + switch (HIWORD(wParam)) + { + case BN_CLICKED: + scan_ = true; + break; + } + break; + case IDC_EDITSYMBOLS_REMOVE: + switch (HIWORD(wParam)) + { + case BN_CLICKED: + scan_ = false; + break; + } + break; + case IDC_EDITSYMBOLS_OK: + switch (HIWORD(wParam)) + { + case BN_CLICKED: + if (fetchDialogData(hwnd)) { + EndDialog(hwnd, true); + } + break; + }; + break; + case IDC_EDITSYMBOLS_CANCEL: + switch (HIWORD(wParam)) + { + case BN_CLICKED: + EndDialog(hwnd, false); + break; + }; + break; + case IDOK: + if (fetchDialogData(hwnd)) { + EndDialog(hwnd, true); + } + break; + case IDCANCEL: + EndDialog(hwnd, false); + break; + } + } + + return FALSE; +} + +bool EditSymbolsWindow::exec() { + return DialogBoxParam(GetModuleHandle(0), MAKEINTRESOURCE(IDD_EDITSYMBOLS), parentHwnd, StaticDlgFunc, (LPARAM)this) != 0; +} + +void EditSymbolsWindow::Scan() { + bool insertSymbols = MIPSAnalyst::ScanForFunctions(address_, address_ + size_ - 1, true); + MIPSAnalyst::FinalizeScan(insertSymbols); +} + +void EditSymbolsWindow::Remove() { + u32 func_address = g_symbolMap->GetFunctionStart(address_); + if (func_address == SymbolMap::INVALID_ADDRESS) { + func_address = g_symbolMap->GetNextSymbolAddress(address_, SymbolType::ST_FUNCTION); + } + + u32 counter = 0; + while (func_address < address_ + size_ && func_address != SymbolMap::INVALID_ADDRESS) { + g_symbolMap->RemoveFunction(func_address, true); + ++counter; + func_address = g_symbolMap->GetNextSymbolAddress(address_, SymbolType::ST_FUNCTION); + } + + if (counter) { + MIPSAnalyst::ForgetFunctions(address_, address_ + size_ - 1); + + // The following was copied from hle.func.remove: + g_symbolMap->SortSymbols(); + + MIPSAnalyst::UpdateHashMap(); + MIPSAnalyst::ApplyHashMap(); + + if (g_Config.bFuncReplacements) { + MIPSAnalyst::ReplaceFunctions(); + } + + // Clear cache for branch lines and such. + DisassemblyManager manager; + manager.clear(); + } +} + +void EditSymbolsWindow::eval() { + if (scan_) { + Scan(); + } + else { + Remove(); + } +} diff --git a/Windows/Debugger/EditSymbolsWindow.h b/Windows/Debugger/EditSymbolsWindow.h new file mode 100644 index 0000000000..cc5baa9757 --- /dev/null +++ b/Windows/Debugger/EditSymbolsWindow.h @@ -0,0 +1,39 @@ +#pragma once +#include +#include "Common/CommonWindows.h" +#include "Common/CommonTypes.h" +#include "Core/Config.h" +#include "Core/Debugger/DebugInterface.h" +#include "Core/Debugger/SymbolMap.h" +#include "Core/MIPS/MIPSAnalyst.h" +#include "Core/Debugger/DisassemblyManager.h" +#include "Core/MemMap.h" + +class EditSymbolsWindow { + HWND parentHwnd; + DebugInterface* cpu; + + bool scan_; + u32 address_; + u32 size_; + + void Scan(); + void Remove(); + + bool GetCheckState(HWND hwnd, int dlgItem); + bool fetchDialogData(HWND hwnd); + + static INT_PTR CALLBACK StaticDlgFunc(HWND hWnd, UINT iMsg, WPARAM wParam, LPARAM lParam); + INT_PTR DlgFunc(HWND hWnd, UINT iMsg, WPARAM wParam, LPARAM lParam); + +public: + EditSymbolsWindow(HWND parent, DebugInterface* cpu) : cpu(cpu) { + parentHwnd = parent; + scan_ = true; + address_ = -1; + size_ = 1; + } + + bool exec(); + void eval(); +}; diff --git a/Windows/GPU/D3D11Context.cpp b/Windows/GPU/D3D11Context.cpp index ed503ab2fc..7319f989f9 100644 --- a/Windows/GPU/D3D11Context.cpp +++ b/Windows/GPU/D3D11Context.cpp @@ -59,13 +59,6 @@ HRESULT D3D11Context::CreateTheDevice(IDXGIAdapter *adapter) { UINT createDeviceFlags = 0; #endif - static const D3D_DRIVER_TYPE driverTypes[] = { - D3D_DRIVER_TYPE_HARDWARE, - D3D_DRIVER_TYPE_WARP, - D3D_DRIVER_TYPE_REFERENCE, - }; - const UINT numDriverTypes = ARRAYSIZE(driverTypes); - static const D3D_FEATURE_LEVEL featureLevels[] = { D3D_FEATURE_LEVEL_12_1, D3D_FEATURE_LEVEL_12_0, diff --git a/Windows/GPU/WindowsVulkanContext.cpp b/Windows/GPU/WindowsVulkanContext.cpp index 818f398420..e4c31ab8e9 100644 --- a/Windows/GPU/WindowsVulkanContext.cpp +++ b/Windows/GPU/WindowsVulkanContext.cpp @@ -131,7 +131,7 @@ bool WindowsVulkanContext::Init(HINSTANCE hInst, HWND hWnd, std::string *error_m return false; } - draw_ = Draw::T3DCreateVulkanContext(vulkan_); + draw_ = Draw::T3DCreateVulkanContext(vulkan_, g_Config.bRenderMultiThreading); SetGPUBackend(GPUBackend::VULKAN, vulkan_->GetPhysicalDeviceProperties(deviceNum).properties.deviceName); bool success = draw_->CreatePresets(); _assert_msg_(success, "Failed to compile preset shaders"); diff --git a/Windows/InputBox.cpp b/Windows/InputBox.cpp index 9db0b10dbd..49cb2eb4e2 100644 --- a/Windows/InputBox.cpp +++ b/Windows/InputBox.cpp @@ -1,3 +1,4 @@ +#include "Common/Common.h" #include "Common/CommonTypes.h" #include "Common/CommonWindows.h" #include "Windows/InputBox.h" diff --git a/Windows/PPSSPP.vcxproj b/Windows/PPSSPP.vcxproj index f0a38f80da..881a31cab5 100644 --- a/Windows/PPSSPP.vcxproj +++ b/Windows/PPSSPP.vcxproj @@ -249,7 +249,7 @@ stdcpp17 - uxtheme.lib;mf.lib;mfplat.lib;mfreadwrite.lib;mfuuid.lib;shlwapi.lib;Winmm.lib;Ws2_32.lib;dsound.lib;comctl32.lib;d3d9.lib;dxguid.lib;avcodec.lib;avformat.lib;avutil.lib;swresample.lib;swscale.lib;opengl32.lib;glu32.lib;%(AdditionalDependencies) + winhttp.lib;uxtheme.lib;mf.lib;mfplat.lib;mfreadwrite.lib;mfuuid.lib;shlwapi.lib;Winmm.lib;Ws2_32.lib;dsound.lib;comctl32.lib;d3d9.lib;dxguid.lib;avcodec.lib;avformat.lib;avutil.lib;swresample.lib;swscale.lib;opengl32.lib;glu32.lib;%(AdditionalDependencies) ../ffmpeg/Windows/x86/lib true Windows @@ -289,7 +289,7 @@ stdcpp17 - uxtheme.lib;mf.lib;mfplat.lib;mfreadwrite.lib;mfuuid.lib;shlwapi.lib;Winmm.lib;Ws2_32.lib;dsound.lib;comctl32.lib;d3d9.lib;dxguid.lib;avcodec.lib;avformat.lib;avutil.lib;swresample.lib;swscale.lib;opengl32.lib;glu32.lib;%(AdditionalDependencies) + winhttp.lib;uxtheme.lib;mf.lib;mfplat.lib;mfreadwrite.lib;mfuuid.lib;shlwapi.lib;Winmm.lib;Ws2_32.lib;dsound.lib;comctl32.lib;d3d9.lib;dxguid.lib;avcodec.lib;avformat.lib;avutil.lib;swresample.lib;swscale.lib;opengl32.lib;glu32.lib;%(AdditionalDependencies) ../ffmpeg/Windows/x86_64/lib true $(OutDir)$(ProjectName).pdb @@ -325,7 +325,7 @@ stdcpp17 - uxtheme.lib;mf.lib;mfplat.lib;mfreadwrite.lib;mfuuid.lib;shlwapi.lib;Winmm.lib;Ws2_32.lib;dsound.lib;comctl32.lib;d3d9.lib;dxguid.lib;avcodec.lib;avformat.lib;avutil.lib;swresample.lib;swscale.lib;oleaut32.lib;comdlg32.lib;shell32.lib;user32.lib;gdi32.lib;advapi32.lib;ole32.lib;%(AdditionalDependencies) + winhttp.lib;uxtheme.lib;mf.lib;mfplat.lib;mfreadwrite.lib;mfuuid.lib;shlwapi.lib;Winmm.lib;Ws2_32.lib;dsound.lib;comctl32.lib;d3d9.lib;dxguid.lib;avcodec.lib;avformat.lib;avutil.lib;swresample.lib;swscale.lib;oleaut32.lib;comdlg32.lib;shell32.lib;user32.lib;gdi32.lib;advapi32.lib;ole32.lib;%(AdditionalDependencies) ../ffmpeg/Windows/aarch64/lib true $(OutDir)$(ProjectName).pdb @@ -359,7 +359,7 @@ stdcpp17 - uxtheme.lib;mf.lib;mfplat.lib;mfreadwrite.lib;mfuuid.lib;shlwapi.lib;Winmm.lib;Ws2_32.lib;dsound.lib;comctl32.lib;d3d9.lib;dxguid.lib;avcodec.lib;avformat.lib;avutil.lib;swresample.lib;swscale.lib;oleaut32.lib;comdlg32.lib;shell32.lib;user32.lib;gdi32.lib;advapi32.lib;ole32.lib;%(AdditionalDependencies) + winhttp.lib;uxtheme.lib;mf.lib;mfplat.lib;mfreadwrite.lib;mfuuid.lib;shlwapi.lib;Winmm.lib;Ws2_32.lib;dsound.lib;comctl32.lib;d3d9.lib;dxguid.lib;avcodec.lib;avformat.lib;avutil.lib;swresample.lib;swscale.lib;oleaut32.lib;comdlg32.lib;shell32.lib;user32.lib;gdi32.lib;advapi32.lib;ole32.lib;%(AdditionalDependencies) ../ffmpeg/Windows/arm/lib true $(OutDir)$(ProjectName).pdb @@ -397,7 +397,7 @@ stdcpp17 - uxtheme.lib;mf.lib;mfplat.lib;mfreadwrite.lib;mfuuid.lib;shlwapi.lib;Winmm.lib;Ws2_32.lib;dsound.lib;comctl32.lib;d3d9.lib;dxguid.lib;avcodec.lib;avformat.lib;avutil.lib;swresample.lib;swscale.lib;opengl32.lib;glu32.lib;%(AdditionalDependencies) + winhttp.lib;uxtheme.lib;mf.lib;mfplat.lib;mfreadwrite.lib;mfuuid.lib;shlwapi.lib;Winmm.lib;Ws2_32.lib;dsound.lib;comctl32.lib;d3d9.lib;dxguid.lib;avcodec.lib;avformat.lib;avutil.lib;swresample.lib;swscale.lib;opengl32.lib;glu32.lib;%(AdditionalDependencies) ../ffmpeg/Windows/x86/lib;%(AdditionalLibraryDirectories) $(OutDir)$(TargetName)$(TargetExt) true @@ -446,7 +446,7 @@ stdcpp17 - uxtheme.lib;mf.lib;mfplat.lib;mfreadwrite.lib;mfuuid.lib;shlwapi.lib;Winmm.lib;Ws2_32.lib;dsound.lib;comctl32.lib;d3d9.lib;dxguid.lib;avcodec.lib;avformat.lib;avutil.lib;swresample.lib;swscale.lib;opengl32.lib;glu32.lib;%(AdditionalDependencies) + winhttp.lib;uxtheme.lib;mf.lib;mfplat.lib;mfreadwrite.lib;mfuuid.lib;shlwapi.lib;Winmm.lib;Ws2_32.lib;dsound.lib;comctl32.lib;d3d9.lib;dxguid.lib;avcodec.lib;avformat.lib;avutil.lib;swresample.lib;swscale.lib;opengl32.lib;glu32.lib;%(AdditionalDependencies) ../ffmpeg/Windows/x86_64/lib;%(AdditionalLibraryDirectories) true Windows @@ -489,7 +489,7 @@ stdcpp17 - uxtheme.lib;mf.lib;mfplat.lib;mfreadwrite.lib;mfuuid.lib;shlwapi.lib;Winmm.lib;Ws2_32.lib;dsound.lib;comctl32.lib;d3d9.lib;dxguid.lib;avcodec.lib;avformat.lib;avutil.lib;swresample.lib;swscale.lib;oleaut32.lib;comdlg32.lib;shell32.lib;user32.lib;gdi32.lib;advapi32.lib;ole32.lib;%(AdditionalDependencies) + winhttp.lib;uxtheme.lib;mf.lib;mfplat.lib;mfreadwrite.lib;mfuuid.lib;shlwapi.lib;Winmm.lib;Ws2_32.lib;dsound.lib;comctl32.lib;d3d9.lib;dxguid.lib;avcodec.lib;avformat.lib;avutil.lib;swresample.lib;swscale.lib;oleaut32.lib;comdlg32.lib;shell32.lib;user32.lib;gdi32.lib;advapi32.lib;ole32.lib;%(AdditionalDependencies) ../ffmpeg/Windows/aarch64/lib;%(AdditionalLibraryDirectories) true Windows @@ -530,7 +530,7 @@ stdcpp17 - uxtheme.lib;mf.lib;mfplat.lib;mfreadwrite.lib;mfuuid.lib;shlwapi.lib;Winmm.lib;Ws2_32.lib;dsound.lib;comctl32.lib;d3d9.lib;dxguid.lib;avcodec.lib;avformat.lib;avutil.lib;swresample.lib;swscale.lib;oleaut32.lib;comdlg32.lib;shell32.lib;user32.lib;gdi32.lib;advapi32.lib;ole32.lib;%(AdditionalDependencies) + winhttp.lib;uxtheme.lib;mf.lib;mfplat.lib;mfreadwrite.lib;mfuuid.lib;shlwapi.lib;Winmm.lib;Ws2_32.lib;dsound.lib;comctl32.lib;d3d9.lib;dxguid.lib;avcodec.lib;avformat.lib;avutil.lib;swresample.lib;swscale.lib;oleaut32.lib;comdlg32.lib;shell32.lib;user32.lib;gdi32.lib;advapi32.lib;ole32.lib;%(AdditionalDependencies) ../ffmpeg/Windows/arm/lib;%(AdditionalLibraryDirectories) true Windows @@ -844,6 +844,7 @@ + @@ -1400,6 +1401,7 @@ + diff --git a/Windows/PPSSPP.vcxproj.filters b/Windows/PPSSPP.vcxproj.filters index 35fa64082c..1c228494e9 100644 --- a/Windows/PPSSPP.vcxproj.filters +++ b/Windows/PPSSPP.vcxproj.filters @@ -280,6 +280,9 @@ Windows\Debugger + + Windows\Debugger + @@ -568,6 +571,9 @@ Windows\Debugger + + Windows\Debugger + Other Platforms\Mac diff --git a/Windows/main.cpp b/Windows/main.cpp index e46931cff7..6cbd2e6243 100644 --- a/Windows/main.cpp +++ b/Windows/main.cpp @@ -234,6 +234,8 @@ std::string System_GetProperty(SystemProperty prop) { gpuDriverVersion = GetVideoCardDriverVersion(); } return gpuDriverVersion; + case SYSPROP_BUILD_VERSION: + return PPSSPP_GIT_VERSION; default: return ""; } @@ -373,6 +375,8 @@ bool System_GetPropertyBool(SystemProperty prop) { return true; case SYSPROP_SUPPORTS_OPEN_FILE_IN_EDITOR: return true; // FileUtil.cpp: OpenFileInEditor + case SYSPROP_SUPPORTS_HTTPS: + return true; default: return false; } diff --git a/Windows/ppsspp.rc b/Windows/ppsspp.rc index 85e33bee8c..e17d92bdde 100644 --- a/Windows/ppsspp.rc +++ b/Windows/ppsspp.rc @@ -380,6 +380,21 @@ BEGIN PUSHBUTTON "Cancel",IDC_BREAKPOINT_CANCEL,186,98,42,14 END +IDD_EDITSYMBOLS DIALOGEX 0, 0, 236, 72 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Edit symbols" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + LTEXT "Address",IDC_STATIC,7,8,27,8 + EDITTEXT IDC_EDITSYMBOLS_ADDRESS,41,7,69,14,ES_AUTOHSCROLL + CONTROL "Scan in range",IDC_EDITSYMBOLS_SCAN,"Button",BS_AUTORADIOBUTTON,144,8,70,10 + CONTROL "Remove in range",IDC_EDITSYMBOLS_REMOVE,"Button",BS_AUTORADIOBUTTON,144,27,70,10 + LTEXT "Size",IDC_STATIC,7,27,14,8 + EDITTEXT IDC_EDITSYMBOLS_SIZE,41,25,69,14,ES_AUTOHSCROLL + DEFPUSHBUTTON "OK",IDC_EDITSYMBOLS_OK,144,51,41,14 + PUSHBUTTON "Cancel",IDC_EDITSYMBOLS_CANCEL,186,51,42,14 +END + IDD_DUMPMEMORY DIALOGEX 0, 0, 230, 100 STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "Dump memory" @@ -755,6 +770,7 @@ BEGIN MENUITEM "Remove Function", ID_DISASM_REMOVEFUNCTION MENUITEM "Add Function Here", ID_DISASM_ADDFUNCTION MENUITEM "NOP instruction(s)", ID_DISASM_NOPINSTRUCTION + MENUITEM "Edit symbols...", ID_DISASM_EDITSYMBOLS END POPUP "reglist" BEGIN diff --git a/Windows/resource.h b/Windows/resource.h index ae1ff111c3..3eb5473ac1 100644 --- a/Windows/resource.h +++ b/Windows/resource.h @@ -61,6 +61,7 @@ #define IDD_GEDBG_TAB_MATRICES 255 #define IDD_GEDBG_STEPCOUNT 256 #define IDD_CPUWATCH 257 +#define IDD_EDITSYMBOLS 258 #define IDC_STOPGO 1001 #define IDC_ADDRESS 1002 @@ -122,6 +123,12 @@ #define IDC_SHOWOFFSETS 1200 #define IDC_GEDBG_PRIMCOUNTER 1201 #define IDC_BUTTON_SEARCH 1204 +#define IDC_EDITSYMBOLS_SCAN 1205 +#define IDC_EDITSYMBOLS_REMOVE 1206 +#define IDC_EDITSYMBOLS_ADDRESS 1207 +#define IDC_EDITSYMBOLS_SIZE 1208 +#define IDC_EDITSYMBOLS_OK 1209 +#define IDC_EDITSYMBOLS_CANCEL 1210 #define ID_FILE_EXIT 40000 #define ID_DEBUG_SAVEMAPFILE 40001 @@ -346,6 +353,7 @@ #define IDC_DISASM_FMT_INT 40233 #define IDC_DISASM_FMT_FLOAT 40234 #define IDC_DISASM_FMT_STR 40235 +#define ID_DISASM_EDITSYMBOLS 40236 // Dummy option to let the buffered rendering hotkey cycle through all the options. diff --git a/android/jni/Android.mk b/android/jni/Android.mk index bfb6874e63..494a4d6316 100644 --- a/android/jni/Android.mk +++ b/android/jni/Android.mk @@ -79,7 +79,10 @@ SPIRV_CROSS_FILES := \ $(SRC)/ext/SPIRV-Cross/spirv_glsl.cpp \ $(SRC)/ext/SPIRV-Cross/spirv_parser.cpp \ $(SRC)/ext/SPIRV-Cross/spirv_cross_parsed_ir.cpp - + +NAETT_FILES := \ + ${SRC}/ext/naett/naett.c + RCHEEVOS_FILES := \ ${SRC}/ext/rcheevos/src/rapi/rc_api_common.c \ ${SRC}/ext/rcheevos/src/rapi/rc_api_editor.c \ @@ -178,6 +181,7 @@ EXEC_AND_LIB_FILES := \ $(VMA_FILES) \ $(SPIRV_CROSS_FILES) \ $(RCHEEVOS_FILES) \ + $(NAETT_FILES) \ $(EXT_FILES) \ $(NATIVE_FILES) \ $(SRC)/Common/Buffer.cpp \ @@ -238,6 +242,8 @@ EXEC_AND_LIB_FILES := \ $(SRC)/Common/Math/lin/matrix4x4.cpp.arm \ $(SRC)/Common/Net/HTTPClient.cpp \ $(SRC)/Common/Net/HTTPHeaders.cpp \ + $(SRC)/Common/Net/HTTPRequest.cpp \ + $(SRC)/Common/Net/HTTPNaettRequest.cpp \ $(SRC)/Common/Net/HTTPServer.cpp \ $(SRC)/Common/Net/NetBuffer.cpp \ $(SRC)/Common/Net/Resolve.cpp \ @@ -384,6 +390,7 @@ EXEC_AND_LIB_FILES := \ $(SRC)/Core/MIPS/MIPSVFPUFallbacks.cpp.arm \ $(SRC)/Core/MIPS/MIPSCodeUtils.cpp.arm \ $(SRC)/Core/MIPS/MIPSDebugInterface.cpp \ + $(SRC)/Core/MIPS/IR/IRAnalysis.cpp \ $(SRC)/Core/MIPS/IR/IRFrontend.cpp \ $(SRC)/Core/MIPS/IR/IRJit.cpp \ $(SRC)/Core/MIPS/IR/IRCompALU.cpp \ diff --git a/android/jni/AndroidVulkanContext.cpp b/android/jni/AndroidVulkanContext.cpp index d12832a7fe..18ef569a12 100644 --- a/android/jni/AndroidVulkanContext.cpp +++ b/android/jni/AndroidVulkanContext.cpp @@ -113,7 +113,7 @@ bool AndroidVulkanContext::InitFromRenderThread(ANativeWindow *wnd, int desiredB bool success = true; if (g_Vulkan->InitSwapchain()) { - draw_ = Draw::T3DCreateVulkanContext(g_Vulkan); + draw_ = Draw::T3DCreateVulkanContext(g_Vulkan, g_Config.bRenderMultiThreading); SetGPUBackend(GPUBackend::VULKAN); success = draw_->CreatePresets(); // Doesn't fail, we ship the compiler. _assert_msg_(success, "Failed to compile preset shaders"); diff --git a/android/jni/app-android.cpp b/android/jni/app-android.cpp index 6574ef63c1..61c737396c 100644 --- a/android/jni/app-android.cpp +++ b/android/jni/app-android.cpp @@ -158,7 +158,8 @@ static int desiredBackbufferSizeX; static int desiredBackbufferSizeY; // Cache the class loader so we can use it from native threads. Required for TextAndroid. -static JavaVM* gJvm = nullptr; +extern JavaVM *gJvm; + static jobject gClassLoader; static jmethodID gFindClassMethod; @@ -412,6 +413,8 @@ std::string System_GetProperty(SystemProperty prop) { return mogaVersion; case SYSPROP_BOARDNAME: return boardName; + case SYSPROP_BUILD_VERSION: + return PPSSPP_GIT_VERSION; default: return ""; } @@ -532,6 +535,10 @@ bool System_GetPropertyBool(SystemProperty prop) { } case SYSPROP_HAS_KEYBOARD: return deviceType != DEVICE_TYPE_VR; +#ifndef HTTPS_NOT_AVAILABLE + case SYSPROP_SUPPORTS_HTTPS: + return true; +#endif default: return false; } diff --git a/assets/lang/ar_AE.ini b/assets/lang/ar_AE.ini index 118fa5c8d6..c82e09f2c8 100644 --- a/assets/lang/ar_AE.ini +++ b/assets/lang/ar_AE.ini @@ -14,12 +14,15 @@ Account = Account Achievement Unlocked = Achievement Unlocked Achievements = Achievements Achievements are disabled = Achievements are disabled +Can't log in to RetroAchievements right now = Can't log in to RetroAchievements right now Challenge Mode = Challenge Mode Challenge Mode (no savestates) = Challenge Mode (no savestates) +Contacting RetroAchievements server... = Contacting RetroAchievements server... Customize = Customize Earned = You have earned %d of %d achievements, and %d of %d points Encore Mode = Encore Mode How to use RetroAchievements = How to use RetroAchievements +In Encore mode - listings may be wrong below = In Encore mode - listings may be wrong below Leaderboard score submission = Leaderboard score submission Leaderboard submission is enabled = Leaderboard submission is enabled Leaderboards = Leaderboards @@ -96,6 +99,7 @@ Disable diagonal input = Disable diagonal input Double tap = Double tap Enable gesture control = Enable gesture control Enable standard shortcut keys = Enable standard shortcut keys +frames = frames Gesture = Gesture Gesture mapping = Gesture mapping Glowing borders = Glowing borders @@ -124,6 +128,7 @@ OnScreen = ‎تحكم لمس الشاشة Portrait = ‎طولية Portrait Reversed = ‎طولية معكوسة PSP Action Buttons = ‎PSP أزرار الأفعال الخاصة بـ +Rapid fire interval = Rapid fire interval Raw input = Raw input Repeat mode = Repeat mode Reset to defaults = Reset to defaults @@ -318,10 +323,14 @@ VFPU = VFPU * PSP res = ‎* PSP حجم Active = Active Back = ‎رجوع +Bottom Center = Bottom center +Bottom Left = Bottom left +Bottom Right = Bottom right Cancel = ‎إلغاء Center = ‎المركز -ChangingGPUBackends = ‎تغيير واجهة معالجة من كرت الشاشة يتطلب إعادة التشغيل. إعادة تشغيل الأن؟ -ChangingInflightFrames = Changing graphics command buffering requires PPSSPP to restart. Restart now? +Center Left = Center left +Center Right = Center right +Changing this setting requires PPSSPP to restart. = Changing this setting requires PPSSPP to restart. Channel: = Channel: Choose PPSSPP save folder = ‎إختر PPSSPP مجلد الحفظ الخاص بـ Confirm Overwrite = ‎هل تريد الكتابة علي هذه البيانات? @@ -369,6 +378,7 @@ Move Up = Move Up Network Connection = ‎إتصال الشبكة NEW DATA = ‎بيانات جديدة No = ‎لا +None = None ObtainingIP = Obtaining IP address.\nPlease wait... OK = ‎حسناً Old savedata detected = ‎بيانات حفظ قديمة قد إكتشفت @@ -377,6 +387,7 @@ Password = Password Remove = Remove Reset = ‎إستعادة Resize = ‎تغيير الحجم +Restart = Restart Retry = ‎حاول مجدداً Save = ‎حفظ Save completed = ‎الحفظ إكتمل. @@ -396,6 +407,9 @@ Supported = Supported There is no data = ‎ليس هناك بيانات. Toggle All = ‎تبديل الكل Toggle List = Toggle list +Top Center = Top center +Top Left = Top left +Top Right = Top right Unsupported = Unsupported Username = Username When you save, it will load on a PSP, but not an older PPSSPP = When you save, it will load on a PSP, but not an older PPSSPP @@ -612,6 +626,7 @@ Stereo rendering = Stereo rendering Stretch = ‎المط Texture Filter = ‎منقي الرسوم Texture Filtering = ‎مفلتر الرسوم +Texture replacement pack activated = Texture replacement pack activated Texture Scaling = ‎تكبير الرسوم Texture Shader = Texture shader Turn off Hardware Tessellation - unsupported = Turn off "hardware tessellation": unsupported @@ -797,11 +812,6 @@ AdHoc Server = Ad hoc server AdhocServer Failed to Bind Port = Ad hoc server failed to bind port AM: Data from Unknown Port = AM: Data from Unknown Port Auto = Auto -Bottom Center = Bottom center -Bottom Left = Bottom left -Bottom Right = Bottom right -Center Left = Center left -Center Right = Center right Change Mac Address = ‎MAC تغيير عنوان الـ Change proAdhocServer Address = Change PRO ad hoc server IP address (localhost = multiple instances) ChangeMacSaveConfirm = Generate a new MAC address? @@ -835,7 +845,6 @@ Invalid IP or hostname = Invalid IP or hostname Minimum Timeout = Minimum timeout (override in ms, 0 = default) Misc = Miscellaneous (default = PSP compatibility) Network Initialized = ‎الشبكة تُطلق -None = None Please change your Port Offset = Please change your port offset Port offset = Port offset (0 = PSP compatibility) Open PPSSPP Multiplayer Wiki Page = Open PPSSPP Multiplayer Wiki Page @@ -846,11 +855,9 @@ Quick Chat 3 = Quick chat 3 Quick Chat 4 = Quick chat 4 Quick Chat 5 = Quick chat 5 QuickChat = Quick chat +Randomize = Randomize Send = Send Send Discord Presence information = Send Discord "Rich Presence" information -Top Center = Top center -Top Left = Top left -Top Right = Top right Unable to find UPnP device = Unable to find UPnP device UPnP (port-forwarding) = UPnP (port forwarding) UPnP need to be reinitialized = UPnP need to be reinitialized @@ -860,6 +867,23 @@ Validating address... = Validating address... WLAN Channel = WLAN channel You're in Offline Mode, go to lobby or online hall = You're in offline mode, go to lobby or online hall +[PSPSettings] +Auto = Auto +Chinese (simplified) = Chinese (simplified) +Chinese (traditional) = Chinese (traditional) +Dutch = Dutch +English = English +French = French +Game language = Game language +German = German +Italian = Italian +Japanese = Japanese +Korean = Korean +Games often don't support all languages = Games often don't support all languages +Portuguese = Portuguese +Russian = Russian +Spanish = Spanish + [Pause] Cheats = ‎الشفرات Continue = ‎إكمال diff --git a/assets/lang/az_AZ.ini b/assets/lang/az_AZ.ini index a6168a7879..37ac303f61 100644 --- a/assets/lang/az_AZ.ini +++ b/assets/lang/az_AZ.ini @@ -6,12 +6,15 @@ Account = Account Achievement Unlocked = Achievement Unlocked Achievements = Achievements Achievements are disabled = Achievements are disabled +Can't log in to RetroAchievements right now = Can't log in to RetroAchievements right now Challenge Mode = Challenge Mode Challenge Mode (no savestates) = Challenge Mode (no savestates) +Contacting RetroAchievements server... = Contacting RetroAchievements server... Customize = Customize Earned = You have earned %d of %d achievements, and %d of %d points Encore Mode = Encore Mode How to use RetroAchievements = How to use RetroAchievements +In Encore mode - listings may be wrong below = In Encore mode - listings may be wrong below Leaderboard score submission = Leaderboard score submission Leaderboard submission is enabled = Leaderboard submission is enabled Leaderboards = Leaderboards @@ -88,6 +91,7 @@ Disable diagonal input = Disable diagonal input Double tap = Double tap Enable gesture control = Enable gesture control Enable standard shortcut keys = Enable standard shortcut keys +frames = frames Gesture = Gesture Gesture mapping = Gesture mapping Glowing borders = Glowing borders @@ -116,6 +120,7 @@ OnScreen = Ekran Kontrolları Açıq Portrait = Portrait Portrait Reversed = Portrait reversed PSP Action Buttons = PSP action buttons +Rapid fire interval = Rapid fire interval Raw input = Raw input Repeat mode = Repeat mode Reset to defaults = Reset to defaults @@ -310,10 +315,14 @@ VFPU = VFPU * PSP res = * PSP res Active = Active Back = Geri +Bottom Center = Bottom center +Bottom Left = Bottom left +Bottom Right = Bottom right Cancel = Cancel Center = Center -ChangingGPUBackends = Changing GPU backends requires PPSSPP to restart. Restart now? -ChangingInflightFrames = Changing graphics command buffering requires PPSSPP to restart. Restart now? +Center Left = Center left +Center Right = Center right +Changing this setting requires PPSSPP to restart. = Changing this setting requires PPSSPP to restart. Channel: = Channel: Choose PPSSPP save folder = Choose PPSSPP save folder Confirm Overwrite = Do you want to overwrite the data? @@ -361,6 +370,7 @@ Move Up = Move Up Network Connection = Network Connection NEW DATA = NEW DATA No = Xeyir +None = None ObtainingIP = Obtaining IP address.\nPlease wait... OK = OK Old savedata detected = Old savedata detected @@ -369,6 +379,7 @@ Password = Password Remove = Remove Reset = Reset Resize = Resize +Restart = Restart Retry = Retry Save = Save Save completed = Qeyd edildi @@ -388,6 +399,9 @@ Supported = Supported There is no data = Veri yoxdur Toggle All = Toggle all Toggle List = Toggle list +Top Center = Top center +Top Left = Top left +Top Right = Top right Unsupported = Unsupported Username = Username When you save, it will load on a PSP, but not an older PPSSPP = When you save, it will load on a PSP, but not an older PPSSPP @@ -604,6 +618,7 @@ Stereo rendering = Stereo rendering Stretch = Stretch Texture Filter = Texture filtering Texture Filtering = Texture filtering +Texture replacement pack activated = Texture replacement pack activated Texture Scaling = Texture scaling Texture Shader = Texture shader Turn off Hardware Tessellation - unsupported = Turn off "hardware tessellation": unsupported @@ -789,11 +804,6 @@ AdHoc Server = Ad hoc server AdhocServer Failed to Bind Port = Ad hoc server failed to bind port AM: Data from Unknown Port = AM: Data from Unknown Port Auto = Auto -Bottom Center = Bottom center -Bottom Left = Bottom left -Bottom Right = Bottom right -Center Left = Center left -Center Right = Center right Change Mac Address = Change MAC address Change proAdhocServer Address = Change PRO ad hoc server IP address (localhost = multiple instances) ChangeMacSaveConfirm = Generate a new MAC address? @@ -827,7 +837,6 @@ Invalid IP or hostname = Invalid IP or hostname Minimum Timeout = Minimum timeout (override in ms, 0 = default) Misc = Miscellaneous (default = PSP compatibility) Network Initialized = Network initialized -None = None Please change your Port Offset = Please change your port offset Port offset = Port offset(0 = PSP compatibility) Open PPSSPP Multiplayer Wiki Page = Open PPSSPP Multiplayer Wiki Page @@ -838,11 +847,9 @@ Quick Chat 3 = Quick chat 3 Quick Chat 4 = Quick chat 4 Quick Chat 5 = Quick chat 5 QuickChat = Quick chat +Randomize = Randomize Send = Send Send Discord Presence information = Send Discord "Rich Presence" information -Top Center = Top center -Top Left = Top left -Top Right = Top right Unable to find UPnP device = Unable to find UPnP device UPnP (port-forwarding) = UPnP (port forwarding) UPnP need to be reinitialized = UPnP need to be reinitialized @@ -852,6 +859,23 @@ Validating address... = Validating address... WLAN Channel = WLAN channel You're in Offline Mode, go to lobby or online hall = You're in offline mode, go to lobby or online hall +[PSPSettings] +Auto = Auto +Chinese (simplified) = Chinese (simplified) +Chinese (traditional) = Chinese (traditional) +Dutch = Dutch +English = English +French = French +Game language = Game language +German = German +Italian = Italian +Japanese = Japanese +Korean = Korean +Games often don't support all languages = Games often don't support all languages +Portuguese = Portuguese +Russian = Russian +Spanish = Spanish + [Pause] Cheats = Cheats Continue = Dəvam diff --git a/assets/lang/bg_BG.ini b/assets/lang/bg_BG.ini index ed28402526..5ad190a2ab 100644 --- a/assets/lang/bg_BG.ini +++ b/assets/lang/bg_BG.ini @@ -6,12 +6,15 @@ Account = Account Achievement Unlocked = Achievement Unlocked Achievements = Achievements Achievements are disabled = Achievements are disabled +Can't log in to RetroAchievements right now = Can't log in to RetroAchievements right now Challenge Mode = Challenge Mode Challenge Mode (no savestates) = Challenge Mode (no savestates) +Contacting RetroAchievements server... = Contacting RetroAchievements server... Customize = Customize Earned = You have earned %d of %d achievements, and %d of %d points Encore Mode = Encore Mode How to use RetroAchievements = How to use RetroAchievements +In Encore mode - listings may be wrong below = In Encore mode - listings may be wrong below Leaderboard score submission = Leaderboard score submission Leaderboard submission is enabled = Leaderboard submission is enabled Leaderboards = Leaderboards @@ -88,6 +91,7 @@ Disable diagonal input = Disable diagonal input Double tap = Double tap Enable gesture control = Enable gesture control Enable standard shortcut keys = Enable standard shortcut keys +frames = frames Gesture = Gesture Gesture mapping = Gesture mapping Glowing borders = Glowing borders @@ -116,6 +120,7 @@ OnScreen = Управление с докосване на екрана Portrait = Портрет Portrait Reversed = Обърнат портрет PSP Action Buttons = PSP action buttons +Rapid fire interval = Rapid fire interval Raw input = Raw input Repeat mode = Repeat mode Reset to defaults = Reset to defaults @@ -310,10 +315,14 @@ VFPU = VFPU * PSP res = * PSP res Active = Active Back = Назад +Bottom Center = Bottom center +Bottom Left = Bottom left +Bottom Right = Bottom right Cancel = Отмени Center = Center -ChangingGPUBackends = Changing GPU backends requires PPSSPP to restart. Restart now? -ChangingInflightFrames = Changing graphics command buffering requires PPSSPP to restart. Restart now? +Center Left = Center left +Center Right = Center right +Changing this setting requires PPSSPP to restart. = Changing this setting requires PPSSPP to restart. Channel: = Channel: Choose PPSSPP save folder = Choose PPSSPP save folder Confirm Overwrite = Искате ли да презапишете данните? @@ -361,6 +370,7 @@ Move Up = Move Up Network Connection = Мрежова връзка NEW DATA = Нови данни No = Не +None = None ObtainingIP = Obtaining IP address.\nPlease wait... OK = OK Old savedata detected = Old savedata detected @@ -369,6 +379,7 @@ Password = Password Remove = Remove Reset = Reset Resize = Преоразмеряване +Restart = Restart Retry = Нов опит Save = Запази Save completed = Съхраняването завършено. @@ -388,6 +399,9 @@ Supported = Supported There is no data = Няма данни. Toggle All = Toggle all Toggle List = Toggle list +Top Center = Top center +Top Left = Top left +Top Right = Top right Unsupported = Unsupported Username = Username When you save, it will load on a PSP, but not an older PPSSPP = When you save, it will load on a PSP, but not an older PPSSPP @@ -604,6 +618,7 @@ Stereo rendering = Stereo rendering Stretch = Stretch Texture Filter = Текстурно филтриране Texture Filtering = Текстурно филтриране +Texture replacement pack activated = Texture replacement pack activated Texture Scaling = Текстурно мащабиране Texture Shader = Texture shader Turn off Hardware Tessellation - unsupported = Turn off "hardware tessellation": unsupported @@ -789,11 +804,6 @@ AdHoc Server = Ad hoc server AdhocServer Failed to Bind Port = Ad hoc server failed to bind port AM: Data from Unknown Port = AM: Data from Unknown Port Auto = Auto -Bottom Center = Bottom center -Bottom Left = Bottom left -Bottom Right = Bottom right -Center Left = Center left -Center Right = Center right Change Mac Address = Change MAC address Change proAdhocServer Address = Change PRO ad hoc server IP address (localhost = multiple instances) ChangeMacSaveConfirm = Generate a new MAC address? @@ -827,7 +837,6 @@ Invalid IP or hostname = Invalid IP or hostname Minimum Timeout = Minimum timeout (override in ms, 0 = default) Misc = Miscellaneous (default = PSP compatibility) Network Initialized = Network initialized -None = None Please change your Port Offset = Please change your port offset Port offset = Port offset(0 = PSP compatibility) Open PPSSPP Multiplayer Wiki Page = Open PPSSPP Multiplayer Wiki Page @@ -838,11 +847,9 @@ Quick Chat 3 = Quick chat 3 Quick Chat 4 = Quick chat 4 Quick Chat 5 = Quick chat 5 QuickChat = Quick chat +Randomize = Randomize Send = Send Send Discord Presence information = Send Discord "Rich Presence" information -Top Center = Top center -Top Left = Top left -Top Right = Top right Unable to find UPnP device = Unable to find UPnP device UPnP (port-forwarding) = UPnP (port forwarding) UPnP need to be reinitialized = UPnP need to be reinitialized @@ -852,6 +859,23 @@ Validating address... = Validating address... WLAN Channel = WLAN channel You're in Offline Mode, go to lobby or online hall = You're in offline mode, go to lobby or online hall +[PSPSettings] +Auto = Auto +Chinese (simplified) = Chinese (simplified) +Chinese (traditional) = Chinese (traditional) +Dutch = Dutch +English = English +French = French +Game language = Game language +German = German +Italian = Italian +Japanese = Japanese +Korean = Korean +Games often don't support all languages = Games often don't support all languages +Portuguese = Portuguese +Russian = Russian +Spanish = Spanish + [Pause] Cheats = чийтове Continue = Продължи diff --git a/assets/lang/ca_ES.ini b/assets/lang/ca_ES.ini index 3624a61b66..f95e5bbbae 100644 --- a/assets/lang/ca_ES.ini +++ b/assets/lang/ca_ES.ini @@ -6,12 +6,15 @@ Account = Account Achievement Unlocked = Achievement Unlocked Achievements = Achievements Achievements are disabled = Achievements are disabled +Can't log in to RetroAchievements right now = Can't log in to RetroAchievements right now Challenge Mode = Challenge Mode Challenge Mode (no savestates) = Challenge Mode (no savestates) +Contacting RetroAchievements server... = Contacting RetroAchievements server... Customize = Customize Earned = You have earned %d of %d achievements, and %d of %d points Encore Mode = Encore Mode How to use RetroAchievements = How to use RetroAchievements +In Encore mode - listings may be wrong below = In Encore mode - listings may be wrong below Leaderboard score submission = Leaderboard score submission Leaderboard submission is enabled = Leaderboard submission is enabled Leaderboards = Leaderboards @@ -88,6 +91,7 @@ Disable diagonal input = Desactivar mov. diagonal Double tap = Doble toc Enable gesture control = Habilitar gestos Enable standard shortcut keys = Habilitar les tecles de drecera +frames = frames Gesture = Gestos Gesture mapping = Configuració de gestos Glowing borders = Vores brillants @@ -116,6 +120,7 @@ OnScreen = Controls tàctils a la pantalla Portrait = Vertical Portrait Reversed = Vertical invertit PSP Action Buttons = Botons d'acció +Rapid fire interval = Rapid fire interval Raw input = Raw input Repeat mode = Mode repetició Reset to defaults = Restaurar paràmetres @@ -310,10 +315,14 @@ VFPU = VFPU * PSP res = * resolució PSP Active = Actiu Back = Enrere +Bottom Center = Bottom center +Bottom Left = Bottom left +Bottom Right = Bottom right Cancel = Cancel·lar Center = Centre -ChangingGPUBackends = Canviar el motor gràfic requereix reiniciar PPSSPP.\nReiniciar ara? -ChangingInflightFrames = Canvar el búfer de comanda de gràfics requereix reiniciar PPSSPP.\nReiniciar ara? +Center Left = Center left +Center Right = Center right +Changing this setting requires PPSSPP to restart. = Canvar aquesta configuració requereix reiniciar PPSSPP.\nReiniciar ara? Channel: = Canal: Choose PPSSPP save folder = Tria la carpeta de guardat de PPSSPP. Confirm Overwrite = Vols sobreescriure les dades? @@ -361,6 +370,7 @@ Move Up = Pujar Network Connection = Connexión de xarxa NEW DATA = NOVES DADES DE PARTIDA No = No +None = None ObtainingIP = Obtenint direcció IP.\nPer favor espera... OK = Acceptar Old savedata detected = S'han detectar dades del joc antigues. @@ -369,6 +379,7 @@ Password = Password Remove = Borrar Reset = Reiniciar Resize = Canviar la mida +Restart = Restart Retry = Reintentar Save = Guardar Save completed = Guardat completat @@ -388,6 +399,9 @@ Supported = Suportat There is no data = No hi han dades Toggle All = Canviar tot Toggle List = Veure llista +Top Center = Top center +Top Left = Top left +Top Right = Top right Unsupported = No suportat Username = Username When you save, it will load on a PSP, but not an older PPSSPP = Quant guardis, la partida carregarà a una PSP, però no en versions antigues de PPSSPP. @@ -604,6 +618,7 @@ Stereo rendering = Renderitzat estèreo Stretch = Estirar Texture Filter = Filtre de textures Texture Filtering = Filtrat de textures +Texture replacement pack activated = Texture replacement pack activated Texture Scaling = Escalat de textures Texture Shader = Shader de textura Turn off Hardware Tessellation - unsupported = Desactivant tessel·lat per maquinari: no suportat. @@ -789,11 +804,6 @@ AdHoc Server = Ad hoc server AdhocServer Failed to Bind Port = Ad hoc server failed to bind port AM: Data from Unknown Port = AM: Data from Unknown Port Auto = Auto -Bottom Center = Bottom center -Bottom Left = Bottom left -Bottom Right = Bottom right -Center Left = Center left -Center Right = Center right Change Mac Address = Change MAC address Change proAdhocServer Address = Change PRO ad hoc server IP address (localhost = multiple instances) ChangeMacSaveConfirm = Generate a new MAC address? @@ -827,7 +837,6 @@ Invalid IP or hostname = Invalid IP or hostname Minimum Timeout = Minimum timeout (override in ms, 0 = default) Misc = Miscellaneous (default = PSP compatibility) Network Initialized = Network initialized -None = None Please change your Port Offset = Please change your port offset Port offset = Port offset(0 = PSP compatibility) Open PPSSPP Multiplayer Wiki Page = Open PPSSPP Multiplayer Wiki Page @@ -838,11 +847,9 @@ Quick Chat 3 = Quick chat 3 Quick Chat 4 = Quick chat 4 Quick Chat 5 = Quick chat 5 QuickChat = Quick chat +Randomize = Randomize Send = Send Send Discord Presence information = Send Discord "Rich Presence" information -Top Center = Top center -Top Left = Top left -Top Right = Top right Unable to find UPnP device = Unable to find UPnP device UPnP (port-forwarding) = UPnP (port forwarding) UPnP need to be reinitialized = UPnP need to be reinitialized @@ -852,6 +859,23 @@ Validating address... = Validating address... WLAN Channel = WLAN channel You're in Offline Mode, go to lobby or online hall = You're in offline mode, go to lobby or online hall +[PSPSettings] +Auto = Auto +Chinese (simplified) = Chinese (simplified) +Chinese (traditional) = Chinese (traditional) +Dutch = Dutch +English = English +French = French +Game language = Game language +German = German +Italian = Italian +Japanese = Japanese +Korean = Korean +Games often don't support all languages = Games often don't support all languages +Portuguese = Portuguese +Russian = Russian +Spanish = Spanish + [Pause] Cheats = Trucs Continue = Continuar diff --git a/assets/lang/cz_CZ.ini b/assets/lang/cz_CZ.ini index e919023cf5..64f72f9cdf 100644 --- a/assets/lang/cz_CZ.ini +++ b/assets/lang/cz_CZ.ini @@ -6,12 +6,15 @@ Account = Account Achievement Unlocked = Achievement Unlocked Achievements = Achievements Achievements are disabled = Achievements are disabled +Can't log in to RetroAchievements right now = Can't log in to RetroAchievements right now Challenge Mode = Challenge Mode Challenge Mode (no savestates) = Challenge Mode (no savestates) +Contacting RetroAchievements server... = Contacting RetroAchievements server... Customize = Customize Earned = You have earned %d of %d achievements, and %d of %d points Encore Mode = Encore Mode How to use RetroAchievements = How to use RetroAchievements +In Encore mode - listings may be wrong below = In Encore mode - listings may be wrong below Leaderboard score submission = Leaderboard score submission Leaderboard submission is enabled = Leaderboard submission is enabled Leaderboards = Leaderboards @@ -88,6 +91,7 @@ Disable diagonal input = Disable diagonal input Double tap = Double tap Enable gesture control = Enable gesture control Enable standard shortcut keys = Enable standard shortcut keys +frames = frames Gesture = Gesture Gesture mapping = Gesture mapping Glowing borders = Glowing borders @@ -116,6 +120,7 @@ OnScreen = Dotykové ovládání na obrazovce Portrait = Na výšku Portrait Reversed = Na výšku obráceně PSP Action Buttons = Tlačítka činností PSP +Rapid fire interval = Rapid fire interval Raw input = Raw input Repeat mode = Repeat mode Reset to defaults = Reset to defaults @@ -310,10 +315,14 @@ VFPU = VFPU * PSP res = * PSP rozlišení Active = Active Back = Zpět +Bottom Center = Bottom center +Bottom Left = Bottom left +Bottom Right = Bottom right Cancel = Zrušit Center = Na střed -ChangingGPUBackends = Změna grafického jádra vyžaduje restart PPSSPP. Chcete tak učinit nyní?? -ChangingInflightFrames = Changing graphics command buffering requires PPSSPP to restart. Restart now? +Center Left = Center left +Center Right = Center right +Changing this setting requires PPSSPP to restart. = Changing this setting requires PPSSPP to restart. Channel: = Channel: Choose PPSSPP save folder = Zvolit složku pro ukládání dat PPSSPP Confirm Overwrite = Chcete tato data přepsat?? @@ -361,6 +370,7 @@ Move Up = Move Up Network Connection = Síťové připojení NEW DATA = NOVÁ DATA No = Ne +None = None ObtainingIP = Obtaining IP address.\nPlease wait... OK = OK Old savedata detected = Zjištěna stará uložená data @@ -369,6 +379,7 @@ Password = Password Remove = Remove Reset = Resetovat Resize = Velikost +Restart = Restart Retry = Zkusit znovu Save = Uložit Save completed = Ukládání dokončeno. @@ -388,6 +399,9 @@ Supported = Supported There is no data = Žádná data k dispozici. Toggle All = Přepnout vše Toggle List = Toggle list +Top Center = Top center +Top Left = Top left +Top Right = Top right Unsupported = Unsupported Username = Username When you save, it will load on a PSP, but not an older PPSSPP = Pokud uložíte, můžete hru načíst na PSP, ale ne na starší verzi PPSSPP @@ -604,6 +618,7 @@ Stereo rendering = Stereo rendering Stretch = Roztažení Texture Filter = Filtr textur Texture Filtering = Filtrování textur +Texture replacement pack activated = Texture replacement pack activated Texture Scaling = Změna velikosti textur Texture Shader = Texture shader Turn off Hardware Tessellation - unsupported = Turn off "hardware tessellation": unsupported @@ -789,11 +804,6 @@ AdHoc Server = Ad hoc server AdhocServer Failed to Bind Port = Ad hoc server failed to bind port AM: Data from Unknown Port = AM: Data from Unknown Port Auto = Auto -Bottom Center = Bottom center -Bottom Left = Bottom left -Bottom Right = Bottom right -Center Left = Center left -Center Right = Center right Change Mac Address = Změnit MAC adresu Change proAdhocServer Address = Změnit IP adresu serveru PRO ad hoc (localhost = multiple instances) ChangeMacSaveConfirm = Generate a new MAC address? @@ -827,7 +837,6 @@ Invalid IP or hostname = Invalid IP or hostname Minimum Timeout = Minimum timeout (override in ms, 0 = default) Misc = Miscellaneous (default = PSP compatibility) Network Initialized = Síť zavedena -None = None Please change your Port Offset = Please change your port offset Port offset = Odchylka portu (0 = Kompatibilita s PSP) Open PPSSPP Multiplayer Wiki Page = Open PPSSPP Multiplayer Wiki Page @@ -838,11 +847,9 @@ Quick Chat 3 = Quick chat 3 Quick Chat 4 = Quick chat 4 Quick Chat 5 = Quick chat 5 QuickChat = Quick chat +Randomize = Randomize Send = Send Send Discord Presence information = Send Discord "Rich Presence" information -Top Center = Top center -Top Left = Top left -Top Right = Top right Unable to find UPnP device = Unable to find UPnP device UPnP (port-forwarding) = UPnP (port forwarding) UPnP need to be reinitialized = UPnP need to be reinitialized @@ -852,6 +859,23 @@ Validating address... = Validating address... WLAN Channel = WLAN channel You're in Offline Mode, go to lobby or online hall = You're in offline mode, go to lobby or online hall +[PSPSettings] +Auto = Auto +Chinese (simplified) = Chinese (simplified) +Chinese (traditional) = Chinese (traditional) +Dutch = Dutch +English = English +French = French +Game language = Game language +German = German +Italian = Italian +Japanese = Japanese +Korean = Korean +Games often don't support all languages = Games often don't support all languages +Portuguese = Portuguese +Russian = Russian +Spanish = Spanish + [Pause] Cheats = Cheaty Continue = Pokračovat diff --git a/assets/lang/da_DK.ini b/assets/lang/da_DK.ini index dd7824e918..33587c9968 100644 --- a/assets/lang/da_DK.ini +++ b/assets/lang/da_DK.ini @@ -6,12 +6,15 @@ Account = Account Achievement Unlocked = Achievement Unlocked Achievements = Achievements Achievements are disabled = Achievements are disabled +Can't log in to RetroAchievements right now = Can't log in to RetroAchievements right now Challenge Mode = Challenge Mode Challenge Mode (no savestates) = Challenge Mode (no savestates) +Contacting RetroAchievements server... = Contacting RetroAchievements server... Customize = Customize Earned = You have earned %d of %d achievements, and %d of %d points Encore Mode = Encore Mode How to use RetroAchievements = How to use RetroAchievements +In Encore mode - listings may be wrong below = In Encore mode - listings may be wrong below Leaderboard score submission = Leaderboard score submission Leaderboard submission is enabled = Leaderboard submission is enabled Leaderboards = Leaderboards @@ -88,6 +91,7 @@ Disable diagonal input = Disable diagonal input Double tap = Double tap Enable gesture control = Enable gesture control Enable standard shortcut keys = Enable standard shortcut keys +frames = frames Gesture = Gesture Gesture mapping = Gesture mapping Glowing borders = Glowing borders @@ -116,6 +120,7 @@ OnScreen = Touch billedskærmsknapper Portrait = Portræt Portrait Reversed = Omvendt portræt PSP Action Buttons = PSP action knapper +Rapid fire interval = Rapid fire interval Raw input = Raw input Repeat mode = Repeat mode Reset to defaults = Reset to defaults @@ -310,10 +315,14 @@ VFPU = VFPU * PSP res = * PSP res Active = Active Back = Tilbage +Bottom Center = Bottom center +Bottom Left = Bottom left +Bottom Right = Bottom right Cancel = Annuler Center = Center -ChangingGPUBackends = Det er nødvendigt at genstarte PPSSPP for at ændre GPU backend. Genstart nu? -ChangingInflightFrames = Changing graphics command buffering requires PPSSPP to restart. Restart now? +Center Left = Center left +Center Right = Center right +Changing this setting requires PPSSPP to restart. = Changing this setting requires PPSSPP to restart. Channel: = Channel: Choose PPSSPP save folder = Vælg hvilken folder PPSSPP gemmer i Confirm Overwrite = Vil du overskrive dette data? @@ -361,6 +370,7 @@ Move Up = Move Up Network Connection = Netværksforbindelse NEW DATA = NY DATA No = Nej +None = None ObtainingIP = Obtaining IP address.\nPlease wait... OK = OK Old savedata detected = Gamle savedata fundet @@ -369,6 +379,7 @@ Password = Password Remove = Remove Reset = Nulstil Resize = Størrelse +Restart = Restart Retry = Prøv igen Save = Gem Save completed = Gemt. @@ -388,6 +399,9 @@ Supported = Supported There is no data = Der findes ingen data. Toggle All = Skift alle Toggle List = Toggle list +Top Center = Top center +Top Left = Top left +Top Right = Top right Unsupported = Unsupported Username = Username When you save, it will load on a PSP, but not an older PPSSPP = Når du gemmer vil det kunne hentes på en PSP men ikke på en ældre PPSSPP @@ -604,6 +618,7 @@ Stereo rendering = Stereo rendering Stretch = Stretch Texture Filter = Texturfilter Texture Filtering = Texturfiltrering +Texture replacement pack activated = Texture replacement pack activated Texture Scaling = Texturskalering Texture Shader = Texture shader Turn off Hardware Tessellation - unsupported = Turn off "hardware tessellation": unsupported @@ -789,11 +804,6 @@ AdHoc Server = Ad hoc server AdhocServer Failed to Bind Port = Ad hoc server failed to bind port AM: Data from Unknown Port = AM: Data from Unknown Port Auto = Auto -Bottom Center = Bottom center -Bottom Left = Bottom left -Bottom Right = Bottom right -Center Left = Center left -Center Right = Center right Change Mac Address = Ændre MAC adressen Change proAdhocServer Address = Ændre PRO ad hoc server IP adresse (localhost = multiple instances) ChangeMacSaveConfirm = Generate a new MAC address? @@ -827,7 +837,6 @@ Invalid IP or hostname = Invalid IP or hostname Minimum Timeout = Minimum timeout (override in ms, 0 = default) Misc = Miscellaneous (default = PSP compatibility) Network Initialized = Netværk er initialiseret -None = None Please change your Port Offset = Please change your port offset Port offset = Port offset(0 = PSP kompatibilitet) Open PPSSPP Multiplayer Wiki Page = Open PPSSPP Multiplayer Wiki Page @@ -838,11 +847,9 @@ Quick Chat 3 = Quick chat 3 Quick Chat 4 = Quick chat 4 Quick Chat 5 = Quick chat 5 QuickChat = Quick chat +Randomize = Randomize Send = Send Send Discord Presence information = Send Discord "Rich Presence" information -Top Center = Top center -Top Left = Top left -Top Right = Top right Unable to find UPnP device = Unable to find UPnP device UPnP (port-forwarding) = UPnP (port forwarding) UPnP need to be reinitialized = UPnP need to be reinitialized @@ -852,6 +859,23 @@ Validating address... = Validating address... WLAN Channel = WLAN channel You're in Offline Mode, go to lobby or online hall = You're in offline mode, go to lobby or online hall +[PSPSettings] +Auto = Auto +Chinese (simplified) = Chinese (simplified) +Chinese (traditional) = Chinese (traditional) +Dutch = Dutch +English = English +French = French +Game language = Game language +German = German +Italian = Italian +Japanese = Japanese +Korean = Korean +Games often don't support all languages = Games often don't support all languages +Portuguese = Portuguese +Russian = Russian +Spanish = Spanish + [Pause] Cheats = Snyd Continue = Fortsæt diff --git a/assets/lang/de_DE.ini b/assets/lang/de_DE.ini index 49e4d31387..eb17e8081f 100644 --- a/assets/lang/de_DE.ini +++ b/assets/lang/de_DE.ini @@ -6,12 +6,15 @@ Account = Account Achievement Unlocked = Achievement Unlocked Achievements = Achievements Achievements are disabled = Achievements are disabled +Can't log in to RetroAchievements right now = Can't log in to RetroAchievements right now Challenge Mode = Challenge Mode Challenge Mode (no savestates) = Challenge Mode (no savestates) +Contacting RetroAchievements server... = Contacting RetroAchievements server... Customize = Customize Earned = You have earned %d of %d achievements, and %d of %d points Encore Mode = Encore Mode How to use RetroAchievements = How to use RetroAchievements +In Encore mode - listings may be wrong below = In Encore mode - listings may be wrong below Leaderboard score submission = Leaderboard score submission Leaderboard submission is enabled = Leaderboard submission is enabled Leaderboards = Leaderboards @@ -88,6 +91,7 @@ Disable diagonal input = Deaktiviere Diagonale Eingabe Double tap = Doppelklick Enable gesture control = Aktiviere Gestenkontrolle Enable standard shortcut keys = Aktiviere standard shortcut keys +frames = frames Gesture = Geste Gesture mapping = Gesten mapping Glowing borders = Leuchtende Ränder @@ -116,6 +120,7 @@ OnScreen = Touch Bildschirmtasten Portrait = Hochformat Portrait Reversed = Hochformat umgekehrt PSP Action Buttons = PSP Aktionstasten +Rapid fire interval = Rapid fire interval Raw input = Raw input Repeat mode = Repeat mode Reset to defaults = Reset to defaults @@ -310,10 +315,14 @@ VFPU = VFPU * PSP res = * PSP Auflösung Active = Aktiv Back = Zurück +Bottom Center = Mitte unten +Bottom Left = links unten +Bottom Right = rechts unten Cancel = Abbrechen Center = Zentriert -ChangingGPUBackends = Um das Grafik-Backend zu wechseln, muss PPSSPP neu gestartet werden. Wollen Sie jetzt neustarten? -ChangingInflightFrames = Das Ändern der Pufferung von Grafikbefehlen erfordert einen Neustart von PPSSPP. Wollen Sie jetzt neustarten? +Center Left = Mitte links +Center Right = Mitte rechts +Changing this setting requires PPSSPP to restart. = Wenn Sie diese Einstellung ändern muss PPSSPP neu gestartet werden. Channel: = Kanal: Choose PPSSPP save folder = Wähle PPSSPP Speicherpfad Confirm Overwrite = Wollen Sie diese Daten überschreiben? @@ -361,6 +370,7 @@ Move Up = Move Up Network Connection = Netzwerkverbindung NEW DATA = Neue Daten No = Nein +None = Nichts ObtainingIP = Obtaining IP address.\nPlease wait... OK = OK Old savedata detected = Alter Speicherstand entdeckt @@ -369,6 +379,7 @@ Password = Password Remove = Remove Reset = Zurücksetzen Resize = Größe ändern +Restart = Restart Retry = Wiederholen Save = Speichern Save completed = Speichern abgeschlossen @@ -388,6 +399,9 @@ Supported = Unterstützt There is no data = Keine Daten vorhanden Toggle All = Alle umschalten Toggle List = Toggle list +Top Center = Mitte oben +Top Left = Links oben +Top Right = Rechts oben Unsupported = Nicht unterstützt Username = Username When you save, it will load on a PSP, but not an older PPSSPP = Wenn Sie speichern, kann es von einer PSP gelesen werden, aber nicht von einem älteren PPSSPP @@ -604,6 +618,7 @@ Stereo rendering = Stereo rendering Stretch = Strecken Texture Filter = Texturfilter Texture Filtering = Texturfilterung +Texture replacement pack activated = Texture replacement pack activated Texture Scaling = Texturskalierung Texture Shader = Texture shader Turn off Hardware Tessellation - unsupported = "Hardware Tessellierung" ausschalten: Nicht unterstützt @@ -789,11 +804,6 @@ AdHoc Server = Ad hoc server AdhocServer Failed to Bind Port = Ad hoc server verbinden von Port fehlgeschlagen AM: Data from Unknown Port = AM: Dateien von Unbekannten Port Auto = Auto -Bottom Center = Mitte unten -Bottom Left = links unten -Bottom Right = rechts unten -Center Left = Mitte links -Center Right = Mitte rechts Change Mac Address = MAC-Adresse ändern Change proAdhocServer Address = proAdhocServer IP-Adresse ändern (localhost = mehrere Instanzen) ChangeMacSaveConfirm = Generate a new MAC address? @@ -827,7 +837,6 @@ Invalid IP or hostname = Ungültige IP oder Hostnamen Minimum Timeout = Minimum timeout (override in ms, 0 = default) Misc = Miscellaneous (default = PSP compatibility) Network Initialized = Netzwerk initialisiert -None = Nichts Please change your Port Offset = Bitte ändern sie Ihren Port-Offset Port offset = Portverschiebung (0 = PSP Kompatibilität) Open PPSSPP Multiplayer Wiki Page = PPSSPP Ad-Hoc Wiki Seite @@ -838,11 +847,9 @@ Quick Chat 3 = Quick chat 3 Quick Chat 4 = Quick chat 4 Quick Chat 5 = Quick chat 5 QuickChat = Quick chat +Randomize = Randomize Send = Senden Send Discord Presence information = Sende Discord "Rich Presence" Information -Top Center = Mitte oben -Top Left = Links oben -Top Right = Rechts oben Unable to find UPnP device = Unable to find UPnP device UPnP (port-forwarding) = UPnP (port forwarding) UPnP need to be reinitialized = UPnP need to be reinitialized @@ -852,6 +859,23 @@ Validating address... = Validiere Adresse... WLAN Channel = WLAN channel You're in Offline Mode, go to lobby or online hall = You're in offline mode, go to lobby or online hall +[PSPSettings] +Auto = Auto +Chinese (simplified) = Chinese (simplified) +Chinese (traditional) = Chinese (traditional) +Dutch = Dutch +English = English +French = French +Game language = Game language +German = German +Italian = Italian +Japanese = Japanese +Korean = Korean +Games often don't support all languages = Games often don't support all languages +Portuguese = Portuguese +Russian = Russian +Spanish = Spanish + [Pause] Cheats = Cheats Continue = Fortsetzen diff --git a/assets/lang/dr_ID.ini b/assets/lang/dr_ID.ini index f4cb54eab5..3bb3d12f38 100644 --- a/assets/lang/dr_ID.ini +++ b/assets/lang/dr_ID.ini @@ -6,12 +6,15 @@ Account = Account Achievement Unlocked = Achievement Unlocked Achievements = Achievements Achievements are disabled = Achievements are disabled +Can't log in to RetroAchievements right now = Can't log in to RetroAchievements right now Challenge Mode = Challenge Mode Challenge Mode (no savestates) = Challenge Mode (no savestates) +Contacting RetroAchievements server... = Contacting RetroAchievements server... Customize = Customize Earned = You have earned %d of %d achievements, and %d of %d points Encore Mode = Encore Mode How to use RetroAchievements = How to use RetroAchievements +In Encore mode - listings may be wrong below = In Encore mode - listings may be wrong below Leaderboard score submission = Leaderboard score submission Leaderboard submission is enabled = Leaderboard submission is enabled Leaderboards = Leaderboards @@ -88,6 +91,7 @@ Disable diagonal input = Disable diagonal input Double tap = Double tap Enable gesture control = Enable gesture control Enable standard shortcut keys = Enable standard shortcut keys +frames = frames Gesture = Gesture Gesture mapping = Gesture mapping Glowing borders = Glowing borders @@ -116,6 +120,7 @@ OnScreen = Tobolo'na jo layar sentu Portrait = Portrait Portrait Reversed = Portrait reversed PSP Action Buttons = PSP action buttons +Rapid fire interval = Rapid fire interval Raw input = Raw input Repeat mode = Repeat mode Reset to defaults = Reset to defaults @@ -310,10 +315,14 @@ VFPU = VFPU * PSP res = * PSP res Active = Active Back = Poleboko' +Bottom Center = Bottom center +Bottom Left = Bottom left +Bottom Right = Bottom right Cancel = Ah edda pole' Center = Center -ChangingGPUBackends = Changing GPU backends requires PPSSPP to restart. Restart now? -ChangingInflightFrames = Changing graphics command buffering requires PPSSPP to restart. Restart now? +Center Left = Center left +Center Right = Center right +Changing this setting requires PPSSPP to restart. = Changing this setting requires PPSSPP to restart. Channel: = Channel: Choose PPSSPP save folder = Choose PPSSPP save folder Confirm Overwrite = Eda na ciapa dipasi toppo' te' data? @@ -361,6 +370,7 @@ Move Up = Move Up Network Connection = Network Connection NEW DATA = DATA baru No = Edda mane +None = None ObtainingIP = Obtaining IP address.\nPlease wait... OK = Okemi mane Old savedata detected = Old savedata detected @@ -369,6 +379,7 @@ Password = Password Remove = Remove Reset = Reset Resize = Resize +Restart = Restart Retry = Retry Save = Save Save completed = Mangkami dianna. @@ -388,6 +399,9 @@ Supported = Supported There is no data = Ai edda data mane. Toggle All = Toggle all Toggle List = Toggle list +Top Center = Top center +Top Left = Top left +Top Right = Top right Unsupported = Unsupported Username = Username When you save, it will load on a PSP, but not an older PPSSPP = When you save, it will load on a PSP, but not an older PPSSPP @@ -604,6 +618,7 @@ Stereo rendering = Stereo rendering Stretch = Stretch Texture Filter = Panyaring Texture Texture Filtering = Saring i Texture +Texture replacement pack activated = Texture replacement pack activated Texture Scaling = Petonggoi Texture Texture Shader = Texture shader Turn off Hardware Tessellation - unsupported = Turn off "hardware tessellation": unsupported @@ -789,11 +804,6 @@ AdHoc Server = Ad hoc server AdhocServer Failed to Bind Port = Ad hoc server failed to bind port AM: Data from Unknown Port = AM: Data from Unknown Port Auto = Auto -Bottom Center = Bottom center -Bottom Left = Bottom left -Bottom Right = Bottom right -Center Left = Center left -Center Right = Center right Change Mac Address = Change MAC address Change proAdhocServer Address = Change PRO ad hoc server IP address (localhost = multiple instances) ChangeMacSaveConfirm = Generate a new MAC address? @@ -827,7 +837,6 @@ Invalid IP or hostname = Invalid IP or hostname Minimum Timeout = Minimum timeout (override in ms, 0 = default) Misc = Miscellaneous (default = PSP compatibility) Network Initialized = Network initialized -None = None Please change your Port Offset = Please change your port offset Port offset = Port offset(0 = PSP compatibility) Open PPSSPP Multiplayer Wiki Page = Open PPSSPP Multiplayer Wiki Page @@ -838,11 +847,9 @@ Quick Chat 3 = Quick chat 3 Quick Chat 4 = Quick chat 4 Quick Chat 5 = Quick chat 5 QuickChat = Quick chat +Randomize = Randomize Send = Send Send Discord Presence information = Send Discord "Rich Presence" information -Top Center = Top center -Top Left = Top left -Top Right = Top right Unable to find UPnP device = Unable to find UPnP device UPnP (port-forwarding) = UPnP (port forwarding) UPnP need to be reinitialized = UPnP need to be reinitialized @@ -852,6 +859,23 @@ Validating address... = Validating address... WLAN Channel = WLAN channel You're in Offline Mode, go to lobby or online hall = You're in offline mode, go to lobby or online hall +[PSPSettings] +Auto = Auto +Chinese (simplified) = Chinese (simplified) +Chinese (traditional) = Chinese (traditional) +Dutch = Dutch +English = English +French = French +Game language = Game language +German = German +Italian = Italian +Japanese = Japanese +Korean = Korean +Games often don't support all languages = Games often don't support all languages +Portuguese = Portuguese +Russian = Russian +Spanish = Spanish + [Pause] Cheats = Cheats Continue = Patarruh diff --git a/assets/lang/en_US.ini b/assets/lang/en_US.ini index ec4b256d5f..58a0bad5bf 100644 --- a/assets/lang/en_US.ini +++ b/assets/lang/en_US.ini @@ -30,8 +30,10 @@ Account = Account Achievement Unlocked = Achievement Unlocked Achievements = Achievements Achievements are disabled = Achievements are disabled +Can't log in to RetroAchievements right now = Can't log in to RetroAchievements right now Challenge Mode = Challenge Mode Challenge Mode (no savestates) = Challenge Mode (no savestates) +Contacting RetroAchievements server... = Contacting RetroAchievements server... Customize = Customize Earned = You have earned %d of %d achievements, and %d of %d points Encore Mode = Encore Mode @@ -113,6 +115,7 @@ Disable diagonal input = Disable diagonal input Double tap = Double tap Enable gesture control = Enable gesture control Enable standard shortcut keys = Enable standard shortcut keys +frames = frames Gesture = Gesture Gesture mapping = Gesture mapping Glowing borders = Glowing borders @@ -141,6 +144,7 @@ OnScreen = On-screen touch controls Portrait = Portrait Portrait Reversed = Portrait reversed PSP Action Buttons = PSP action buttons +Rapid fire interval = Rapid fire interval Raw input = Raw input Reset to defaults = Reset to defaults Screen Rotation = Screen rotation @@ -305,6 +309,7 @@ Log Dropped Frame Statistics = Log dropped frame statistics Log Level = Log level Log View = Log view Logging Channels = Logging channels +Multi-threaded rendering = Multi-threaded rendering Next = Next No block = No block Prev = Previous @@ -335,11 +340,15 @@ VFPU = VFPU * PSP res = * PSP res Active = Active Back = Back +Bottom Center = Bottom center +Bottom Left = Bottom left +Bottom Right = Bottom right Cancel = Cancel Center = Center -ChangingGPUBackends = Changing GPU backends requires PPSSPP to restart. Restart now? -ChangingInflightFrames = Changing graphics command buffering requires PPSSPP to restart. Restart now? +Center Left = Center left +Center Right = Center right Channel: = Channel: +Changing this setting requires PPSSPP to restart. = Changing this setting requires PPSSPP to restart. Choose PPSSPP save folder = Choose PPSSPP save folder Confirm Overwrite = Do you want to overwrite the data? Confirm Save = Do you want to save this data? @@ -386,6 +395,7 @@ Move Down = Move Down Network Connection = Network Connection NEW DATA = NEW DATA No = No +None = None ObtainingIP = Obtaining IP address.\nPlease wait... OK = OK Old savedata detected = Old savedata detected @@ -394,6 +404,7 @@ Password = Password Remove = Remove Reset = Reset Resize = Resize +Restart = Restart Retry = Retry Save = Save Save completed = Save completed. @@ -413,6 +424,9 @@ Supported = Supported There is no data = There is no data. Toggle All = Toggle all Toggle List = Toggle list +Top Center = Top center +Top Left = Top left +Top Right = Top right Unsupported = Unsupported Username = Username When you save, it will load on a PSP, but not an older PPSSPP = When you save, it will load on a PSP, but not an older PPSSPP @@ -629,6 +643,7 @@ Stereo rendering = Stereo rendering Stretch = Stretch Texture Filter = Texture filtering Texture Filtering = Texture filtering +Texture replacement pack activated = Texture replacement pack activated Texture Scaling = Texture scaling Texture Shader = Texture shader Turn off Hardware Tessellation - unsupported = Turn off "hardware tessellation": unsupported @@ -785,11 +800,6 @@ AdHoc Server = Ad hoc server AdhocServer Failed to Bind Port = Ad hoc server failed to bind port AM: Data from Unknown Port = AM: Data from Unknown Port Auto = Auto -Bottom Center = Bottom center -Bottom Left = Bottom left -Bottom Right = Bottom right -Center Left = Center left -Center Right = Center right Change Mac Address = Change MAC address ChangeMacSaveConfirm = Generate a new MAC address? ChangeMacSaveWarning = Some games verify the MAC address when loading savedata, so this may break old saves. @@ -823,7 +833,6 @@ Invalid IP or hostname = Invalid IP or hostname Minimum Timeout = Minimum timeout (override in ms, 0 = default) Misc = Miscellaneous (default = PSP compatibility) Network Initialized = Network initialized -None = None Please change your Port Offset = Please change your port offset Port offset = Port offset (0 = PSP compatibility) Open PPSSPP Multiplayer Wiki Page = Open PPSSPP Multiplayer Wiki Page @@ -834,11 +843,9 @@ Quick Chat 3 = Quick chat 3 Quick Chat 4 = Quick chat 4 Quick Chat 5 = Quick chat 5 QuickChat = Quick chat +Randomize = Randomize Send = Send Send Discord Presence information = Send Discord "Rich Presence" information -Top Center = Top center -Top Left = Top left -Top Right = Top right Unable to find UPnP device = Unable to find UPnP device UPnP (port-forwarding) = UPnP (port forwarding) UPnP need to be reinitialized = UPnP need to be reinitialized @@ -974,6 +981,23 @@ Use PSP folder at root of storage = Use PSP folder at root of storage Welcome to PPSSPP! = Welcome to PPSSPP! WhatsThis = What's this? +[PSPSettings] +Auto = Auto +Chinese (simplified) = Chinese (simplified) +Chinese (traditional) = Chinese (traditional) +Dutch = Dutch +English = English +French = French +Game language = Game language +German = German +Italian = Italian +Japanese = Japanese +Korean = Korean +Games often don't support all languages = Games often don't support all languages +Portuguese = Portuguese +Russian = Russian +Spanish = Spanish + [RemoteISO] Browse Games = Browse games Local Server Port = Local server port diff --git a/assets/lang/es_ES.ini b/assets/lang/es_ES.ini index 2fd2dc1007..a18d4d4825 100644 --- a/assets/lang/es_ES.ini +++ b/assets/lang/es_ES.ini @@ -1,39 +1,42 @@ [Achievements] -%1: Leaderboard attempt failed = %1: Leaderboard attempt failed -%1: Leaderboard attempt started = %1: Leaderboard attempt started -%d achievements, %d points = %d achievements, %d points -Account = Account -Achievement Unlocked = Achievement Unlocked -Achievements = Achievements -Achievements are disabled = Achievements are disabled -Challenge Mode = Challenge Mode -Challenge Mode (no savestates) = Challenge Mode (no savestates) -Customize = Customize -Earned = You have earned %d of %d achievements, and %d of %d points -Encore Mode = Encore Mode -How to use RetroAchievements = How to use RetroAchievements -Leaderboard score submission = Leaderboard score submission -Leaderboard submission is enabled = Leaderboard submission is enabled +%d achievements, %d points = %d logros, %d puntos +%1: Leaderboard attempt started = %1: intento para el leaderboard iniciado +%1: Leaderboard attempt failed = %1: intento para el leaderboard fallido +Account = Cuenta +Achievement Unlocked = Logro Desbloqueado +Achievements = Logros +Achievements are disabled = Los logros están desactivados +Can't log in to RetroAchievements right now = No se pudo iniciar sesión a RetroAchievements +Challenge Mode = Modo Desafío +Challenge Mode (no savestates) = Modo Desafío (Sin estados) +Contacting RetroAchievements server... = Contactando al servidor de RetroAchievements... +Customize = Customizar +Earned = Has obtenido %d de %d logros y %d de %d puntos +Encore Mode = Modo Encore +How to use RetroAchievements = Como usar RetroAchievements +In Encore mode - listings may be wrong below = En el modo Encore - las listas de abajo pueden estar equivocadas +Leaderboard score submission = Subir puntos al leaderboard +Leaderboard submission is enabled = Se ha activado la subida de puntos al leaderboard Leaderboards = Leaderboards Links = Links -Locked achievements = Locked achievements -Log bad memory accesses = Log bad memory accesses -Mastered %1 = Mastered %1 -Register on www.retroachievements.org = Register on www.retroachievements.org -RetroAchievements are not available for this game = RetroAchievements are not available for this game -RetroAchievements website = RetroAchievements website +Locked achievements = Logros bloqueados +Log bad memory accesses = Registrar malos accesos a la memoría +Mastered %1 = Completaste %1 +Register on www.retroachievements.org = Registrate en www.retroachievements.org +RetroAchievements are not available for this game = RetroAchievements no está disponible para este juego +RetroAchievements website = Página web de RetroAchievements Rich Presence = Rich Presence -Save state loaded without achievement data = Save state loaded without achievement data -Save states not available in Challenge Mode = Save states not available in Challenge Mode -Sound Effects = Sound Effects -Statistics = Statistics -Submitted %1 for %2 = Submitted %1 for %2 -Syncing achievements data... = Syncing achievements data... -Test Mode = Test Mode -This feature is not available in Challenge Mode = This feature is not available in Challenge Mode -This game has no achievements = This game has no achievements -Unlocked achievements = Unlocked achievements -Unofficial achievements = Unofficial achievements +Save states not available in Challenge Mode = Los estados no están disponibles en el modo desafío +Save state loaded without achievement data = Estado guardado sin información de los logros +Sound Effects = Efectos de sonido +Statistics = Estadísticas +Submitted %1 for %2 = Se subió %1 para %2 +Syncing achievements data... = Sincronizando información de logros... +Test Mode = Modo prueba +This feature is not available in Challenge Mode = Esta opción no está disponible en el modo desafío +This game has no achievements = Este juego no tiene logros +Unlocked achievements = Logros desbloqueados +Unofficial achievements = Logros no oficiales [Audio] Alternate speed volume = Alternar velocidad de sonido @@ -88,6 +91,7 @@ Disable diagonal input = Deshabilitar mov. diagonal Double tap = Doble toque Enable gesture control = Habilitar gestos Enable standard shortcut keys = Habilitar teclas de atajo +frames = frames Gesture = Gestos Gesture mapping = Ajustes de gestos Glowing borders = Bordes brillantes @@ -116,6 +120,7 @@ OnScreen = Controles táctiles en pantalla Portrait = Vertical Portrait Reversed = Vertical invertido PSP Action Buttons = Botones de acción +Rapid fire interval = Rapid fire interval Raw input = Entrada raw Repeat mode = Repeat mode Reset to defaults = Restaurar ajustes @@ -310,10 +315,14 @@ VFPU = VFPU * PSP res = * resolución PSP Active = Activo Back = Atrás +Bottom Center = Inferior centrado +Bottom Left = Inferior izquierda +Bottom Right = Inferior derecha Cancel = Cancelar Center = Centrar -ChangingGPUBackends = Cambiar el motor gráfico requiere reiniciar PPSSPP.\n¿Reiniciar ahora? -ChangingInflightFrames = Cambiar el búfer de comando de gráficos requiere reiniciar PPSSPP.\n¿Reiniciar ahora? +Center Left = Centrado izquierdo +Center Right = Centrado derecho +Changing this setting requires PPSSPP to restart. = Cambiar esta opción requiere reiniciar PPSSPP.\n¿Reiniciar ahora? Channel: = Canal: Choose PPSSPP save folder = Elige la carpeta de guardado de PPSSPP. Confirm Overwrite = ¿Deseas sobreescribir los datos? @@ -361,6 +370,7 @@ Move Up = Subir Network Connection = Conexión de red NEW DATA = NUEVOS DATOS DE PARTIDA No = No +None = Ninguno ObtainingIP = Obteniendo direcciónnn IP.\nPor favor espera... OK = Aceptar Old savedata detected = Se han detectado datos del juego antiguos. @@ -369,6 +379,7 @@ Password = Password Remove = Quitar Reset = Reiniciar Resize = Tamaño +Restart = Reiniciar Retry = Reintentar Save = Guardar Save completed = Los datos del juego han sido almacenados. @@ -388,6 +399,9 @@ Supported = Soportado There is no data = La tarjeta de memoria está vacía. Toggle All = Cambiar todo Toggle List = Ver lista +Top Center = Superior centrado +Top Left = Superior izquierda +Top Right = Superior derecha Unsupported = No soportado Username = Username When you save, it will load on a PSP, but not an older PPSSPP = Cuando guardes, la partida cargará en una PSP, pero no en versiones antiguas de PPSSPP. @@ -604,6 +618,7 @@ Stereo rendering = Renderizado estéreo Stretch = Estirar Texture Filter = Filtro de texturas Texture Filtering = Filtrado de texturas +Texture replacement pack activated = Texture replacement pack activated Texture Scaling = Escalado de texturas Texture Shader = Shader de textura Turn off Hardware Tessellation - unsupported = Desactivando teselado por hardware: no soportado. @@ -790,11 +805,6 @@ AdHoc Server = Servidor AdHoc AdhocServer Failed to Bind Port = Servidor Adhoc falló al unir puerto. AM: Data from Unknown Port = AM: Datos de puerto desconocido Auto = Automático -Bottom Center = Inferior centrado -Bottom Left = Inferior izquierda -Bottom Right = Inferior derecha -Center Left = Centrado izquierdo -Center Right = Centrado derecho Change Mac Address = Cambiar dirección MAC Change proAdhocServer Address = Cambiar dirección de proAdhocServer (localhost = varias instancias) ChangeMacSaveConfirm = Generar una nueva dirección MAC? @@ -828,7 +838,6 @@ Invalid IP or hostname = IP o nombre del host inválido Minimum Timeout = Tiempo de espera mínimo (en ms, 0 por defecto) Misc = Otros ajustes (por defecto = compatibilidad) Network Initialized = Juego en red iniciado -None = Ninguno Please change your Port Offset = Por favor cambia el offset del puerto. Port offset = Variar puerto de red (0 = compatible con PSP) Open PPSSPP Multiplayer Wiki Page = Página wiki sobre PPSSPP Ad-Hoc @@ -839,11 +848,9 @@ Quick Chat 3 = Chat rápido 3 Quick Chat 4 = Chat rápido 4 Quick Chat 5 = Chat rápido 5 QuickChat = Chat rápido +Randomize = Randomize Send = Enviar Send Discord Presence information = Enviar información a Discord "Rich Presence" -Top Center = Superior centrado -Top Left = Superior izquierda -Top Right = Superior derecha Unable to find UPnP device = Dispositivo UPnP no encontrado UPnP (port-forwarding) = UPnP (redirección de puertos) UPnP need to be reinitialized = La función de UPnP debe reinicializarse. @@ -853,6 +860,23 @@ Validating address... = Validando dirección... WLAN Channel = Canal WLAN You're in Offline Mode, go to lobby or online hall = Estás en modo offline, ve al lobby o a la sala online +[PSPSettings] +Auto = Automático +Chinese (simplified) = Chino (Simplificado) +Chinese (traditional) = Chino (Tradicional) +Dutch = Neerlandés +English = Inglés +French = Francés +Game language = Lenguaje del juego +German = Alemán +Italian = Italiano +Japanese = Japones +Korean = Coreano +Games often don't support all languages = Los juegos usualmente no pueden usar todos los lenguajes +Portuguese = Portugués +Russian = Ruso +Spanish = Castellano + [Pause] Cheats = Trucos Continue = Continuar diff --git a/assets/lang/es_LA.ini b/assets/lang/es_LA.ini index 29dd67c212..641c32113a 100644 --- a/assets/lang/es_LA.ini +++ b/assets/lang/es_LA.ini @@ -1,40 +1,42 @@ [Achievements] -%1: Leaderboard attempt failed = %1: Leaderboard attempt failed -%1: Leaderboard attempt started = %1: Leaderboard attempt started -%d achievements, %d points = %d achievements, %d points -Account = Account -Achievement Unlocked = Achievement Unlocked -Achievements = Achievements -Achievements are disabled = Achievements are disabled -Challenge Mode = Challenge Mode -Challenge Mode (no savestates) = Challenge Mode (no savestates) -Customize = Customize -Earned = You have earned %d of %d achievements, and %d of %d points -Encore Mode = Encore Mode -How to use RetroAchievements = How to use RetroAchievements -Leaderboard score submission = Leaderboard score submission -Leaderboard submission is enabled = Leaderboard submission is enabled +%d achievements, %d points = %d logros, %d puntos +%1: Leaderboard attempt started = %1: intento para el leaderboard iniciado +%1: Leaderboard attempt failed = %1: intento para el leaderboard fallido +Account = Cuenta +Achievement Unlocked = Logro Desbloqueado +Achievements = Logros +Achievements are disabled = Los logros están desactivados +Can't log in to RetroAchievements right now = No se pudo iniciar sesión a RetroAchievements +Challenge Mode = Modo Desafío +Challenge Mode (no savestates) = Modo Desafío (Sin estados) +Contacting RetroAchievements server... = Contactando al servidor de RetroAchievements... +Customize = Customizar +Earned = Has obtenido %d de %d logros y %d de %d puntos +Encore Mode = Modo Encore +How to use RetroAchievements = Como usar RetroAchievements +In Encore mode - listings may be wrong below = En el modo Encore - las listas de abajo pueden estar equivocadas +Leaderboard score submission = Subir puntos al leaderboard +Leaderboard submission is enabled = Se ha activado la subida de puntos al leaderboard Leaderboards = Leaderboards Links = Links -Locked achievements = Locked achievements -Log bad memory accesses = Log bad memory accesses -Mastered %1 = Mastered %1 -Register on www.retroachievements.org = Register on www.retroachievements.org -RetroAchievements are not available for this game = RetroAchievements are not available for this game -RetroAchievements website = RetroAchievements website +Locked achievements = Logros bloqueados +Log bad memory accesses = Registrar malos accesos a la memoría +Mastered %1 = Completaste %1 +Register on www.retroachievements.org = Registrate en www.retroachievements.org +RetroAchievements are not available for this game = RetroAchievements no está disponible para este juego +RetroAchievements website = Página web de RetroAchievements Rich Presence = Rich Presence -Save state loaded without achievement data = Save state loaded without achievement data -Save states not available in Challenge Mode = Save states not available in Challenge Mode -Sound Effects = Sound Effects -Statistics = Statistics -Submitted %1 for %2 = Submitted %1 for %2 -Syncing achievements data... = Syncing achievements data... -Test Mode = Test Mode -This feature is not available in Challenge Mode = This feature is not available in Challenge Mode -This game has no achievements = This game has no achievements -Unlocked achievements = Unlocked achievements -#Unofficial = Unofficial -Unofficial achievements = Unofficial achievements +Save states not available in Challenge Mode = Los estados no están disponibles en el modo desafío +Save state loaded without achievement data = Estado guardado sin información de los logros +Sound Effects = Efectos de sonido +Statistics = Estadísticas +Submitted %1 for %2 = Se subió %1 para %2 +Syncing achievements data... = Sincronizando información de logros... +Test Mode = Modo prueba +This feature is not available in Challenge Mode = Esta opción no está disponible en el modo desafío +This game has no achievements = Este juego no tiene logros +Unlocked achievements = Logros desbloqueados +Unofficial achievements = Logros no oficiales [Audio] Alternate speed volume = Alternar velocidad de volumen @@ -89,6 +91,7 @@ Disable diagonal input = Deshabilitar mov. diagonal Double tap = Doble toque Enable gesture control = Habilitar gestos Enable standard shortcut keys = Habilitar teclas de atajo +frames = frames Gesture = Gestos Gesture mapping = Configurac. de gestos Glowing borders = Bordes brillantes @@ -117,6 +120,7 @@ OnScreen = Controles en pantalla Portrait = Retrato Portrait Reversed = Retrato invertido PSP Action Buttons = Botones de acción PSP +Rapid fire interval = Rapid fire interval Raw input = Raw input Repeat mode = Repeat mode Reset to defaults = Restaurar config. @@ -311,10 +315,14 @@ VFPU = VFPU * PSP res = * PSP res Active = Activo Back = Atrás +Bottom Center = Abajo +Bottom Left = Abajo Izquierda +Bottom Right = Abajo Derecha Cancel = Cancelar Center = Centrar -ChangingGPUBackends = Cambiar el GPU requiere reiniciar PPSSPP.\n¿Reiniciar ahora? -ChangingInflightFrames = Cambiar el almacenamiento en búfer de comandos gráficos requiere que PPSSPP se reinicie.\n¿Reiniciar ahora? +Center Left = Izquierda +Center Right = Derecha +Changing this setting requires PPSSPP to restart. = Cambiar esta opción requiere que PPSSPP se reinicie.\n¿Reiniciar ahora? Channel: = Canal: Choose PPSSPP save folder = Elige la carpeta de guardado de PPSSPP. Confirm Overwrite = Hay archivos existentes,\n¿Deseas sobreescribirlos? @@ -362,6 +370,7 @@ Move Up = Move Up Network Connection = Conexión de red NEW DATA = CREAR DATOS No = No +None = None ObtainingIP = Obteniendo dirección IP.\nEspera un momento... OK = OK Old savedata detected = Se han detectado datos del juego antiguos. @@ -370,6 +379,7 @@ Password = Password Remove = Remove Reset = Reiniciar Resize = Tamaño +Restart = Reiniciar Retry = Reintentar Save = Guardar Save completed = Los datos del juego han sido almacenados. @@ -389,6 +399,9 @@ Supported = Soportado There is no data = No se han encontrado datos del juego. Toggle All = Cambiar todo Toggle List = Cambiar lista +Top Center = Arriba +Top Left = Arriba Izquierda +Top Right = Arriba Derecha Unsupported = No es soportado Username = Username When you save, it will load on a PSP, but not an older PPSSPP = Cuando guardes, la partida cargará en una PSP, pero no en versiones antiguas de PPSSPP. @@ -605,6 +618,7 @@ Stereo rendering = Stereo rendering Stretch = Estirar Texture Filter = Filtro de texturas Texture Filtering = Filtrado de texturas +Texture replacement pack activated = Texture replacement pack activated Texture Scaling = Escalado de texturas Texture Shader = Shader de texturas Turn off Hardware Tessellation - unsupported = Desactivando Teselado por Hardware: no es soportado. @@ -790,11 +804,6 @@ AdHoc Server = Servidor Adhoc AdhocServer Failed to Bind Port = Servidor Adhoc falló al unir puerto. AM: Data from Unknown Port = AM: Datos de puerto desconocido Auto = Auto -Bottom Center = Abajo -Bottom Left = Abajo Izquierda -Bottom Right = Abajo Derecha -Center Left = Izquierda -Center Right = Derecha Change Mac Address = Cambiar dirección MAC Change proAdhocServer Address = Cambiar dirección IP de\nservidor PRO Adhoc (localhost = multiples instancias) ChangeMacSaveConfirm = Generate a new MAC address? @@ -828,7 +837,6 @@ Invalid IP or hostname = IP o nombre de host no válido Minimum Timeout = Tiempo de espera mínimo (en ms, 0 = por defecto) Misc = Otros ajustes (por defecto = compatibilidad con PSP) Network Initialized = Juego en red iniciado -None = None Please change your Port Offset = Por favor cambia el offset del puerto. Port offset = Variar puerto de red (0 = compatibilidad con PSP) Open PPSSPP Multiplayer Wiki Page = Página wiki sobre PPSSPP Ad-Hoc @@ -839,11 +847,9 @@ Quick Chat 3 = Chat rápido 3 Quick Chat 4 = Chat rápido 4 Quick Chat 5 = Chat rápido 5 QuickChat = Chat rápido +Randomize = Randomize Send = Enviar Send Discord Presence information = Enviar información al Discord "Rich Presence" -Top Center = Arriba -Top Left = Arriba Izquierda -Top Right = Arriba Derecha Unable to find UPnP device = No se encuentra el dispositivo UPnP UPnP (port-forwarding) = UPnP (redirección de puertos) UPnP need to be reinitialized = La función de UPnP debe reinicializarse. @@ -853,6 +859,23 @@ Validating address... = Validando dirección ... WLAN Channel = Canal WLAN You're in Offline Mode, go to lobby or online hall = Estás en modo fuera de línea, ve al lobby o sala en línea +[PSPSettings] +Auto = Automático +Chinese (simplified) = Chino (Simplificado) +Chinese (traditional) = Chino (Tradicional) +Dutch = Neerlandés +English = Inglés +French = Francés +Game language = Lenguaje del juego +German = Alemán +Italian = Italiano +Japanese = Japones +Korean = Coreano +Games often don't support all languages = Los juegos usualmente no pueden usar todos los lenguajes +Portuguese = Portugués +Russian = Ruso +Spanish = Español + [Pause] Cheats = Trampas Continue = Continuar @@ -950,6 +973,8 @@ Twitter @PPSSPP_emu = Twitter @PPSSPP_emu website = Sitio web oficial written = Escrito en lenguaje C++ para una mejor portabilidad y velocidad +[PSPSettings] + [RemoteISO] Browse Games = Buscar juegos Local Server Port = Puerto del servidor local @@ -1218,7 +1243,7 @@ Reset Recording on Save/Load State = Reiniciar grabación al abrir/guardar estad Restore Default Settings = Reestablecer ajustes Rewind Snapshot Interval = Frecuencia de rebobinado\n(consume memoria) Save path in installed.txt = Guardar ruta en installed.txt -Save path in My Documents = Guardar ruta en\n"C:/Users/(tu usuario)/Documents" +Save path in My Documents = Guardar ruta en "C:/Users/(tu usuario)/Documents" Savestate Slot = Ranura de estado guardado Savestate slot backups = Copias de seguridad de estado guardado Screenshots as PNG = Capturas en PNG diff --git a/assets/lang/fa_IR.ini b/assets/lang/fa_IR.ini index 2cd3ede07d..354f6ce5d4 100644 --- a/assets/lang/fa_IR.ini +++ b/assets/lang/fa_IR.ini @@ -6,12 +6,15 @@ Account = Account Achievement Unlocked = Achievement Unlocked Achievements = Achievements Achievements are disabled = Achievements are disabled +Can't log in to RetroAchievements right now = Can't log in to RetroAchievements right now Challenge Mode = Challenge Mode Challenge Mode (no savestates) = Challenge Mode (no savestates) +Contacting RetroAchievements server... = Contacting RetroAchievements server... Customize = Customize Earned = You have earned %d of %d achievements, and %d of %d points Encore Mode = Encore Mode How to use RetroAchievements = How to use RetroAchievements +In Encore mode - listings may be wrong below = In Encore mode - listings may be wrong below Leaderboard score submission = Leaderboard score submission Leaderboard submission is enabled = Leaderboard submission is enabled Leaderboards = Leaderboards @@ -88,6 +91,7 @@ Disable diagonal input = Disable diagonal input Double tap = Double tap Enable gesture control = Enable gesture control Enable standard shortcut keys = Enable standard shortcut keys +frames = frames Gesture = Gesture Gesture mapping = Gesture mapping Glowing borders = Glowing borders @@ -116,6 +120,7 @@ OnScreen = ‎نمایش کنترل های لمسی Portrait = ‎عمودی Portrait Reversed = ‎عمودی وارونه PSP Action Buttons = ‎اکشن دکمه های +Rapid fire interval = Rapid fire interval Raw input = ورودی راو Repeat mode = حالت تکرار Reset to defaults = ریست کردن پیشفرض @@ -310,10 +315,14 @@ VFPU = VFPU * PSP res = * PSP res Active = Active Back = ‎برگشت +Bottom Center = Bottom center +Bottom Left = Bottom left +Bottom Right = Bottom right Cancel = ‎انصراف Center = وسط -ChangingGPUBackends = تغییر Backends GPU نیاز به PPSSPP برای راه اندازی مجدد. راه اندازی مجدد حالا؟ -ChangingInflightFrames = تغییر دستور گرافیک گرافیک نیاز به PPSSPP برای راه اندازی مجدد دارد. راه اندازی مجدد حالا؟ +Center Left = Center left +Center Right = Center right +Changing this setting requires PPSSPP to restart. = تغییر دستور گرافیک گرافیک نیاز به PPSSPP برای راه اندازی مجدد دارد. راه اندازی مجدد حالا؟ Channel: = کانال: Choose PPSSPP save folder = انتخاب پوشه برای PPSSSPP Confirm Overwrite = ‎آیا می خواهید که دیتا ها را بازنویسی کنید؟ @@ -361,6 +370,7 @@ Move Up = حرکت به بالا Network Connection = Network Connection NEW DATA = ‎دیتای جدید No = ‎خیر +None = None ObtainingIP = Obtaining IP address.\nPlease wait... OK = ‎تایید Old savedata detected = Old savedata detected @@ -369,6 +379,7 @@ Password = Password Remove = حذف Reset = بازیابی Resize = اندازه گیری +Restart = Restart Retry = تلاش مجدد Save = ‎ذخیره Save completed = ‎.ذخیره تکمیل شد @@ -388,6 +399,9 @@ Supported = حمایت میشود There is no data = ‎.دیتایی وجود ندارد Toggle All = تغییر وضعیت همه Toggle List = تغییر وضعیت لیست +Top Center = Top center +Top Left = Top left +Top Right = Top right Unsupported = ساپورت نمیشود Username = Username When you save, it will load on a PSP, but not an older PPSSPP = When you save, it will load on a PSP, but not an older PPSSPP @@ -604,6 +618,7 @@ Stereo rendering = Stereo rendering Stretch = ‎کشیدن Texture Filter = ‎فیلتر تکسچر Texture Filtering = ‎فیلتر کردن تکسچر +Texture replacement pack activated = Texture replacement pack activated Texture Scaling = ‎تغییر سایز تکسچر ها Texture Shader = Texture shader Turn off Hardware Tessellation - unsupported = Turn off "hardware tessellation": unsupported @@ -789,11 +804,6 @@ AdHoc Server = Ad hoc server AdhocServer Failed to Bind Port = Ad hoc server failed to bind port AM: Data from Unknown Port = AM: Data from Unknown Port Auto = Auto -Bottom Center = Bottom center -Bottom Left = Bottom left -Bottom Right = Bottom right -Center Left = Center left -Center Right = Center right Change Mac Address = Change MAC address Change proAdhocServer Address = Change PRO ad hoc server IP address (localhost = multiple instances) ChangeMacSaveConfirm = Generate a new MAC address? @@ -827,7 +837,6 @@ Invalid IP or hostname = Invalid IP or hostname Minimum Timeout = Minimum timeout (override in ms, 0 = default) Misc = Miscellaneous (default = PSP compatibility) Network Initialized = Network initialized -None = None Please change your Port Offset = Please change your port offset Port offset = Port offset(0 = PSP compatibility) Open PPSSPP Multiplayer Wiki Page = Open PPSSPP Multiplayer Wiki Page @@ -838,11 +847,9 @@ Quick Chat 3 = Quick chat 3 Quick Chat 4 = Quick chat 4 Quick Chat 5 = Quick chat 5 QuickChat = Quick chat +Randomize = Randomize Send = Send Send Discord Presence information = Send Discord "Rich Presence" information -Top Center = Top center -Top Left = Top left -Top Right = Top right Unable to find UPnP device = Unable to find UPnP device UPnP (port-forwarding) = UPnP (port forwarding) UPnP need to be reinitialized = UPnP need to be reinitialized @@ -852,6 +859,23 @@ Validating address... = Validating address... WLAN Channel = WLAN channel You're in Offline Mode, go to lobby or online hall = You're in offline mode, go to lobby or online hall +[PSPSettings] +Auto = Auto +Chinese (simplified) = Chinese (simplified) +Chinese (traditional) = Chinese (traditional) +Dutch = Dutch +English = English +French = French +Game language = Game language +German = German +Italian = Italian +Japanese = Japanese +Korean = Korean +Games often don't support all languages = Games often don't support all languages +Portuguese = Portuguese +Russian = Russian +Spanish = Spanish + [Pause] Cheats = ‎کد تقلب Continue = ‎ادامه بازی diff --git a/assets/lang/fi_FI.ini b/assets/lang/fi_FI.ini index fe047eb8da..01e14c2e77 100644 --- a/assets/lang/fi_FI.ini +++ b/assets/lang/fi_FI.ini @@ -1,39 +1,42 @@ [Achievements] %1: Leaderboard attempt failed = %1: Leaderboard attempt failed %1: Leaderboard attempt started = %1: Leaderboard attempt started -%d achievements, %d points = %d achievements, %d points -Account = Account -Achievement Unlocked = Achievement Unlocked -Achievements = Achievements -Achievements are disabled = Achievements are disabled -Challenge Mode = Challenge Mode -Challenge Mode (no savestates) = Challenge Mode (no savestates) -Customize = Customize -Earned = You have earned %d of %d achievements, and %d of %d points +%d achievements, %d points = %d saavutusta, %d pistettä +Account = Tili +Achievement Unlocked = Saavutus avattu +Achievements = Saavutukset +Achievements are disabled = Saavutukset ovat pois käytöstä +Can't log in to RetroAchievements right now = RetroAchievements:iin ei voida kirjautua juuri nyt +Challenge Mode = Haastetila +Challenge Mode (no savestates) = Haastetila (ei tilatallennuksia) +Contacting RetroAchievements server... = Yhdistetään RetroAchievements:in palvelimelle... +Customize = Mukauta +Earned = Olet ansainnut %d/%d saavutusta, ja %d/%d pistettä Encore Mode = Encore Mode -How to use RetroAchievements = How to use RetroAchievements -Leaderboard score submission = Leaderboard score submission -Leaderboard submission is enabled = Leaderboard submission is enabled -Leaderboards = Leaderboards -Links = Links -Locked achievements = Locked achievements -Log bad memory accesses = Log bad memory accesses +How to use RetroAchievements = Kuinka käyttää RetroAchievements:ia +In Encore mode - listings may be wrong below = In Encore mode - listings may be wrong below +Leaderboard score submission = Sijoituslistan pisteiden lähetys +Leaderboard submission is enabled = Sijoituslistan lähetys on käytössä +Leaderboards = Sijoituslista +Links = Linkit +Locked achievements = Lukitut saavutukset +Log bad memory accesses = Kirjaa virheelliset muistipääsyt Mastered %1 = Mastered %1 -Register on www.retroachievements.org = Register on www.retroachievements.org -RetroAchievements are not available for this game = RetroAchievements are not available for this game -RetroAchievements website = RetroAchievements website +Register on www.retroachievements.org = Rekisteröidy osoitteessa www.retroachievements.org +RetroAchievements are not available for this game = RetroAchievements eivät ole käytössä tälle pelille +RetroAchievements website = RetroAchievements verkkosivu Rich Presence = Rich Presence -Save state loaded without achievement data = Save state loaded without achievement data -Save states not available in Challenge Mode = Save states not available in Challenge Mode -Sound Effects = Sound Effects -Statistics = Statistics +Save state loaded without achievement data = Tilatallennus ladattu, ilman saavutuksen tietoja +Save states not available in Challenge Mode = Tilatallennukset eivät ole käytössä haastetilassa +Sound Effects = Äänimerkit +Statistics = Tilastot Submitted %1 for %2 = Submitted %1 for %2 -Syncing achievements data... = Syncing achievements data... -Test Mode = Test Mode -This feature is not available in Challenge Mode = This feature is not available in Challenge Mode -This game has no achievements = This game has no achievements -Unlocked achievements = Unlocked achievements -Unofficial achievements = Unofficial achievements +Syncing achievements data... = Synkoronoidaan saavutusten tietoja... +Test Mode = Testitila +This feature is not available in Challenge Mode = Tämä ominaisuus ei ole käytettävissä haastetilassa +This game has no achievements = Tällä pelillä ei ole saavutuksia +Unlocked achievements = Avatut saavutukset +Unofficial achievements = Epäviralliset saavutukset [Audio] Alternate speed volume = Vaihtoehtoisen nopeuden äänenvoimakkuus @@ -55,90 +58,92 @@ Use global volume = Käytä yleistä äänenvoimakkuutta WASAPI (fast) = WASAPI (nopea) [Controls] -Analog Binding = Analog Binding -Analog Limiter = Analog limiter -Analog Settings = Analog Settings -Analog Stick = Analog stick -Analog Style = Analog Style -AnalogLimiter Tip = When the analog limiter button is pressed -Auto = Auto -Auto-centering analog stick = Auto-centering analog stick -Auto-hide buttons after delay = Auto-hide buttons after delay +Analog Binding = Analoginen kartoitus +Analog Limiter = Analoginen rajoitin +Analog Settings = Analogisen asetukset +Analog Stick = Analoginen sauva +Analog Style = Analogisen tyyli +AnalogLimiter Tip = Kun analogisen rajoittimen näppäintä painetaan +Auto = Automaattinen +Auto-centering analog stick = Automaattisesti keskittävä analoginen sauva +Auto-hide buttons after delay = Piilota näppäimet automaattisesti viiveen jälkeen Auto-rotation speed = Auto-rotation speed -Binds = Binds -Button Binding = Button Binding -Button Opacity = Painikkeiden peittävyys -Button style = Button style -Calibrate Analog Stick = Calibrate Analog Stick -Calibrate = Calibrate -Calibrated = Calibrated -Calibration = Calibration +Binds = Sidokset +Button Binding = Näppäinten sidokset +Button Opacity = Näppäinten peittävyys +Button style = Näppäinten tyyli +Calibrate Analog Stick = Kalibroi analoginen sauva +Calibrate = Kalibroi +Calibrated = Kalibroitu +Calibration = Kalibraatio Circular low end radius = Circular low end radius Circular stick input = Circular stick input Classic = Klassinen -Confine Mouse = Trap mouse within window/display area -Control Mapping = Control mapping +Confine Mouse = Kaappaa hiiri ikkunan/näytön sisälle +Control Mapping = Ohjauksen kartoitus Custom Key Setting = Custom Key Setting -Customize = Customize -Customize Touch Controls = Edit touch control layout... -D-PAD = D-Pad -Deadzone radius = Deadzone radius -Disable D-Pad diagonals (4-way touch) = Disable D-Pad diagonals (4-way touch) -Disable diagonal input = Disable diagonal input -Double tap = Double tap -Enable gesture control = Enable gesture control +Customize = Mukauta +Customize Touch Controls = Muokkaa kosketusohjauksen asetelmaa... +D-PAD = Ristiohjain +Deadzone radius = Katvealueen säde +Disable D-Pad diagonals (4-way touch) = Poista käytöstä ristiohjaimen vinottainen syöte (nelisuuntainen syöte) +Disable diagonal input = Poista vinottainen syöte käytöstä +Double tap = Kaksinkertainen napautus +Enable gesture control = Ota eleohjaukset käyttöön Enable standard shortcut keys = Enable standard shortcut keys -Gesture = Gesture -Gesture mapping = Gesture mapping -Glowing borders = Glowing borders -HapticFeedback = Haptic feedback (vibration) -Hide touch analog stick background circle = Hide touch analog stick background circle -Icon = Icon -Ignore gamepads when not focused = Ignore gamepads when not focused -Ignore Windows Key = Disable Windows key -Invert Axes = Invert axes -Invert Tilt along X axis = Invert tilt along X axis -Invert Tilt along Y axis = Invert tilt along Y axis +frames = kuvat +Gesture = Eleet +Gesture mapping = Eleiden kartoitus +Glowing borders = Hohtavat rajat +HapticFeedback = Haptinen palaute (värinä) +Hide touch analog stick background circle = Piiloita kosketettavan analogisen sauvan taustaympyrä +Icon = Kuvake +Ignore gamepads when not focused = Älä huomioi ohjaimia, kun ohjelma ei keskitettynä +Ignore Windows Key = Poista Windows-näppäin käytöstä +Invert Axes = Käännä akselit +Invert Tilt along X axis = Käännä kallistus X-akselilla +Invert Tilt along Y axis = Käännä kallistus Y-akselilla Keep this button pressed when right analog is pressed = Keep this button pressed when right analog is pressed -Keyboard = Keyboard control settings -L/R Trigger Buttons = L/R trigger buttons +Keyboard = Näppäimistöohjauksen asetukset +L/R Trigger Buttons = L/R-liipaisimen näppäimet Landscape = Landscape Landscape Auto = Landscape auto Landscape Reversed = Landscape reversed Low end radius = Low end radius Mouse = Mouse settings -Mouse sensitivity = Mouse sensitivity -Mouse smoothing = Mouse smoothing +Mouse sensitivity = Hiiren herkkyys +Mouse smoothing = Hiiren tasoitus MouseControl Tip = You can now map mouse in control mapping screen by pressing the 'M' icon. None (Disabled) = None (disabled) -Off = Off +Off = Pois OnScreen = Kosketusnäytön kontrollit Portrait = Portrait Portrait Reversed = Portrait reversed -PSP Action Buttons = PSP action buttons +PSP Action Buttons = PSP-toiminta näppäimet +Rapid fire interval = Rapid fire interval Raw input = Raw input Repeat mode = Repeat mode -Reset to defaults = Reset to defaults -Screen Rotation = Screen rotation -Sensitivity (scale) = Sensitivity (scale) -Sensitivity = Sensitivity +Reset to defaults = Palauta oletukset +Screen Rotation = Näytön kierto +Sensitivity (scale) = Herkkyys (skaala) +Sensitivity = Herkkyys Shape = Shape Show Touch Pause Menu Button = Show pause menu button -Swipe = Swipe -Swipe sensitivity = Swipe sensitivity -Swipe smoothing = Swipe smoothing -Thin borders = Thin borders -Tilt control setup = Tilt control setup -Tilt Input Type = Tilt input type -Tilt Sensitivity along X axis = Tilt sensitivity along X axis -Tilt Sensitivity along Y axis = Tilt sensitivity along Y axis -To Calibrate = Hold device at your preferred angle and press "Calibrate". -Toggle mode = Toggle mode -Touch Control Visibility = Touch control visibility -Use custom right analog = Use custom right analog -Use Mouse Control = Use mouse control -Visibility = Visibility -Visible = Visible +Swipe = Pyyhkäise +Swipe sensitivity = Pyyhkäisyn herkkyys +Swipe smoothing = Pyyhkäisyn tasoitus +Thin borders = Ohuet rajat +Tilt control setup = Kallistusohjauksen asetus +Tilt Input Type = Kallistusohjauksen tyyppi +Tilt Sensitivity along X axis = Kallistuksen herkkyys X-akselilla +Tilt Sensitivity along Y axis = Kallistuksen herkkyys Y-akselilla +To Calibrate = Pidä laitettasi halutussa kulmassa, ja paina 'Kalibroi'. +Toggle mode = Vaihto tila +Touch Control Visibility = Kosketusohjauksen näkyvyys +Use custom right analog = Käytä mukautettua oikeaa analogista +Use Mouse Control = Käytä hiiriohjausta +Visibility = Näkyvyys +Visible = Näkyvä X = X Y = Y @@ -153,41 +158,41 @@ Refresh Rate = Ruudunpäivitysnopeus # If your language does not show well with the default font, you can use Font to specify a different one. # Just add it to your language's ini file and uncomment it (remove the # by Font). #Font = Trebuchet MS -About PPSSPP... = &About PPSSPP... -Auto = &Auto -Auto Max Quality = Auto Max &Quality -Backend = Rendering &Backend (Restarts PPSSPP) +About PPSSPP... = &Tietoa PPSSPP:stä... +Auto = &Automaattinen +Auto Max Quality = Automaattinen maksimi &laatu +Backend = Renderöinnin taustaraja&pinta (PPSSPP uudelleenkäynnistyy) Bicubic = &Bicubic Break = Break Break on Load = Break on load -Buy Gold = Buy &Gold -Control Mapping... = C&ontrol Mapping... -Copy PSP memory base address = Copy PSP memory base &address -Debugging = &Debug -Deposterize = &Deposterize +Buy Gold = Osta &Gold +Control Mapping... = &Ohjauksen kartoitus... +Copy PSP memory base address = Kopioi PSP:n muistin &perusosoite +Debugging = &Vianetsintä +Deposterize = &Häiriöiden korjaus Direct3D9 = &Direct3D9 Direct3D11 = Direct3D &11 Disassembly = &Disassembly... Discord = Discord -Display Layout && Effects = Display layout && effects... -Display Rotation = Display rotation -Dump Next Frame to Log = D&ump Next Frame to Log -Emulation = &Emulation -Enable Cheats = Enable &Cheats -Enable Sound = Enable S&ound -Exit = E&xit -Extract File... = E&xtract File... -File = &File -Frame Skipping = &Frame skipping -Frame Skipping Type = Frame skipping type -Fullscreen = Fu&llscreen -Game Settings = &Game Settings +Display Layout && Effects = Näyttöasettelu ja -&efektit +Display Rotation = Näytön kierto +Dump Next Frame to Log = Tallenna seuraava &kuva +Emulation = &Emulaatio +Enable Cheats = Ota &huijaukset käyttöön +Enable Sound = Ota &ääni käyttöön +Exit = &Poistu +Extract File... = &Pura tiedosto... +File = &Tiedosto +Frame Skipping = &Kuvan ohitus +Frame Skipping Type = Kuvan ohituksen tyyppi +Fullscreen = &Koko näyttö +Game Settings = &Pelin asetukset GE Debugger... = GE Debugge&r... GitHub = Git&Hub Hardware Transform = &Hardware Transform -Help = &Help -Hybrid = &Hybrid -Hybrid + Bicubic = H&ybrid + Bicubic +Help = &Apua +Hybrid = &Hybridi +Hybrid + Bicubic = H&ybridi + Bicubic Ignore Illegal Reads/Writes = &Ignore Illegal Reads/Writes Ignore Windows Key = Ignore Windows Key Keep PPSSPP On Top = &Keep PPSSPP on Top @@ -217,37 +222,37 @@ Pause = &Pause Pause When Not Focused = &Pause When Not Focused Portrait = Portrait Portrait reversed = Portrait reversed -PPSSPP Forums = PPSSPP &Forums -Record = &Record -Record Audio = Record &audio -Record Display = Record &display -Rendering Mode = Rendering M&ode -Rendering Resolution = &Rendering Resolution -Reset = R&eset -Reset Symbol Table = Reset S&ymbol Table -Run = &Run -Save .sym File... = Sav&e .sym File... -Save Map File... = &Save Map File... -Save State = S&ave State -Save State File... = &Save State File... -Savestate Slot = Saves&tate Slot -Screen Scaling Filter = Scr&een Scaling Filter -Show Debug Statistics = Show Debu&g Statistics -Show FPS Counter = Show &FPS Counter -Skip Number of Frames = Skip number of frames -Skip Percent of FPS = Skip percent of FPS -Stop = &Stop -Switch UMD = Switch UMD -Take Screenshot = &Take Screenshot -Texture Filtering = Te&xture Filtering -Texture Scaling = &Texture Scaling -Use Lossless Video Codec (FFV1) = &Use lossless video codec (FFV1) -Use output buffer for video = Use output buffer for video -Vertex Cache = &Vertex Cache +PPSSPP Forums = PPSSPP:n &foorumit +Record = &Nauhoita +Record Audio = Nauhoita &ääntä +Record Display = Nauhoita &näyttöä +Rendering Mode = Ren&deröinti tapa +Rendering Resolution = &Renderöinti resoluutio +Reset = &Nollaa +Reset Symbol Table = Nollaa s&ymbolitaulukko +Run = &Suorita +Save .sym File... = Tallenna .&sym-tiedosto... +Save Map File... = Tallenna &kartoitustiedosto... +Save State = Tallenna &tila +Save State File... = Tilatallennuksen ti&edosto... +Savestate Slot = Tilatallennuksen &lohko +Screen Scaling Filter = N&äytön skaalaussuodatin +Show Debug Statistics = Näytä &vianetsinnän tiedot +Show FPS Counter = Näytä kuvalaskuri (FPS) +Skip Number of Frames = Ohita tämä määrä kuvia +Skip Percent of FPS = Ohita prosenttiosuus kuvista +Stop = &Pysäytä +Switch UMD = Vaihda UMD +Take Screenshot = Ota &kuvakaappaus +Texture Filtering = &Tekstuurien suodatus +Texture Scaling = &Tekstuurien skaalaus +Use Lossless Video Codec (FFV1) = Käytä häviötöntä &videokoodekkia (FFV1) +Use output buffer for video = Käytä ulostulopuskuria videolle +Vertex Cache = &Vertex-välimuisti VSync = VS&ync Vulkan = Vulkan -Window Size = &Window Size -www.ppsspp.org = Visit www.&ppsspp.org +Window Size = &Ikkunan koko +www.ppsspp.org = Käy osoitteessa www.&ppsspp.org xBRZ = &xBRZ [Developer] @@ -258,9 +263,9 @@ Block address = Block address By Address = By address Copy savestates to memstick root = Copy save states to Memory Stick root Create/Open textures.ini file for current game = Create/Open textures.ini file for current game -Current = Current -Dev Tools = Development tools -DevMenu = DevMenu +Current = Nykyinen +Dev Tools = Kehitystökalut +DevMenu = Kehitysvalikko Disabled JIT functionality = Disabled JIT functionality Draw Frametimes Graph = Draw frametimes graph Dump Decrypted Eboot = Dump decrypted EBOOT.BIN on game boot @@ -310,10 +315,14 @@ VFPU = VFPU * PSP res = * PSP res Active = Active Back = Takaisin +Bottom Center = Bottom center +Bottom Left = Bottom left +Bottom Right = Bottom right Cancel = Cancel Center = Center -ChangingGPUBackends = Changing GPU backends requires PPSSPP to restart. Restart now? -ChangingInflightFrames = Changing graphics command buffering requires PPSSPP to restart. Restart now? +Center Left = Center left +Center Right = Center right +Changing this setting requires PPSSPP to restart. = Changing this setting requires PPSSPP to restart. Channel: = Channel: Choose PPSSPP save folder = Choose PPSSPP save folder Confirm Overwrite = Do you want to overwrite the data? @@ -324,7 +333,7 @@ ConnectingPleaseWait = Connecting.\nPlease wait... ConnectionName = Connection name Corrupted Data = Corrupted data Delete = Poista -Delete all = Delete all +Delete all = Poista kaikki Delete completed = Poisto onnistui. DeleteConfirm = Tämä tallennus poistetaan.\nHaluatko varmasti jatkaa? DeleteConfirmAll = Do you really want to delete all\nyour save data for this game? @@ -361,6 +370,7 @@ Move Up = Move Up Network Connection = Network Connection NEW DATA = NEW DATA No = Ei +None = None ObtainingIP = Obtaining IP address.\nPlease wait... OK = OK Old savedata detected = Old savedata detected @@ -369,13 +379,14 @@ Password = Password Remove = Remove Reset = Reset Resize = Resize +Restart = Restart Retry = Retry Save = Save Save completed = Tallennus onnistui. Saving = Tallentaa\nOdota... -SavingFailed = Unable to save data. +SavingFailed = Ei voitu tallentaa. Search = Search -seconds, 0:off = seconds, 0 = off +seconds, 0:off = sekunteissa, 0 = pois Select = Valitse Settings = Settings Shift = Shift @@ -388,8 +399,11 @@ Supported = Tuettu There is no data = Dataa ei ole. Toggle All = Toggle all Toggle List = Toggle list +Top Center = Top center +Top Left = Top left +Top Right = Top right Unsupported = Ei tuettu -Username = Username +Username = Käyttäjänimi When you save, it will load on a PSP, but not an older PPSSPP = When you save, it will load on a PSP, but not an older PPSSPP When you save, it will not work on outdated PSP Firmware anymore = When you save, it will not work on outdated PSP firmware anymore Yes = Kyllä @@ -469,7 +483,7 @@ Hong Kong = Hong Kong InstallData = Data install Japan = Japani Korea = Korea -MB = MB +MB = Mt One moment please... = Pieni hetki... Play = Pelaa Remove From Recent = Poista "äskettäin" listalta... @@ -482,9 +496,9 @@ Use UI background = Use UI background [Graphics] % of the void = % of the void % of viewport = % of viewport -%, 0:unlimited = %, 0 = unlimited -(supersampling) = (supersampling) -(upscaling) = (upscaling) +%, 0:unlimited = %, 0 = rajoittamaton +(supersampling) = (ylinäytteistys) +(upscaling) = (ylösskaalaus) 1x PSP = 1x PSP 2x = 2x 2x PSP = 2x PSP @@ -503,15 +517,15 @@ Use UI background = Use UI background 16x = 16x Aggressive = Aggressiivinen Alternative Speed = Vaihtoehtoinen nopeus -Alternative Speed 2 = Vaihtoehtoinen nopeus 2 (prosenteissa, 0 = rajoittamaton) +Alternative Speed 2 = Vaihtoehtoinen nopeus 2 Anisotropic Filtering = Anisotrooppisen suodatus -Antialiasing (MSAA) = Antialiasing (MSAA) +Antialiasing (MSAA) = Pehmentäminen (MSAA) Aspect Ratio = Kuvasuhde Auto = Automaattinen -Auto (1:1) = Auto (1:1) -Auto (same as Rendering) = Automaattinen (sama kuin renderöinti resoluutio) -Auto FrameSkip = Automaattinen ruudunohitus -Auto Max Quality = Auto Max Quality +Auto (1:1) = Automaattinen (1:1) +Auto (same as Rendering) = Automaattinen (sama kuin renderöintiresoluutio) +Auto FrameSkip = Automaattinen kuvanohitus +Auto Max Quality = Automaattinen maksimi laatu Auto Scaling = Automaattinen skaalaus Backend = Taustarajapinta Balanced = Tasapainotettu @@ -521,95 +535,96 @@ Buffer graphics commands (faster, input lag) = Buffer graphics commands (faster, BufferedRenderingRequired = Warning: This game is not compatible with "Skip buffer effects" Camera = Kamera Camera Device = Kameralaite -Cardboard Screen Size = Screen size (in % of the viewport) -Cardboard Screen X Shift = X shift (in % of the blank space) -Cardboard Screen Y Shift = Y shift (in % of the blank space) -Cardboard VR Settings = Google Cardboard VR settings +Cardboard Screen Size = Näyttökoko (näkymän koon prosentuaalinen osuus) +Cardboard Screen X Shift = X-siirtymä (tyhjän tilan prosentuaalinen osuus) +Cardboard Screen Y Shift = Y-siirtymä (tyhjän tilan prosentuaalinen osuus) +Cardboard VR Settings = Google Cardboard VR -asetukset Cheats = Huijaukset CPU Core = CPU core Debugging = Vian etsintä -DefaultCPUClockRequired = Warning: This game requires the CPU clock to be set to default. -Deposterize = Deposterize -Deposterize Tip = Fixes visual banding glitches in upscaled textures +DefaultCPUClockRequired = Varoitus: Tämä peli vaatii Prosessorin kellon olevan asetettu oletusarvoon. +Deposterize = Häiriöiden korjaus +Deposterize Tip = Korjaa visuaaliset viivamaiset häiriöt ylöskaalattujen tekstuurien yhteydessä. Device = Laite Direct3D 9 = Direct3D 9 Direct3D 11 = Direct3D 11 Disabled = Poistettu käytöstä -Display Layout && Effects = Display layout && effects -Display Resolution (HW scaler) = Display resolution (HW scaler) -Enable Cardboard VR = Enable Cardboard VR +Display Layout && Effects = Näyttöasettelu ja -efektit +Display Resolution (HW scaler) = Näyttöresoluutio (Laitteiston skaalaaja) +Enable Cardboard VR = Ota käyttöön Cardboard VR FPS = FPS -Frame Rate Control = Framerate control -Frame Skipping = Ruudun ohitus -Frame Skipping Type = Frame skipping type +Frame Rate Control = Kuvataajuuden hallinta +Frame Skipping = Kuvan ohitus +Frame Skipping Type = Kuvan ohituksen tyyppi FullScreen = Koko näyttö -Geometry shader culling = Geometry shader culling -GPUReadbackRequired = Warning: This game requires "Skip GPU Readbacks" to be set to Off. -Hack Settings = Hack settings (may cause glitches) -Hardware Tessellation = Hardware tessellation +Geometry shader culling = Geometria-varjostimen karsinta +GPUReadbackRequired = Varoitus: Tämä peli vaatii, että "Ohita näytönohjaimen lukemat" on pois päältä. +Hack Settings = Hakkerointiasetukset (saattaa aiheutaa virheitä) +Hardware Tessellation = Laitteistotessellaatio Hardware Transform = Laitteistomuunnos -hardware transform error - falling back to software = Hardware transform error, falling back to software -HardwareTessellation Tip = Uses hardware to make curves -High = High +hardware transform error - falling back to software = Laitteistomuunnosvirhe, palataan ohjelmistotilaan +HardwareTessellation Tip = Käyttää laitteistoa muodostaakseen käyriä +High = Korkea Hybrid = Hybridi -Hybrid + Bicubic = Hybrid + Bicubic -Ignore camera notch when centering = Ignore camera notch when centering -Integer scale factor = Integer scale factor -Internal Resolution = Internal resolution -Lazy texture caching = Lazy texture caching (speedup) -Lazy texture caching Tip = Faster, but can cause text problems in a few games +Hybrid + Bicubic = Hybridi + Bicubic +Ignore camera notch when centering = Ohita kameran lovi keskittäessä +Integer scale factor = Kokonaislukuskaalauksen kerroin +Internal Resolution = Sisäinen resoluutio +Lazy texture caching = Laiska tekstuurivarastointi (nopeutus) +Lazy texture caching Tip = Nopeampi, mutta voi aiheuttaa tekstiongelmia joissakin peleissä Linear = Lineaarinen -Low = Low -LowCurves = Spline/Bezier curves quality -LowCurves Tip = Only used by some games, controls smoothness of curves -Lower resolution for effects (reduces artifacts) = Lower resolution for effects (reduces artifacts) -Manual Scaling = Manual scaling -Medium = Medium +Low = Matala +LowCurves = Spline/Bezier-käyrien laatu +LowCurves Tip = Käytetään vain joissakin peleissä, ohjaa käyrien sileyttä +Lower resolution for effects (reduces artifacts) = Alempi resoluutio efekteille (vähentää häiriöitä) +Manual Scaling = Manuaalinen skaalaus +Medium = Keskitaso Mode = Mode -Must Restart = You must restart PPSSPP for this change to take effect. -Native device resolution = Native device resolution +Must Restart = Sinun täytyy käynnistää PPSSPP uudelleen, jotta tämä muutos astuisi voimaan. +Native device resolution = Laiteen alkuperäinen resoluutio Nearest = Lähin -No buffer = No buffer +No buffer = Ei puskuria Show Battery % = Näytä akku % Show Speed = Näytä nopeus -Skip Buffer Effects = Skip buffer effects +Skip Buffer Effects = Ohita puskuriefektit None = Ei mitään -Number of Frames = Number of frames +Number of Frames = Kuvien määrä Off = Pois OpenGL = OpenGL Overlay Information = Overlay information -Partial Stretch = Partial stretch -Percent of FPS = Percent of FPS -Performance = Performance -Postprocessing shaders = Postprocessing shaders +Partial Stretch = Osittainen venytys +Percent of FPS = Kuvataajuden prosentti +Performance = Suorituskyky +Postprocessing shaders = Jälkikäsittelyvarjostimet Recreate Activity = Recreate activity -Render duplicate frames to 60hz = Render duplicate frames to 60 Hz -RenderDuplicateFrames Tip = Can make framerate smoother in games that run at lower framerates +Render duplicate frames to 60hz = Renderöi kuvat tuplana 60 Hz:siin +RenderDuplicateFrames Tip = Saattaa tehdä kuvataajuudesta tasaisemman peleissä, jotka toimivat matalammalla kuvataajuudella Rendering Mode = Renderöinti tapa Rendering Resolution = Renderöinti resoluutio -RenderingMode NonBuffered Tip = Nopeampi, mutta mitään ei välttämättä piirry joissakin peleissä +RenderingMode NonBuffered Tip = Nopeampi, mutta kaikki ei välttämättä piirry joissakin peleissä Rotation = Kierto Safe = Turvallinen Screen Scaling Filter = Näytön skaalauksen suodatin Show Debug Statistics = Näytä virheenkorjaustilastot -Show FPS Counter = Näytä FPS-laskuri -Skip GPU Readbacks = Skip GPU Readbacks +Show FPS Counter = Näytä kuvalaskuri (FPS) +Skip GPU Readbacks = Ohita GPU-lukemat Software Rendering = Ohjelmistopohjainen renderointi (kokeellinen) -Software Skinning = Software skinning -SoftwareSkinning Tip = Combine skinned model draws on the CPU, faster in most games +Software Skinning = Ohjelmistopohjainen muokkaus (skinning) +SoftwareSkinning Tip = Yhdistää skinnattujen mallien piirtämisen prosessorilla, nopeampi useimmissa peleissä Speed = Nopeus -Speed Hacks = Nopeus kikkoja (saattaa aiheuttaa renderöinti virheitä!) +Speed Hacks = Nopeuskikat (saattaa aiheuttaa renderöinti virheitä!) Stereo display shader = Stereo display shader -Stereo rendering = Stereo renderöinti +Stereo rendering = Stereorenderöinti Stretch = Venytä Texture Filter = Tekstuurisuodatin Texture Filtering = Tekstuurin suodatus +Texture replacement pack activated = Tekstuurin korvauspaketti aktivoidu Texture Scaling = Tekstuurin skaalaus Texture Shader = Tekstuurin varjostin -Turn off Hardware Tessellation - unsupported = Turn off "hardware tessellation": unsupported +Turn off Hardware Tessellation - unsupported = Ota pois käytöstä "laitteistotessellaatio": ei tuettu Unlimited = Rajoittamaton -Up to 1 = 1 asti -Up to 2 = 2 asti +Up to 1 = Enintään 1 +Up to 2 = Enintään 2 Upscale Level = Ylösskaalauksen taso Upscale Type = Ylösskaalauksen tapa UpscaleLevel Tip = Prosessorille raskas - joillekkin skaalauksille voidaan antaa viivettä nykimisen välttämiseksi @@ -632,19 +647,19 @@ Zip archive corrupt = ZIP-arkisto on korruptoitunut Zip file does not contain PSP software = ZIP-tiedosto ei sisällä PSP-ohjelmistoa [KeyMapping] -Allow combo mappings = Allow combo mappings -Autoconfigure = Auto configure -Autoconfigure for device = Autoconfigure for device -Bind All = Bind All -Clear All = Clear all -Combo mappings are not enabled = Combo mappings are not enabled -Default All = Restore defaults -Map a new key for = Map a new key for -Map Key = Map key -Map Mouse = Map mouse -Replace = Replace -Show PSP = Show PSP -You can press ESC to cancel. = You can press Esc to cancel. +Allow combo mappings = Salli yhdistelmäkartoitukset +Autoconfigure = Automaattinen määritys +Autoconfigure for device = Automaattinen määritys laitteelle +Bind All = Kartoita kaikki +Clear All = Tyhjennä kaikki +Combo mappings are not enabled = Yhdistelmäkuvaukset eivät ole käytössä +Default All = Palauta oletusasetukset +Map a new key for = Kartoita uusi näppäin kohteelle +Map Key = Kartoita näppäin +Map Mouse = Kartoita hiiri +Replace = Korvaa +Show PSP = Näytä PSP +You can press ESC to cancel. = Voit peruuttaa painamalla Esc. [MainMenu] Browse = Selaa... @@ -663,12 +678,12 @@ Load = Lataa... Loading... = Ladataan... PinPath = PinPath PPSSPP can't load games or save right now = PPSSPP ei voi ladata pelejä tai tallentaa juuri nyt -Recent = Äskettäin +Recent = Viimeisimmät SavesAreTemporary = PPSSPP tallentaa tilapäiseen tallennustilaan SavesAreTemporaryGuidance = Pura PPSSPP jonnekkin tallentaaksesi pysyvästi SavesAreTemporaryIgnore = Jätä varoitus huomiotta -UnpinPath = Unpin -UseBrowseOrLoad = Use Browse to choose a folder, or Load to choose a file. +UnpinPath = Poista kiinnitetty polku +UseBrowseOrLoad = Käytä Selaa valitaksesi kansio tai Lataa valitaksesi tiedosto. www.ppsspp.org = www.ppsspp.org [MainSettings] @@ -681,119 +696,114 @@ System = Järjestelmä Tools = Työkalut [MappableControls] -Alt speed 1 = Alt speed 1 -Alt speed 2 = Alt speed 2 -An.Down = Analog Down -An.Left = Analog Left -An.Right = Analog Right -An.Up = Analog Up -Analog limiter = Analog limiter -Analog speed = Analog speed -Analog Stick = Analog stick -Audio/Video Recording = Audio/Video recording -AxisSwap = Axis swap -Circle = Circle -Cross = Cross -Custom %d = Custom %d -D-pad down = D-pad down -D-pad left = D-pad left -D-pad right = D-pad right -D-pad up = D-pad up -DevMenu = DevMenu -Display Landscape = Display Landscape -Display Landscape Reversed = Display Landscape Reversed -Display Portrait = Display Portrait -Display Portrait Reversed = Display Portrait Reversed +Alt speed 1 = Vaihtoehtoinen nopeus 1 +Alt speed 2 = Vaihtoehtoinen nopeus 2 +An.Down = Analoginen alas +An.Left = Analoginen vasen +An.Right = Analoginen oikea +An.Up = Analoginen ylös +Analog limiter = Analoginen rajoitin +Analog speed = Analoginen nopeus +Analog Stick = Analoginen sauva +Audio/Video Recording = Ääni/video nauhoitus +AxisSwap = Akselinvaihto +Circle = Ympyrä +Cross = Risti +Custom %d = Mukautettu %d +D-pad down = Ristiohjain alas +D-pad left = Ristiohjain vasen +D-pad right = Ristiohjain oikea +D-pad up = Ristiohjain ylös +DevMenu = Kehittäjävalikko +Display Landscape = Näytä vaakakuva +Display Landscape Reversed = Näytä vaakakuva käänteisenä +Display Portrait = Näytä pystykuva +Display Portrait Reversed = Näytä pystykuva käänteisenä Double tap button = Double tap button -Down = Dpad Down -Dpad = Dpad -Frame Advance = Frame advance -Hold = Hold +Down = Alas +Dpad = Ristiohjain +Frame Advance = Kuva eteenpäin +Hold = Pidä Home = Home L = L -Left = Dpad Left -Load State = Load State +Left = Vasemalle +Load State = Lataa tila Mute toggle = Mute toggle -Next Slot = Next Slot -None = None +Next Slot = Seuraava lohko +None = Ei mitään Note = Note -OpenChat = Open chat +OpenChat = Avaa keskustelu Pause = Pause R = R -RapidFire = Rapid-fire -Record = Record +RapidFire = Nopea tulitus +Record = Nauhoita Remote hold = Remote hold -Rewind = Rewind -Right = Dpad Right -Right Analog Stick = Right Analog Stick -RightAn.Down = RightAn.Down -RightAn.Left = RightAn.Left -RightAn.Right = RightAn.Right -RightAn.Up = RightAn.Up -Rotate Analog (CCW) = Rotate analog (CCW) -Rotate Analog (CW) = Rotate analog (CW) -Save State = Save State -Screen = Screen -Screenshot = Screenshot +Rewind = Takaisinkelaus +Right = Oikealle +Right Analog Stick = Oikea analoginen sauva +RightAn.Down = OikeaAn.Alas +RightAn.Left = OikeaAn.Vasen +RightAn.Right = OikeaAn.Oikea +RightAn.Up = OikeaAn.Ylös +Rotate Analog (CCW) = Käännä analogista vastapäivään +Rotate Analog (CW) = Käännä analogista myötäpäivään +Save State = Tallena tila +Screen = Näyttö +Screenshot = Kuvakaappaus Select = Select -SpeedToggle = Speed toggle -Square = Square +SpeedToggle = Nopeuden vaihto +Square = Neliö Start = Start -Swipe Down = Swipe Down -Swipe Left = Swipe Left -Swipe Right = Swipe Right -Swipe Up = Swipe Up -tap to customize = tap to customize -Texture Dumping = Texture dumping -Texture Replacement = Texture replacement -Toggle Fullscreen = Toggle fullscreen +Swipe Down = Pyyhkäise alas +Swipe Left = Pyyhkäise vasemmalle +Swipe Right = Pyyhkäise oikealle +Swipe Up = Pyyhkäise ylös +tap to customize = kosketa mukauttaaksesi +Texture Dumping = Tekstuurien tallentaminen +Texture Replacement = Tekstuurien korvaus +Toggle Fullscreen = Vaihda koko ruudun tilan välillä Toggle mode = Toggle mode -Triangle = Triangle -Fast-forward = Fast-forward -Up = Dpad Up +Triangle = Kolmio +Fast-forward = Pikakelaus +Up = Ylös Vol + = Vol + Vol - = Vol - Wlan = WLAN [MemStick] -Already contains PSP data = Already contains PSP data -Create or Choose a PSP folder = Create or Choose a PSP folder -Current = Current -DataCanBeShared = Data can be shared between PPSSPP regular/Gold -DataCannotBeShared = Data CANNOT be shared between PPSSPP regular/Gold! -DataWillBeLostOnUninstall = Warning! Data will be lost when you uninstall PPSSPP! -DataWillStay = Data will stay even if you uninstall PPSSPP. -Done! = Done! -EasyUSBAccess = Easy USB access -Failed to move some files! = Failed to move some files! -Failed to save config = Failed to save config -Free space = Free space -Manually specify PSP folder = Manually specify PSP folder -MemoryStickDescription = Choose where to keep PSP data (Memory Stick) -Move Data = Move Data +Already contains PSP data = Sisältää jo PSP-tiedot +Create or Choose a PSP folder = Luo tai valitse PSP-kansio +Current = Nykyinen +DataCanBeShared = Tiedot voidaan jakaa PPSSPP:n tavallisen / Gold-version välillä +DataCannotBeShared = Tietoja EI VOIDA jakaan PPSSPP:n tavallisen / Gold-version välillä! +DataWillBeLostOnUninstall = Varoitus! Tiedot häviävät, kun poistat PPSSPP:n! +DataWillStay = Tiedot säilyvät jopa jos poistat PPSSPP:n. +Done! = Valmis! +EasyUSBAccess = Helppo USB-pääsy +Failed to move some files! = Joitain tiedostoja ei voitu siirtää! +Failed to save config = Asetusten tallentaminen epäonnistui +Free space = Vapaa tila +Manually specify PSP folder = Määritä PSP-kansio manuaalisesti +MemoryStickDescription = Valitse minne haluat säilyttää PSP-tiedot (muistikortti) +Move Data = Siirrä tiedot Selected PSP Data Folder = Selected PSP Data Folder -No data will be changed = No data will be changed -PPSSPP will restart after the change = PPSSPP will restart after the change -Skip for now = Skip for now -Starting move... = Starting move... -That folder doesn't work as a memstick folder. = That folder doesn't work as a memstick folder. -USBAccessThrough = USB access through Android/data/org.ppsspp.ppsspp/files -USBAccessThroughGold = USB access through Android/data/org.ppsspp.ppssppgold/files -Use App Private Data = Use App Private Data -Use PSP folder at root of storage = Use PSP folder at root of storage -Welcome to PPSSPP! = Welcome to PPSSPP! -WhatsThis = What's this? +No data will be changed = Mitään tietoja ei muuteta +PPSSPP will restart after the change = PPSSPP käynnistyy uudelleen muutoksen jälkeen +Skip for now = Ohita nyt +Starting move... = Siirto alkaa... +That folder doesn't work as a memstick folder. = Kyseinen kansio ei toimi muistikorttikansiona. +USBAccessThrough = USB-pääsy Android/data/org.ppsspp.ppsspp/files -kautta +USBAccessThroughGold = USB-pääsy Android/data/org.ppsspp.ppssppgold/files -kautta +Use App Private Data = Käytä sovelluksen yksityistä dataa +Use PSP folder at root of storage = Käytä PSP-kansiota tallennustilan juuressa +Welcome to PPSSPP! = Tervetuloa PPSSPP:n! +WhatsThis = Mikä tämä on? [Networking] AdHoc Server = Ad hoc server AdhocServer Failed to Bind Port = Ad hoc server failed to bind port AM: Data from Unknown Port = AM: Data from Unknown Port Auto = Auto -Bottom Center = Bottom center -Bottom Left = Bottom left -Bottom Right = Bottom right -Center Left = Center left -Center Right = Center right Change Mac Address = Change MAC address Change proAdhocServer Address = Change PRO ad hoc server IP address (localhost = multiple instances) ChangeMacSaveConfirm = Generate a new MAC address? @@ -827,7 +837,6 @@ Invalid IP or hostname = Invalid IP or hostname Minimum Timeout = Minimum timeout (override in ms, 0 = default) Misc = Miscellaneous (default = PSP compatibility) Network Initialized = Network initialized -None = None Please change your Port Offset = Please change your port offset Port offset = Port offset(0 = PSP compatibility) Open PPSSPP Multiplayer Wiki Page = Open PPSSPP Multiplayer Wiki Page @@ -838,11 +847,9 @@ Quick Chat 3 = Quick chat 3 Quick Chat 4 = Quick chat 4 Quick Chat 5 = Quick chat 5 QuickChat = Quick chat +Randomize = Randomize Send = Send Send Discord Presence information = Send Discord "Rich Presence" information -Top Center = Top center -Top Left = Top left -Top Right = Top right Unable to find UPnP device = Unable to find UPnP device UPnP (port-forwarding) = UPnP (port forwarding) UPnP need to be reinitialized = UPnP need to be reinitialized @@ -852,20 +859,37 @@ Validating address... = Validating address... WLAN Channel = WLAN channel You're in Offline Mode, go to lobby or online hall = You're in offline mode, go to lobby or online hall +[PSPSettings] +Auto = Automaattinen +Chinese (simplified) = Kiina (yksinkertaistettu) +Chinese (traditional) = Kiina (perinteinen) +Dutch = Hollanti +English = Englanti +French = Ranska +Game language = Pelin kieli +German = Saksa +Italian = Italia +Japanese = Japani +Korean = Korea +Games often don't support all languages = Pelit eivät usein tue kaikkia kieliä +Portuguese = Portugali +Russian = Venäjä +Spanish = Espanja + [Pause] Cheats = Huijaukset Continue = Jatka -Create Game Config = Create game config -Delete Game Config = Delete game config -Exit to menu = Exit to menu -Game Settings = Game settings -Load State = Lataa tilanne -Rewind = Rewind -Save State = Tallenna tilanne +Create Game Config = Luo pelin asetustiedosto +Delete Game Config = Poista pelin asetustiedosto +Exit to menu = Poistu valikkoon +Game Settings = Pelin asetukset +Load State = Lataa tila +Rewind = Takaisinkelaus +Save State = Tallenna tila Settings = Asetukset -Switch UMD = Switch UMD -Undo last load = Undo last load -Undo last save = Undo last save +Switch UMD = Vaihda UMD +Undo last load = Peru viimeisin tilan lataus +Undo last save = Peru viimeisin tilan tallennus [PostShaders] (duplicated setting, previous slider will be used) = (duplicated setting, previous slider will be used) @@ -910,64 +934,64 @@ VideoSmoothingAA = VideoSmoothingAA Vignette = Vignette [PSPCredits] -all the forum mods = all the forum mods -build server = build server -Buy Gold = Buy Gold -check = Also check out Dolphin, the best Wii/GC emu around: -CheckOutPPSSPP = Check out PPSSPP, the awesome PSP emulator: https://www.ppsspp.org/ -contributors = Contributors: +all the forum mods = kaikki foorumin moderaattorit +build server = koontipalvelin +Buy Gold = Osta Gold +check = Tutustu myös Dolphiniin, parhaaseen Wii/GC-emulaattoriin: +CheckOutPPSSPP = Tutustu PPSSPP:ään, mahtavaan PSP-emulaattoriin: https://www.ppsspp.org/ +contributors = Osallistujat: created = Luonut Discord = Discord -info1 = PPSSPP is intended for educational purposes only. -info2 = Please make sure that you own the rights to any games -info3 = you play by owning the UMD or by buying the digital -info4 = download from the PSN store on your real PSP. -info5 = PSP is a trademark by Sony, Inc. -iOS builds = iOS builds -license = Ilmainen ohjelmisto GPL 2.0+ -list = compatibility lists, forums, and development info -PPSSPP Forums = PPSSPP Forums -Privacy Policy = Privacy policy -Share PPSSPP = Share PPSSPP -specialthanks = Special thanks to: -specialthanksKeithGalocy = at NVIDIA (hardware, advice) -specialthanksMaxim = for his amazing Atrac3+ decoder work -testing = testing -this translation by = this translation by: -title = A fast and portable PSP emulator -tools = Free tools used: +info1 = PPSSPP on tarkoitettu vain opetustarkoituksiin. +info2 = Varmista, että omistat oikeudet kaikkiin peleihin, +info3 = joita pelaat omistamalla UMD:n tai ostamalla digitaalisen +info4 = latauksen PSN-kaupasta oikealla PSP:lläsi. +info5 = PSP on Sony, Inc:n tavaramerkki. +iOS builds = iOS buildit +license = Ilmainen ohjelmisto, GPL 2.0+ +list = yhteensopivuuslistat, foorumit ja kehitystiedot +PPSSPP Forums = PPSSPP-foorumit +Privacy Policy = Tietosuojakäytäntö +Share PPSSPP = Jaa PPSSPP +specialthanks = Erityiskiitokset: +specialthanksKeithGalocy = NVIDIA:lle (laitteisto, neuvot) +specialthanksMaxim = hänen uskomattomasta Atrac3+ -dekooderityöstään +testing = testaus +this translation by = tämän käännöksen teki: +title = Nopea ja helposti siirrettävä PSP-emulaattori +tools = Vapaita käytettyjä työkaluja: # Add translators or contributors who translated PPSSPP into your language here. # Add translators1-6 for up to 6 lines of translator credits. # Leave extra lines blank. 4 contributors per line seems to look best. -translators1 = +translators1 = Jaakko Saarikko translators2 = translators3 = translators4 = translators5 = translators6 = Twitter @PPSSPP_emu = Twitter @PPSSPP_emu -website = Check out the website: -written = Written in C++ for speed and portability +website = Tutustu verkkosivustoon: +written = Kirjoitettu C++:ssa nopeutta ja siirrettävyyttä varten [RemoteISO] -Browse Games = Browse games -Local Server Port = Local server port -Manual Mode Client = Manual mode client -Remote disc streaming = Remote disc streaming -Remote Port = Remote port -Remote Server = Remote server -Remote Subdirectory = Remote subdirectory -RemoteISODesc = Games in your recent list will be shared -RemoteISOLoading = Connected, loading game list... -RemoteISOScanning = Scanning... Click "share games" on your server device -RemoteISOScanningTimeout = Scanning... check your desktop's firewall settings -RemoteISOWifi = Note: Connect both devices to the same Wi-Fi network -RemoteISOWinFirewall = WARNING: Windows Firewall is blocking sharing -Settings = Settings -Share Games (Server) = Share games (server) -Share on PPSSPP startup = Share on PPSSPP startup -Stop Sharing = Stop sharing -Stopping.. = Stopping... +Browse Games = Selaa pelejä +Local Server Port = Paikallisen palvelimen portti +Manual Mode Client = Käsikäyttötilan asiakas +Remote disc streaming = Kauko-levyjen suoratoisto +Remote Port = Kaukoportti +Remote Server = Kaukopavelin +Remote Subdirectory = Kaukokansio +RemoteISODesc = Viimeksi käytettyjen pelien lista jaetaan +RemoteISOLoading = Yhdistetty, ladataan pelilistaa... +RemoteISOScanning = Etsitään... Valitse "jaa pelit" palvelinlaitteellasi +RemoteISOScanningTimeout = Etsitään... tarkista työpöydän palomuurin asetukset +RemoteISOWifi = Huomaa: Yhdistä molemmat laitteet samaan Wi-Fi-verkkoon +RemoteISOWinFirewall = VAROITUS: Windowsin palomuuri estää jakamisen +Settings = Asetukset +Share Games (Server) = Jaa pelejä (palvelin) +Share on PPSSPP startup = Jaa käynnistyksen yhteydessä +Stop Sharing = Lopeta jakaminen +Stopping.. = Lopetetaan... [Reporting] Bad = Bad @@ -1013,16 +1037,16 @@ Unselected Overall Description = How well does this game emulate? View Feedback = View all feedbacks [Savedata] -Date = Date -Filename = Filename -No screenshot = No screenshot -None yet. Things will appear here after you save. = None yet. Things will appear here after you save. -Nothing matching '%1' was found. = Nothing matching '%1' was found. -Save Data = Save data -Save States = Save states -Savedata Manager = Savedata manager -Showing matches for '%1'. = Showing matches for '%1'. -Size = Size +Date = Päivämäärä +Filename = Tiedostonimi +No screenshot = Ei kuvakaappausta +None yet. Things will appear here after you save. = Ei vielä. Tähän ilmestyy asioita tallentamisen jälkeen. +Nothing matching '%1' was found. = Ei tuloksia haulla '%1'. +Save Data = Tallennustiedosto +Save States = Tilatallennukset +Savedata Manager = Tallennustiedoston hallinta +Showing matches for '%1'. = Näytetään tulokset haulle '%1'. +Size = Koko [Screen] Cardboard VR OFF = Cardboard VR off @@ -1054,22 +1078,22 @@ State load undone = State load undone Untitled PSP game = Untitled PSP game [Search] -Clear filter = Clear filter -Filter = Filter -Filtering settings by '%1' = Filtering settings by '%1' -Find settings = Find settings -No settings matched '%1' = No settings matched '%1' -Search term = Search term +Clear filter = Tyhjennä suodatin +Filter = Suodata +Filtering settings by '%1' = Suodatetaan asetuksia hakusanalla '%1' +Find settings = Etsi asetuksia +No settings matched '%1' = Ei löytynyt asetuksia hakusanalla '%1' +Search term = Hakusana [Store] -Already Installed = Already installed -Connection Error = Connection error -Install = Install -Launch Game = Launch game -Loading... = Loading... -MB = MB -Size = Size -Uninstall = Uninstall +Already Installed = Jo asennettu +Connection Error = Yhteysvirhe +Install = Asenna +Launch Game = Käynnistä peli +Loading... = Ladataan... +MB = Mt +Size = Koko +Uninstall = Poista asennus [SysInfo] %0.2f Hz = %0.2f Hz @@ -1140,29 +1164,29 @@ Vulkan Extensions = Vulkan extensions Vulkan Features = Vulkan features [System] -(broken) = (broken) +(broken) = (rikki) 12HR = 12HR 24HR = 24HR -Auto = Auto -Auto Load Savestate = Auto load savestate -AVI Dump started. = AVI dump started -AVI Dump stopped. = AVI dump stopped -Cache ISO in RAM = Cache full ISO in RAM -Change CPU Clock = Change emulated PSP's CPU clock (unstable) -Color Saturation = Color Saturation -Color Tint = Color Tint +Auto = Automaattinen +Auto Load Savestate = Lataa tilatallennus automaattisesti +AVI Dump started. = AVI-tallennus aloitettu. +AVI Dump stopped. = AVI-tallennus lopetettu. +Cache ISO in RAM = Lataa ISO-tiedosto RAM-muistiin +Change CPU Clock = Muuta emuloidun PSP:n näytönohjaimen kelloa (epävakaa) +Color Saturation = Värisaturaatio +Color Tint = Värin sävy Error: load undo state is from a different game = Error: load undo state is from a different game Failed to load state for load undo. Error in the file system. = Failed to load state for load undo. Error in the file system. Floating symbols = Floating symbols -Game crashed = Game crashed -Language = Language -Memory Stick folder = Memory Stick folder -Memory Stick size = Memory Stick size +Game crashed = Peli kaatui +Language = Kieli +Memory Stick folder = Muistikortin kansio +Memory Stick size = Muistikortin koko Change Nickname = Vaihda nimimerkkiä ChangingMemstickPath = Save games, save states, and other data will not be copied to this folder.\n\nChange the Memory Stick folder? ChangingMemstickPathInvalid = That path couldn't be used to save Memory Stick files. -Cheats = Cheats -Clear Recent = Clear "Recent" +Cheats = Huijaukset +Clear Recent = Tyhjennä "Viimeisimmät" Clear Recent Games List = Clear Recent games list Clear UI background = Clear UI background Confirmation Button = Confirmation button @@ -1176,92 +1200,92 @@ Display Games on a grid = Display "Games" on a grid Display Homebrew on a grid = Display "Homebrew && Demos" on a grid Display Recent on a grid = Display "Recent" on a grid Dynarec (JIT) = Dynarec (JIT) -Emulation = Emulation +Emulation = Emulaatio Enable Cheats = Ota huijaukset käyttöön Enable Compatibility Server Reports = Ota yhteensopivat palvelinilmoitukset käyttöön -Failed to load state. Error in the file system. = Failed to load state. Error in the file system. -Failed to save state. Error in the file system. = Failed to save state. Error in the file system. -Fast (lag on slow storage) = Fast (lag on slow storage) -Fast Memory = Nopea muisti (epävakaa) -Force real clock sync (slower, less lag) = Force real clock sync (slower, less lag) -Games list settings = Games list settings -General = General -Grid icon size = Grid icon size -Help the PPSSPP team = Help the PPSSPP team -Host (bugs, less lag) = Host (bugs, less lag) -Ignore bad memory accesses = Ignore bad memory accesses -Increase size = Increase size -Interpreter = Interpreter -IO timing method = I/O timing method -IR Interpreter = IR interpreter -Memory Stick Folder = Memory Stick folder -Memory Stick inserted = Memory Stick inserted -MHz, 0:default = MHz, 0 = default -MMDDYYYY = MMDDYYYY -Moving background = Moving background -Newest Save = Newest save -No animation = No animation -Not a PSP game = Not a PSP game -Off = Off -Oldest Save = Oldest save -Path does not exist! = Path does not exist! -PSP Memory Stick = PSP Memory Stick -PSP Model = PSP model -PSP Settings = PSP settings +Failed to load state. Error in the file system. = Tilatallennuksen lataus epäonnistui. Virhe tiedostojärjestelmässä. +Failed to save state. Error in the file system. = Tilatallennuksen luonti epäonnistui. Virhe tiedostojärjestelmässä. +Fast (lag on slow storage) = Nopea (viive hidas tallennustilassa) +Fast Memory = Nopea muisti +Force real clock sync (slower, less lag) = Pakota todellinen kellon synkronointi (hidastaa, vähemmän viivettä) +Games list settings = Peliluettelon asetukset +General = Yleiset +Grid icon size = Ruudukon kuvakkeen koko +Help the PPSSPP team = Auta PPSSPP-tiimiä +Host (bugs, less lag) = Isäntä (bugit, vähemmän viivettä) +Ignore bad memory accesses = Ohita huonot muistipääsyt +Increase size = Kasvata kokoa +Interpreter = Tulkki +IO timing method = I/O-aikamenetelmä +IR Interpreter = IR-tulkki +Memory Stick Folder = Muistikortin kansio +Memory Stick inserted = Muistikortin asetettu +MHz, 0:default = MHz, 0 = oletus +MMDDYYYY = KKPPVVVV +Moving background = Liikkuva tausta +Newest Save = Uusin tallennus +No animation = Ei animaatiota +Not a PSP game = Ei PSP-peli +Off = Pois +Oldest Save = Vanhin tallennus +Path does not exist! = Polkua ei ole olemassa! +PSP Memory Stick = PSP-muistikortti +PSP Model = PSP-malli +PSP Settings = PSP-asetukset PSP-1000 = PSP-1000 PSP-2000/3000 = PSP-2000/3000 -Recent games = Recent games -Record Audio = Record audio -Record Display = Record display -Reset Recording on Save/Load State = Reset recording on Save/Load state -Restore Default Settings = Restore PPSSPP's settings to default -Rewind Snapshot Interval = Rewind Snapshot Interval (mem hog) -Save path in installed.txt = Save path in installed.txt -Save path in My Documents = Save path in My Documents -Savestate Slot = Savestate slot -Savestate slot backups = Savestate slot backups -Screenshots as PNG = Save screenshots in PNG format -Set Memory Stick folder = Set Memory Stick folder -Set UI background... = Set UI background... -Show ID = Show ID -Show Memory Stick folder = Show Memory Stick folder -Show region flag = Show region flag -Simulate UMD delays = Simulate UMD delays -Slot 1 = Slot 1 -Slot 2 = Slot 2 -Slot 3 = Slot 3 -Slot 4 = Slot 4 -Slot 5 = Slot 5 -Storage full = Storage full -Sustained performance mode = Sustained performance mode -Theme = Theme -Time Format = Time format -Transparent UI background = Transparent UI background -UI = UI -UI background animation = UI background animation -UI Sound = UI sound -undo %c = backup %c +Recent games = Viimeisimmät pelit +Record Audio = Nauhoita ääntä +Record Display = Nauhoita näyttöä +Reset Recording on Save/Load State = Nollaa nauhoitus tallennettaessa/ladattaessa tila +Restore Default Settings = Palauta PPSSPP:n oletusasetukset +Rewind Snapshot Interval = Pikakelaa tilannevedosten välit (muistisyöppö) +Save path in installed.txt = Tallennuskansio installed.txt-tiedostossa +Save path in My Documents = Tallennuskansio Omat tiedostot -kansiossa +Savestate Slot = Tilatallennuksen lohko +Savestate slot backups = Tallennustilan paikan varmuuskopiot +Screenshots as PNG = Tallenna kuvankaappaukset PNG-muodossa +Set Memory Stick folder = Aseta muistikortin kansio +Set UI background... = Aseta käyttöliittymän tausta... +Show ID = Näytä tunniste +Show Memory Stick folder = Näytä muistikortin kansio +Show region flag = Näytä alueen lippu +Simulate UMD delays = Simuloi UMD-viiveet +Slot 1 = Lohko 1 +Slot 2 = Lohko 2 +Slot 3 = Lohko 3 +Slot 4 = Lohko 4 +Slot 5 = Lohko 5 +Storage full = Tallennustila täynnä +Sustained performance mode = Jatkuva suorituskykytila +Theme = Teema +Time Format = Ajan muoto +Transparent UI background = Läpinäkyvä käyttöliittymän tausta +UI = Käyttöliittymä +UI background animation = Käyttöliittymän taustan animaatio +UI Sound = Käyttöliittymän äänet +undo %c = peruuta %c USB = USB -Use Lossless Video Codec (FFV1) = Use lossless video codec (FFV1) -Use O to confirm = Use O as confirmation button +Use Lossless Video Codec (FFV1) = Käytä häviöttömän videon koodekkia (FFV1) +Use O to confirm = Käytä O-näppäintä vahvistukseen Use output buffer (with overlay) for recording = Use output buffer (with overlay) for recording -Use system native keyboard = Use system native keyboard -Use X to confirm = Use X as confirmation button -VersionCheck = Check for new versions of PPSSPP -WARNING: Android battery save mode is on = WARNING: Android battery save mode is On -WARNING: Battery save mode is on = WARNING: Battery save mode is on -Waves = Waves -YYYYMMDD = YYYYMMDD +Use system native keyboard = Käytä järjestelmän omaa näppäimistöä +Use X to confirm = Käytä X-näppäintä vahvistukseen +VersionCheck = Tarkista uudet PPSSPP-versiot +WARNING: Android battery save mode is on = VAROITUS: Androidin akun säästötila on käytössä +WARNING: Battery save mode is on = VAROITUS: Akun säästötila on käytössä +Waves = Aallot +YYYYMMDD = VVVVKKPP [TextureShaders] -Off = Off +Off = Pois TexMMPX = TexMMPX Tex2xBRZ = Tex2xBRZ Tex4xBRZ = Tex4xBRZ [Themes] -Dark = Dark -Default = Default +Dark = Tumma +Default = Oletus [UI Elements] %1 button = %1 button diff --git a/assets/lang/fr_FR.ini b/assets/lang/fr_FR.ini index efc59af1a6..9059c48b8d 100644 --- a/assets/lang/fr_FR.ini +++ b/assets/lang/fr_FR.ini @@ -6,12 +6,15 @@ Account = Account Achievement Unlocked = Achievement Unlocked Achievements = Achievements Achievements are disabled = Achievements are disabled +Can't log in to RetroAchievements right now = Can't log in to RetroAchievements right now Challenge Mode = Challenge Mode Challenge Mode (no savestates) = Challenge Mode (no savestates) +Contacting RetroAchievements server... = Contacting RetroAchievements server... Customize = Customize Earned = You have earned %d of %d achievements, and %d of %d points Encore Mode = Encore Mode How to use RetroAchievements = How to use RetroAchievements +In Encore mode - listings may be wrong below = In Encore mode - listings may be wrong below Leaderboard score submission = Leaderboard score submission Leaderboard submission is enabled = Leaderboard submission is enabled Leaderboards = Leaderboards @@ -88,6 +91,7 @@ Disable diagonal input = Disable diagonal input Double tap = Double tap Enable gesture control = Enable gesture control Enable standard shortcut keys = Enable standard shortcut keys +frames = frames Gesture = Gesture Gesture mapping = Gesture mapping Glowing borders = Bordures lumineuses @@ -116,6 +120,7 @@ OnScreen = Commandes tactiles Portrait = Portrait Portrait Reversed = Portrait inversé PSP Action Buttons = Boutons d'action +Rapid fire interval = Rapid fire interval Raw input = Raw input Repeat mode = Repeat mode Reset to defaults = Reset to defaults @@ -310,10 +315,14 @@ VFPU = VFPU * PSP res = × définition PSP Active = Actif Back = Retour +Bottom Center = En bas au centre +Bottom Left = En bas à gauche +Bottom Right = En bas à droite Cancel = Annuler Center = Centrer -ChangingGPUBackends = Changer de rendu back-end nécessite de\nredémarrer PPSSPP.\nRedémarrer maintenant ? -ChangingInflightFrames = Modifier la mise en mémoire tampon des commandes graphiques nécessite le redémarrage de PPSSPP. Redémarrer maintenant ? +Center Left = Au centre à gauche +Center Right = Au centre à droite +Changing this setting requires PPSSPP to restart. = La modification de ce paramètre nécessite le redémarrage de PPSSPP. Redémarrer maintenant ? Channel: = Canal : Choose PPSSPP save folder = Veuillez choisir le dossier de sauvegarde de PPSSPP. Confirm Overwrite = Voulez-vous écraser les données ? @@ -361,6 +370,7 @@ Move Up = Move Up Network Connection = Connexion réseau NEW DATA = NOUVELLES DONNÉES No = Non +None = None ObtainingIP = Obtention de l'adresse IP\nVeuillez patienter... OK = OK Old savedata detected = Ancienne sauvegarde détectée @@ -369,6 +379,7 @@ Password = Password Remove = Remove Reset = Remise à 0 Resize = Zoomer +Restart = Restart Retry = Réessayer Save = Sauvegarder Save completed = Sauvegarde terminée. @@ -388,6 +399,9 @@ Supported = Supporté There is no data = Aucune donnée trouvée. Toggle All = Tout cocher Toggle List = Afficher liste +Top Center = En haut au centre +Top Left = En haut à gauche +Top Right = En haut à droite Unsupported = Non supporté Username = Username When you save, it will load on a PSP, but not an older PPSSPP = Si vous sauvegardez, le chargement sera possible sur une PSP, mais pas sur une ancienne version de PPSSPP. @@ -604,6 +618,7 @@ Stereo rendering = Stereo rendering Stretch = Étirement Texture Filter = Filtre de textures Texture Filtering = Filtrage des textures +Texture replacement pack activated = Texture replacement pack activated Texture Scaling = Mise à l'échelle des textures Texture Shader = Shader de textures Turn off Hardware Tessellation - unsupported = Veuillez désactiver "Tessellation matérielle" : non supportée @@ -789,11 +804,6 @@ AdHoc Server = Serveur ad hoc AdhocServer Failed to Bind Port = Échec de liaison du port par le serveur ad hoc AM: Data from Unknown Port = AM: Data from Unknown Port Auto = Automatique -Bottom Center = En bas au centre -Bottom Left = En bas à gauche -Bottom Right = En bas à droite -Center Left = Au centre à gauche -Center Right = Au centre à droite Change Mac Address = Modifier l'adresse MAC Change proAdhocServer Address = Modifier l'adresse IP du serveur ad hoc (localhost = plusieurs instances) ChangeMacSaveConfirm = Generate a new MAC address? @@ -827,7 +837,6 @@ Invalid IP or hostname = IP ou nom de domaine invalide Minimum Timeout = Temps d'expiration minimum (forçage en ms, 0 = par défaut) Misc = Divers (par défaut = compatibilité PSP) Network Initialized = Réseau initialisé ! -None = None Please change your Port Offset = Veuillez changer votre décalage de port Port offset = Décalage de port (0 = compatibilité PSP) Open PPSSPP Multiplayer Wiki Page = Open PPSSPP Multiplayer Wiki Page @@ -838,11 +847,9 @@ Quick Chat 3 = Chat rapide 3 Quick Chat 4 = Chat rapide 4 Quick Chat 5 = Chat rapide 5 QuickChat = Chat rapide +Randomize = Randomize Send = Envoyer Send Discord Presence information = Envoyer l'information "Rich Presence" de Discord -Top Center = En haut au centre -Top Left = En haut à gauche -Top Right = En haut à droite Unable to find UPnP device = Appareil UPnP introuvable UPnP (port-forwarding) = UPnP (redirection de port) UPnP need to be reinitialized = UPnP doit être réinitialisé @@ -852,6 +859,23 @@ Validating address... = Validation de l'adresse... WLAN Channel = Canal WLAN You're in Offline Mode, go to lobby or online hall = Vous êtes en mode hors ligne, allez dans le hall en ligne. +[PSPSettings] +Auto = Auto +Chinese (simplified) = Chinese (simplified) +Chinese (traditional) = Chinese (traditional) +Dutch = Dutch +English = English +French = French +Game language = Game language +German = German +Italian = Italian +Japanese = Japanese +Korean = Korean +Games often don't support all languages = Games often don't support all languages +Portuguese = Portuguese +Russian = Russian +Spanish = Spanish + [Pause] Cheats = Codes de triche Continue = Continuer diff --git a/assets/lang/gl_ES.ini b/assets/lang/gl_ES.ini index dc1b1af2ec..12c814bc61 100644 --- a/assets/lang/gl_ES.ini +++ b/assets/lang/gl_ES.ini @@ -6,12 +6,15 @@ Account = Account Achievement Unlocked = Achievement Unlocked Achievements = Achievements Achievements are disabled = Achievements are disabled +Can't log in to RetroAchievements right now = Can't log in to RetroAchievements right now Challenge Mode = Challenge Mode Challenge Mode (no savestates) = Challenge Mode (no savestates) +Contacting RetroAchievements server... = Contacting RetroAchievements server... Customize = Customize Earned = You have earned %d of %d achievements, and %d of %d points Encore Mode = Encore Mode How to use RetroAchievements = How to use RetroAchievements +In Encore mode - listings may be wrong below = In Encore mode - listings may be wrong below Leaderboard score submission = Leaderboard score submission Leaderboard submission is enabled = Leaderboard submission is enabled Leaderboards = Leaderboards @@ -88,6 +91,7 @@ Disable diagonal input = Disable diagonal input Double tap = Double tap Enable gesture control = Enable gesture control Enable standard shortcut keys = Enable standard shortcut keys +frames = frames Gesture = Gesture Gesture mapping = Gesture mapping Glowing borders = Glowing borders @@ -116,6 +120,7 @@ OnScreen = Controis táctiles en pantalla Portrait = Vertical Portrait Reversed = Vertical invertido PSP Action Buttons = Botóns de acción +Rapid fire interval = Rapid fire interval Raw input = Raw input Repeat mode = Repeat mode Reset to defaults = Reset to defaults @@ -310,10 +315,14 @@ VFPU = VFPU * PSP res = * PSP res Active = Active Back = Atrás +Bottom Center = Bottom center +Bottom Left = Bottom left +Bottom Right = Bottom right Cancel = Cancelar Center = Center -ChangingGPUBackends = Cambiar o motor gráfico require reiniciar PPSSPP.\nReiniciar agora? -ChangingInflightFrames = Changing graphics command buffering requires PPSSPP to restart. Restart now? +Center Left = Center left +Center Right = Center right +Changing this setting requires PPSSPP to restart. = Changing this setting requires PPSSPP to restart. Channel: = Channel: Choose PPSSPP save folder = Elixe a carpeta de gardado de PPSSPP. Confirm Overwrite = Desexas sobreescribir os datos? @@ -361,6 +370,7 @@ Move Up = Move Up Network Connection = Conexión de rede NEW DATA = NOVOS DATOS No = Non +None = None ObtainingIP = Obtaining IP address.\nPlease wait... OK = Aceptar Old savedata detected = Detectados datos de gardado antigos. @@ -369,6 +379,7 @@ Password = Password Remove = Remove Reset = Reiniciar Resize = Tamaño +Restart = Restart Retry = Reintentar Save = Gardar Save completed = Gardado completado. @@ -388,6 +399,9 @@ Supported = Supported There is no data = A tarxeta de memoria está baleira. Toggle All = Cambiar todo Toggle List = Toggle list +Top Center = Top center +Top Left = Top left +Top Right = Top right Unsupported = Unsupported Username = Username When you save, it will load on a PSP, but not an older PPSSPP = Cando gardes, a partida cargará nunha PSP, pero non en versións antigas de PPSSPP. @@ -604,6 +618,7 @@ Stereo rendering = Stereo rendering Stretch = Stretch Texture Filter = Filtro de texturas Texture Filtering = Filtrado de texturas +Texture replacement pack activated = Texture replacement pack activated Texture Scaling = Escalado de texturas Texture Shader = Texture shader Turn off Hardware Tessellation - unsupported = Turn off "hardware tessellation": unsupported @@ -789,11 +804,6 @@ AdHoc Server = Ad hoc server AdhocServer Failed to Bind Port = Ad hoc server failed to bind port AM: Data from Unknown Port = AM: Data from Unknown Port Auto = Auto -Bottom Center = Bottom center -Bottom Left = Bottom left -Bottom Right = Bottom right -Center Left = Center left -Center Right = Center right Change Mac Address = Cambiar dirección MAC Change proAdhocServer Address = Cambiar IP de proAdhocServer (localhost = multiple instances) ChangeMacSaveConfirm = Generate a new MAC address? @@ -827,7 +837,6 @@ Invalid IP or hostname = Invalid IP or hostname Minimum Timeout = Minimum timeout (override in ms, 0 = default) Misc = Miscellaneous (default = PSP compatibility) Network Initialized = Rede inicializada -None = None Please change your Port Offset = Please change your port offset Port offset = Port offset(0 = PSP compatibility) Open PPSSPP Multiplayer Wiki Page = Open PPSSPP Multiplayer Wiki Page @@ -838,11 +847,9 @@ Quick Chat 3 = Quick chat 3 Quick Chat 4 = Quick chat 4 Quick Chat 5 = Quick chat 5 QuickChat = Quick chat +Randomize = Randomize Send = Send Send Discord Presence information = Send Discord "Rich Presence" information -Top Center = Top center -Top Left = Top left -Top Right = Top right Unable to find UPnP device = Unable to find UPnP device UPnP (port-forwarding) = UPnP (port forwarding) UPnP need to be reinitialized = UPnP need to be reinitialized @@ -852,6 +859,23 @@ Validating address... = Validating address... WLAN Channel = WLAN channel You're in Offline Mode, go to lobby or online hall = You're in offline mode, go to lobby or online hall +[PSPSettings] +Auto = Auto +Chinese (simplified) = Chinese (simplified) +Chinese (traditional) = Chinese (traditional) +Dutch = Dutch +English = English +French = French +Game language = Game language +German = German +Italian = Italian +Japanese = Japanese +Korean = Korean +Games often don't support all languages = Games often don't support all languages +Portuguese = Portuguese +Russian = Russian +Spanish = Spanish + [Pause] Cheats = Trucos Continue = Continuar diff --git a/assets/lang/gr_EL.ini b/assets/lang/gr_EL.ini index 55ee37398c..f4558e3660 100644 --- a/assets/lang/gr_EL.ini +++ b/assets/lang/gr_EL.ini @@ -6,12 +6,15 @@ Account = Account Achievement Unlocked = Achievement Unlocked Achievements = Achievements Achievements are disabled = Achievements are disabled +Can't log in to RetroAchievements right now = Can't log in to RetroAchievements right now Challenge Mode = Challenge Mode Challenge Mode (no savestates) = Challenge Mode (no savestates) +Contacting RetroAchievements server... = Contacting RetroAchievements server... Customize = Customize Earned = You have earned %d of %d achievements, and %d of %d points Encore Mode = Encore Mode How to use RetroAchievements = How to use RetroAchievements +In Encore mode - listings may be wrong below = In Encore mode - listings may be wrong below Leaderboard score submission = Leaderboard score submission Leaderboard submission is enabled = Leaderboard submission is enabled Leaderboards = Leaderboards @@ -88,6 +91,7 @@ Disable diagonal input = Disable diagonal input Double tap = Double tap Enable gesture control = Enable gesture control Enable standard shortcut keys = Enable standard shortcut keys +frames = frames Gesture = Gesture Gesture mapping = Gesture mapping Glowing borders = Φωτιζόμενα σύνορα @@ -116,6 +120,7 @@ OnScreen = Χειριστήριο Οθόνης Αφής Portrait = Πορτραίτο Portrait Reversed = Πορτραίτο (αναστροφή) PSP Action Buttons = Πλήκτρα δράσης PSP +Rapid fire interval = Rapid fire interval Raw input = Raw input Repeat mode = Repeat mode Reset to defaults = Reset to defaults @@ -310,10 +315,14 @@ VFPU = VFPU * PSP res = * ανάλυση PSP Active = ΕΝΕΡΓΟ Back = ΠΙΣΩ +Bottom Center = Bottom center +Bottom Left = Bottom left +Bottom Right = Bottom right Cancel = ΑΚΥΡΟ Center = ΚΕΝΤΡΟ -ChangingGPUBackends = Η αλλαγή Συστημάτων Υποστήριξης GPU απαιτεί επανεκκίνηση του PPSSPP. Επανεκκίνηση τώρα? -ChangingInflightFrames = Changing graphics command buffering requires PPSSPP to restart. Restart now? +Center Left = Center left +Center Right = Center right +Changing this setting requires PPSSPP to restart. = Changing this setting requires PPSSPP to restart. Channel: = Channel: Choose PPSSPP save folder = Επιλογή φακέλου αποθήκευσης PPSSPP Confirm Overwrite = ΑΝΤΙΚΑΤΑΣΤΑΣΗ ΔΕΔΟΜΕΝΩΝ; @@ -361,6 +370,7 @@ Move Up = Move Up Network Connection = ΣΥΝΔΕΣΗ ΔΙΚΤΥΟΥ NEW DATA = ΝΕΑ ΔΕΔΟΜΕΝΑ No = ΟΧΙ +None = None ObtainingIP = Obtaining IP address.\nPlease wait... OK = OK Old savedata detected = Εντοπίστηκαν παλαιά αρχεία αποθήκευσης @@ -369,6 +379,7 @@ Password = Password Remove = Remove Reset = ΕΠΑΝΑΦΟΡΑ Resize = ΑΛΛΑΓΗ ΜΕΓΕΘΟΥΣ +Restart = Restart Retry = ΕΠΑΝΑΛΗΨΗ Save = ΑΠΟΘΗΚΕΥΣΗ Save completed = ΑΠΟΘΗΚΕΥΣΗ ΟΛΟΚΛΗΡΩΘΗΚΕ. @@ -388,6 +399,9 @@ Supported = Supported There is no data = ΔΕΝ ΥΠΑΡΧΟΥΝ ΔΕΔΟΜΕΝΑ Toggle All = ΕΝΑΛΛΑΓΗ ΟΛΩΝ Toggle List = Toggle list +Top Center = Top center +Top Left = Top left +Top Right = Top right Unsupported = Unsupported Username = Username When you save, it will load on a PSP, but not an older PPSSPP = Όταν αποθηκεύετε, θα φορτώσει στο PSP, αλλά όχι σε μια παλαιότερη έκδοση του PPSSPP @@ -604,6 +618,7 @@ Stereo rendering = Stereo rendering Stretch = Επέκταση Texture Filter = Φίλτρο Υφών Texture Filtering = Φιλτράρισμα Υφών +Texture replacement pack activated = Texture replacement pack activated Texture Scaling = Κλιμάκωση Υφών Texture Shader = Texture shader Turn off Hardware Tessellation - unsupported = Απενεργοποιήστε το "Hardware Tessellation": δεν υποστηρίζεται @@ -789,11 +804,6 @@ AdHoc Server = Ad hoc server AdhocServer Failed to Bind Port = Ad hoc server failed to bind port AM: Data from Unknown Port = AM: Data from Unknown Port Auto = Auto -Bottom Center = Bottom center -Bottom Left = Bottom left -Bottom Right = Bottom right -Center Left = Center left -Center Right = Center right Change Mac Address = Αλλαγή φυσικής διεύθυνσης (MAC) Change proAdhocServer Address = Αλλαγή διεύθυνσης IP proAdhocServer (localhost = multiple instances) ChangeMacSaveConfirm = Generate a new MAC address? @@ -827,7 +837,6 @@ Invalid IP or hostname = Μη έγκυρη IP or hostname Minimum Timeout = Minimum timeout (override in ms, 0 = default) Misc = Miscellaneous (default = PSP compatibility) Network Initialized = Δίκτυο Εκκινήθηκε -None = None Please change your Port Offset = Please change your port offset Port offset = Offset πήλης(0 = για συμβατότητα PSP) Open PPSSPP Multiplayer Wiki Page = Open PPSSPP Multiplayer Wiki Page @@ -838,11 +847,9 @@ Quick Chat 3 = Quick chat 3 Quick Chat 4 = Quick chat 4 Quick Chat 5 = Quick chat 5 QuickChat = Quick chat +Randomize = Randomize Send = Send Send Discord Presence information = Αποστολή "Rich Presence" πληροφοριών σε Discord -Top Center = Top center -Top Left = Top left -Top Right = Top right Unable to find UPnP device = Unable to find UPnP device UPnP (port-forwarding) = UPnP (port forwarding) UPnP need to be reinitialized = UPnP need to be reinitialized @@ -852,6 +859,23 @@ Validating address... = Επικύρωση διεύθυνσης... WLAN Channel = WLAN channel You're in Offline Mode, go to lobby or online hall = You're in offline mode, go to lobby or online hall +[PSPSettings] +Auto = Auto +Chinese (simplified) = Chinese (simplified) +Chinese (traditional) = Chinese (traditional) +Dutch = Dutch +English = English +French = French +Game language = Game language +German = German +Italian = Italian +Japanese = Japanese +Korean = Korean +Games often don't support all languages = Games often don't support all languages +Portuguese = Portuguese +Russian = Russian +Spanish = Spanish + [Pause] Cheats = Κωδικοί Continue = Συνέχεια diff --git a/assets/lang/he_IL.ini b/assets/lang/he_IL.ini index 7fcfbfc901..0ee8fd9926 100644 --- a/assets/lang/he_IL.ini +++ b/assets/lang/he_IL.ini @@ -6,12 +6,15 @@ Account = Account Achievement Unlocked = Achievement Unlocked Achievements = Achievements Achievements are disabled = Achievements are disabled +Can't log in to RetroAchievements right now = Can't log in to RetroAchievements right now Challenge Mode = Challenge Mode Challenge Mode (no savestates) = Challenge Mode (no savestates) +Contacting RetroAchievements server... = Contacting RetroAchievements server... Customize = Customize Earned = You have earned %d of %d achievements, and %d of %d points Encore Mode = Encore Mode How to use RetroAchievements = How to use RetroAchievements +In Encore mode - listings may be wrong below = In Encore mode - listings may be wrong below Leaderboard score submission = Leaderboard score submission Leaderboard submission is enabled = Leaderboard submission is enabled Leaderboards = Leaderboards @@ -88,6 +91,7 @@ Disable diagonal input = Disable diagonal input Double tap = Double tap Enable gesture control = Enable gesture control Enable standard shortcut keys = Enable standard shortcut keys +frames = frames Gesture = Gesture Gesture mapping = Gesture mapping Glowing borders = Glowing borders @@ -116,6 +120,7 @@ OnScreen = מקשי מגע על המסך Portrait = Portrait Portrait Reversed = Portrait reversed PSP Action Buttons = PSP action buttons +Rapid fire interval = Rapid fire interval Raw input = Raw input Repeat mode = Repeat mode Reset to defaults = Reset to defaults @@ -310,10 +315,14 @@ VFPU = VFPU * PSP res = * PSP res Active = Active Back = << +Bottom Center = Bottom center +Bottom Left = Bottom left +Bottom Right = Bottom right Cancel = לטב Center = Center -ChangingGPUBackends = Changing GPU backends requires PPSSPP to restart. Restart now? -ChangingInflightFrames = Changing graphics command buffering requires PPSSPP to restart. Restart now? +Center Left = Center left +Center Right = Center right +Changing this setting requires PPSSPP to restart. = Changing this setting requires PPSSPP to restart. Channel: = Channel: Choose PPSSPP save folder = Choose PPSSPP save folder Confirm Overwrite = ?ולא םינותנ בתכשל הצור @@ -361,6 +370,7 @@ Move Up = Move Up Network Connection = Network Connection NEW DATA = םישדח םינותנ No = אל +None = None ObtainingIP = Obtaining IP address.\nPlease wait... OK = רדסב Old savedata detected = Old savedata detected @@ -369,6 +379,7 @@ Password = Password Remove = Remove Reset = Reset Resize = Resize +Restart = Restart Retry = Retry Save = Save Save completed = .המלשוה הרימש @@ -388,6 +399,9 @@ Supported = Supported There is no data = .םינותנ ןיא Toggle All = Toggle all Toggle List = Toggle list +Top Center = Top center +Top Left = Top left +Top Right = Top right Unsupported = Unsupported Username = Username When you save, it will load on a PSP, but not an older PPSSPP = When you save, it will load on a PSP, but not an older PPSSPP @@ -604,6 +618,7 @@ Stereo rendering = Stereo rendering Stretch = Stretch Texture Filter = קידוד טקסטורה Texture Filtering = קידוד טקסטורה +Texture replacement pack activated = Texture replacement pack activated Texture Scaling = מידת טקסטורה Texture Shader = Texture shader Turn off Hardware Tessellation - unsupported = Turn off "hardware tessellation": unsupported @@ -789,11 +804,6 @@ AdHoc Server = Ad hoc server AdhocServer Failed to Bind Port = Ad hoc server failed to bind port AM: Data from Unknown Port = AM: Data from Unknown Port Auto = Auto -Bottom Center = Bottom center -Bottom Left = Bottom left -Bottom Right = Bottom right -Center Left = Center left -Center Right = Center right Change Mac Address = Change MAC address Change proAdhocServer Address = Change PRO ad hoc server IP address (localhost = multiple instances) ChangeMacSaveConfirm = Generate a new MAC address? @@ -827,7 +837,6 @@ Invalid IP or hostname = Invalid IP or hostname Minimum Timeout = Minimum timeout (override in ms, 0 = default) Misc = Miscellaneous (default = PSP compatibility) Network Initialized = Network initialized -None = None Please change your Port Offset = Please change your port offset Port offset = Port offset(0 = PSP compatibility) Open PPSSPP Multiplayer Wiki Page = Open PPSSPP Multiplayer Wiki Page @@ -838,11 +847,9 @@ Quick Chat 3 = Quick chat 3 Quick Chat 4 = Quick chat 4 Quick Chat 5 = Quick chat 5 QuickChat = Quick chat +Randomize = Randomize Send = Send Send Discord Presence information = Send Discord "Rich Presence" information -Top Center = Top center -Top Left = Top left -Top Right = Top right Unable to find UPnP device = Unable to find UPnP device UPnP (port-forwarding) = UPnP (port forwarding) UPnP need to be reinitialized = UPnP need to be reinitialized @@ -852,6 +859,23 @@ Validating address... = Validating address... WLAN Channel = WLAN channel You're in Offline Mode, go to lobby or online hall = You're in offline mode, go to lobby or online hall +[PSPSettings] +Auto = Auto +Chinese (simplified) = Chinese (simplified) +Chinese (traditional) = Chinese (traditional) +Dutch = Dutch +English = English +French = French +Game language = Game language +German = German +Italian = Italian +Japanese = Japanese +Korean = Korean +Games often don't support all languages = Games often don't support all languages +Portuguese = Portuguese +Russian = Russian +Spanish = Spanish + [Pause] Cheats = Cheats Continue = המשך diff --git a/assets/lang/he_IL_invert.ini b/assets/lang/he_IL_invert.ini index 2beaef34c1..f4a7b671d5 100644 --- a/assets/lang/he_IL_invert.ini +++ b/assets/lang/he_IL_invert.ini @@ -6,12 +6,15 @@ Account = Account Achievement Unlocked = Achievement Unlocked Achievements = Achievements Achievements are disabled = Achievements are disabled +Can't log in to RetroAchievements right now = Can't log in to RetroAchievements right now Challenge Mode = Challenge Mode Challenge Mode (no savestates) = Challenge Mode (no savestates) +Contacting RetroAchievements server... = Contacting RetroAchievements server... Customize = Customize Earned = You have earned %d of %d achievements, and %d of %d points Encore Mode = Encore Mode How to use RetroAchievements = How to use RetroAchievements +In Encore mode - listings may be wrong below = In Encore mode - listings may be wrong below Leaderboard score submission = Leaderboard score submission Leaderboard submission is enabled = Leaderboard submission is enabled Leaderboards = Leaderboards @@ -88,6 +91,7 @@ Disable diagonal input = Disable diagonal input Double tap = Double tap Enable gesture control = Enable gesture control Enable standard shortcut keys = Enable standard shortcut keys +frames = frames Gesture = Gesture Gesture mapping = Gesture mapping Glowing borders = Glowing borders @@ -116,6 +120,7 @@ OnScreen = ךסמה לע עגמ ישקמ Portrait = Portrait Portrait Reversed = Portrait reversed PSP Action Buttons = PSP action buttons +Rapid fire interval = Rapid fire interval Raw input = Raw input Repeat mode = Repeat mode Reset to defaults = Reset to defaults @@ -310,10 +315,14 @@ VFPU = VFPU * PSP res = * PSP res Active = Active Back = << +Bottom Center = Bottom center +Bottom Left = Bottom left +Bottom Right = Bottom right Cancel = לטב Center = Center -ChangingGPUBackends = Changing GPU backends requires PPSSPP to restart. Restart now? -ChangingInflightFrames = Changing graphics command buffering requires PPSSPP to restart. Restart now? +Center Left = Center left +Center Right = Center right +Changing this setting requires PPSSPP to restart. = Changing this setting requires PPSSPP to restart. Channel: = Channel: Choose PPSSPP save folder = Choose PPSSPP save folder Confirm Overwrite = ?ולא םינותנ בתכשל הצור @@ -361,6 +370,7 @@ Move Up = Move Up Network Connection = Network Connection NEW DATA = םישדח םינותנ No = אל +None = None ObtainingIP = Obtaining IP address.\nPlease wait... OK = רדסב Old savedata detected = Old savedata detected @@ -369,6 +379,7 @@ Password = Password Remove = Remove Reset = Reset Resize = Resize +Restart = Restart Retry = Retry Save = Save Save completed = .המלשוה הרימש @@ -388,6 +399,9 @@ Supported = Supported There is no data = .םינותנ ןיא Toggle All = Toggle all Toggle List = Toggle list +Top Center = Top center +Top Left = Top left +Top Right = Top right Unsupported = Unsupported Username = Username When you save, it will load on a PSP, but not an older PPSSPP = When you save, it will load on a PSP, but not an older PPSSPP @@ -604,6 +618,7 @@ Stereo rendering = Stereo rendering Stretch = Stretch Texture Filter = הרוטסקט דודיק Texture Filtering = הרוטסקט דודיק +Texture replacement pack activated = Texture replacement pack activated Texture Scaling = הרוטסקט תדימ Texture Shader = Texture shader Turn off Hardware Tessellation - unsupported = Turn off "hardware tessellation": unsupported @@ -789,11 +804,6 @@ AdHoc Server = Ad hoc server AdhocServer Failed to Bind Port = Ad hoc server failed to bind port AM: Data from Unknown Port = AM: Data from Unknown Port Auto = Auto -Bottom Center = Bottom center -Bottom Left = Bottom left -Bottom Right = Bottom right -Center Left = Center left -Center Right = Center right Change Mac Address = Change MAC address Change proAdhocServer Address = Change PRO ad hoc server IP address (localhost = multiple instances) ChangeMacSaveConfirm = Generate a new MAC address? @@ -827,7 +837,6 @@ Invalid IP or hostname = Invalid IP or hostname Minimum Timeout = Minimum timeout (override in ms, 0 = default) Misc = Miscellaneous (default = PSP compatibility) Network Initialized = Network initialized -None = None Please change your Port Offset = Please change your port offset Port offset = Port offset(0 = PSP compatibility) Open PPSSPP Multiplayer Wiki Page = Open PPSSPP Multiplayer Wiki Page @@ -838,11 +847,9 @@ Quick Chat 3 = Quick chat 3 Quick Chat 4 = Quick chat 4 Quick Chat 5 = Quick chat 5 QuickChat = Quick chat +Randomize = Randomize Send = Send Send Discord Presence information = Send Discord "Rich Presence" information -Top Center = Top center -Top Left = Top left -Top Right = Top right Unable to find UPnP device = Unable to find UPnP device UPnP (port-forwarding) = UPnP (port forwarding) UPnP need to be reinitialized = UPnP need to be reinitialized @@ -852,6 +859,23 @@ Validating address... = Validating address... WLAN Channel = WLAN channel You're in Offline Mode, go to lobby or online hall = You're in offline mode, go to lobby or online hall +[PSPSettings] +Auto = Auto +Chinese (simplified) = Chinese (simplified) +Chinese (traditional) = Chinese (traditional) +Dutch = Dutch +English = English +French = French +Game language = Game language +German = German +Italian = Italian +Japanese = Japanese +Korean = Korean +Games often don't support all languages = Games often don't support all languages +Portuguese = Portuguese +Russian = Russian +Spanish = Spanish + [Pause] Cheats = Cheats Continue = ךשמה diff --git a/assets/lang/hr_HR.ini b/assets/lang/hr_HR.ini index 735a976f69..84a68e2dbe 100644 --- a/assets/lang/hr_HR.ini +++ b/assets/lang/hr_HR.ini @@ -6,12 +6,15 @@ Account = Account Achievement Unlocked = Achievement Unlocked Achievements = Achievements Achievements are disabled = Achievements are disabled +Can't log in to RetroAchievements right now = Can't log in to RetroAchievements right now Challenge Mode = Challenge Mode Challenge Mode (no savestates) = Challenge Mode (no savestates) +Contacting RetroAchievements server... = Contacting RetroAchievements server... Customize = Customize Earned = You have earned %d of %d achievements, and %d of %d points Encore Mode = Encore Mode How to use RetroAchievements = How to use RetroAchievements +In Encore mode - listings may be wrong below = In Encore mode - listings may be wrong below Leaderboard score submission = Leaderboard score submission Leaderboard submission is enabled = Leaderboard submission is enabled Leaderboards = Leaderboards @@ -88,6 +91,7 @@ Disable diagonal input = Disable diagonal input Double tap = Double tap Enable gesture control = Enable gesture control Enable standard shortcut keys = Enable standard shortcut keys +frames = frames Gesture = Gesture Gesture mapping = Gesture mapping Glowing borders = Svijetleći rubovi @@ -116,6 +120,7 @@ OnScreen = Dodirne kontrole na ekranu Portrait = Portret Portrait Reversed = Obrnuti portret PSP Action Buttons = PSP akcijske kontrole +Rapid fire interval = Rapid fire interval Raw input = Raw input Repeat mode = Repeat mode Reset to defaults = Reset to defaults @@ -310,10 +315,14 @@ VFPU = VFPU * PSP res = * PSP res Active = Aktivno Back = Nazad +Bottom Center = Bottom center +Bottom Left = Bottom left +Bottom Right = Bottom right Cancel = Odustani Center = Center -ChangingGPUBackends = Mijenjanje GPU poslužitelja zahtijeva ponovno pokretanje PPSSPP. Ponovno pokrenuti? -ChangingInflightFrames = Mijenjanje grafičkih komandnih poliranja zahtijeva ponovno pokretanje PPSSPP. Ponovno pokrenuti? +Center Left = Center left +Center Right = Center right +Changing this setting requires PPSSPP to restart. = Promjena ove postavke zahtijeva ponovno pokretanje PPSSPP-a. Channel: = Channel: Choose PPSSPP save folder = Izaberi PPSSPP save mapu Confirm Overwrite = Želite li pisati preko podataka? @@ -361,6 +370,7 @@ Move Up = Move Up Network Connection = Internetska veza NEW DATA = NOVA DATA No = Ne +None = None ObtainingIP = Obtaining IP address.\nPlease wait... OK = OK Old savedata detected = Uočena stara savedata @@ -369,6 +379,7 @@ Password = Password Remove = Remove Reset = Reset Resize = Resize +Restart = Restart Retry = Ponovi Save = Spremi Save completed = Spremanje dovršeno. @@ -388,6 +399,9 @@ Supported = Podržano There is no data = Nema date. Toggle All = Uključi sve Toggle List = Toggle list +Top Center = Top center +Top Left = Top left +Top Right = Top right Unsupported = Nepodržano Username = Username When you save, it will load on a PSP, but not an older PPSSPP = Kada spremiš,učitat će se na PSP, ali ne na starijem PPSSPP-u @@ -604,6 +618,7 @@ Stereo rendering = Stereo rendering Stretch = Rastezanje Texture Filter = Filtar tekstura Texture Filtering = Filtriranje tekstura +Texture replacement pack activated = Texture replacement pack activated Texture Scaling = Mjerenje tekstura Texture Shader = Texture shader Turn off Hardware Tessellation - unsupported = Isključi "hardver mozailk": nepodržano @@ -789,11 +804,6 @@ AdHoc Server = Ad hoc server AdhocServer Failed to Bind Port = Ad hoc server failed to bind port AM: Data from Unknown Port = AM: Data from Unknown Port Auto = Auto -Bottom Center = Bottom center -Bottom Left = Bottom left -Bottom Right = Bottom right -Center Left = Center left -Center Right = Center right Change Mac Address = Promijeni MAC addresu Change proAdhocServer Address = Promijeni PRO ad hoc server IP addresu (localhost = multiple instances) ChangeMacSaveConfirm = Generate a new MAC address? @@ -827,7 +837,6 @@ Invalid IP or hostname = Nevažeći IP ili domaćin Minimum Timeout = Minimum timeout (override in ms, 0 = default) Misc = Miscellaneous (default = PSP compatibility) Network Initialized = Veza Uspostavljena -None = None Please change your Port Offset = Please change your port offset Port offset = Port offset (0 = PSP kompatibilnost) Open PPSSPP Multiplayer Wiki Page = Open PPSSPP Multiplayer Wiki Page @@ -838,11 +847,9 @@ Quick Chat 3 = Quick chat 3 Quick Chat 4 = Quick chat 4 Quick Chat 5 = Quick chat 5 QuickChat = Quick chat +Randomize = Randomize Send = Send Send Discord Presence information = Šalji Discord "Rich Presence" informaciju -Top Center = Top center -Top Left = Top left -Top Right = Top right Unable to find UPnP device = Unable to find UPnP device UPnP (port-forwarding) = UPnP (port forwarding) UPnP need to be reinitialized = UPnP need to be reinitialized @@ -852,6 +859,23 @@ Validating address... = Provjeravanje adrese... WLAN Channel = WLAN channel You're in Offline Mode, go to lobby or online hall = Trenutno si u "Offline",idi u lobby ili online hall +[PSPSettings] +Auto = Auto +Chinese (simplified) = Chinese (simplified) +Chinese (traditional) = Chinese (traditional) +Dutch = Dutch +English = English +French = French +Game language = Game language +German = German +Italian = Italian +Japanese = Japanese +Korean = Korean +Games often don't support all languages = Games often don't support all languages +Portuguese = Portuguese +Russian = Russian +Spanish = Spanish + [Pause] Cheats = Šifre Continue = Nastavi diff --git a/assets/lang/hu_HU.ini b/assets/lang/hu_HU.ini index 1aaec7b421..dbc560b78d 100644 --- a/assets/lang/hu_HU.ini +++ b/assets/lang/hu_HU.ini @@ -6,12 +6,15 @@ Account = Account Achievement Unlocked = Achievement Unlocked Achievements = Achievements Achievements are disabled = Achievements are disabled +Can't log in to RetroAchievements right now = Can't log in to RetroAchievements right now Challenge Mode = Challenge Mode Challenge Mode (no savestates) = Challenge Mode (no savestates) +Contacting RetroAchievements server... = Contacting RetroAchievements server... Customize = Customize Earned = You have earned %d of %d achievements, and %d of %d points Encore Mode = Encore Mode How to use RetroAchievements = How to use RetroAchievements +In Encore mode - listings may be wrong below = In Encore mode - listings may be wrong below Leaderboard score submission = Leaderboard score submission Leaderboard submission is enabled = Leaderboard submission is enabled Leaderboards = Leaderboards @@ -88,6 +91,7 @@ Disable diagonal input = Disable diagonal input Double tap = Double tap Enable gesture control = Enable gesture control Enable standard shortcut keys = Enable standard shortcut keys +frames = frames Gesture = Gesture Gesture mapping = Gesture mapping Glowing borders = Világító szélek @@ -116,6 +120,7 @@ OnScreen = Érintőképernyős irányítás Portrait = Álló Portrait Reversed = Fordított álló PSP Action Buttons = PSP akció gombok +Rapid fire interval = Rapid fire interval Raw input = Raw input Repeat mode = Repeat mode Reset to defaults = Reset to defaults @@ -310,10 +315,14 @@ VFPU = VFPU * PSP res = * PSP felbontás Active = Aktív Back = Vissza +Bottom Center = Alul középen +Bottom Left = Alul bal old. +Bottom Right = Alul jobb old. Cancel = Mégse Center = Középre -ChangingGPUBackends = GPU backend megváltoztatásához újra kell indítani az alkalmazást. Újraindítod most? -ChangingInflightFrames = Grafikai parancs-bufferelés megváltoztatásához újra kell indítani az alkalmazást. Újraindítod most? +Center Left = Középen bal old. +Center Right = Középen jobb old. +Changing this setting requires PPSSPP to restart. = Grafikai parancs-bufferelés megváltoztatásához újra kell indítani az alkalmazást. Újraindítod most? Channel: = Channel: Choose PPSSPP save folder = PPSSPP mentési mappa kiválasztása Confirm Overwrite = Biztosan felülírod? @@ -361,6 +370,7 @@ Move Up = Move Up Network Connection = Hálózati kapcsolat NEW DATA = ÚJ MENTÉS No = Nem +None = None ObtainingIP = Obtaining IP address.\nPlease wait... OK = OK Old savedata detected = Régi mentés észlelve @@ -369,6 +379,7 @@ Password = Password Remove = Remove Reset = Visszaállít Resize = Átméretez +Restart = Restart Retry = Újra Save = Mentés Save completed = Mentve. @@ -388,6 +399,9 @@ Supported = Támogatott There is no data = Nincs adat. Toggle All = Összes átkapcsolása Toggle List = Toggle list +Top Center = Felül középen +Top Left = Felül bal old. +Top Right = Felül jobb old. Unsupported = Nem támogatott Username = Username When you save, it will load on a PSP, but not an older PPSSPP = Ha mentesz, PSP-n vissza tudod majd tölteni, de egy régebbi PPSSPP-n nem @@ -604,6 +618,7 @@ Stereo rendering = Stereo rendering Stretch = Nyújtás Texture Filter = Textúra szűrés Texture Filtering = Textúra szűrés +Texture replacement pack activated = Texture replacement pack activated Texture Scaling = Textúra nagyítás Texture Shader = Texture shader Turn off Hardware Tessellation - unsupported = Kapcsold ki a "Hardveres tesszelációt": nem támogatott! @@ -789,11 +804,6 @@ AdHoc Server = Ad hoc server AdhocServer Failed to Bind Port = Ad hoc server failed to bind port AM: Data from Unknown Port = AM: Data from Unknown Port Auto = Auto -Bottom Center = Alul középen -Bottom Left = Alul bal old. -Bottom Right = Alul jobb old. -Center Left = Középen bal old. -Center Right = Középen jobb old. Change Mac Address = MAC cím megváltoztatása Change proAdhocServer Address = ProAdhoc szerver címe (localhost = multiple instances) ChangeMacSaveConfirm = Generate a new MAC address? @@ -827,7 +837,6 @@ Invalid IP or hostname = Érvénytelen IP vagy kiszolgáló név Minimum Timeout = Minimum timeout (override in ms, 0 = default) Misc = Miscellaneous (default = PSP compatibility) Network Initialized = Hálózat inicializálva -None = None Please change your Port Offset = Please change your port offset Port offset = Port eltolás/offset (0 = PSP kompatibilitás) Open PPSSPP Multiplayer Wiki Page = Open PPSSPP Multiplayer Wiki Page @@ -838,11 +847,9 @@ Quick Chat 3 = Gyors üzenet 3 Quick Chat 4 = Gyors üzenet 4 Quick Chat 5 = Gyors üzenet 5 QuickChat = Quick chat +Randomize = Randomize Send = Küldés Send Discord Presence information = Discord "Rich Presence" információ küldése -Top Center = Felül középen -Top Left = Felül bal old. -Top Right = Felül jobb old. Unable to find UPnP device = Unable to find UPnP device UPnP (port-forwarding) = UPnP (port forwarding) UPnP need to be reinitialized = UPnP need to be reinitialized @@ -852,6 +859,23 @@ Validating address... = Cím ellenőrzése... WLAN Channel = WLAN channel You're in Offline Mode, go to lobby or online hall = Offline üzemmódban vagy, menj a lobbiba vagy az online terembe +[PSPSettings] +Auto = Auto +Chinese (simplified) = Chinese (simplified) +Chinese (traditional) = Chinese (traditional) +Dutch = Dutch +English = English +French = French +Game language = Game language +German = German +Italian = Italian +Japanese = Japanese +Korean = Korean +Games often don't support all languages = Games often don't support all languages +Portuguese = Portuguese +Russian = Russian +Spanish = Spanish + [Pause] Cheats = Csalások Continue = Folytatás diff --git a/assets/lang/id_ID.ini b/assets/lang/id_ID.ini index 58f6bcdd22..ce05491d38 100644 --- a/assets/lang/id_ID.ini +++ b/assets/lang/id_ID.ini @@ -6,12 +6,15 @@ Account = Account Achievement Unlocked = Achievement Unlocked Achievements = Achievements Achievements are disabled = Achievements are disabled +Can't log in to RetroAchievements right now = Can't log in to RetroAchievements right now Challenge Mode = Challenge Mode Challenge Mode (no savestates) = Challenge Mode (no savestates) +Contacting RetroAchievements server... = Contacting RetroAchievements server... Customize = Customize Earned = You have earned %d of %d achievements, and %d of %d points Encore Mode = Encore Mode How to use RetroAchievements = How to use RetroAchievements +In Encore mode - listings may be wrong below = In Encore mode - listings may be wrong below Leaderboard score submission = Leaderboard score submission Leaderboard submission is enabled = Leaderboard submission is enabled Leaderboards = Leaderboards @@ -88,6 +91,7 @@ Disable diagonal input = Nonaktifkan masukan diagonal Double tap = Ketuk dua kali Enable gesture control = Aktifkan kontrol gerakan Enable standard shortcut keys = Aktifkan tombol pintas standar +frames = frames Gesture = Gerak Gesture mapping = Pemetaan gerakan Glowing borders = Pinggiran bercahaya @@ -116,6 +120,7 @@ OnScreen = Kontrol sentuh di layar Portrait = Tegak Portrait Reversed = Tegak terbalik PSP Action Buttons = Tombol aksi PSP +Rapid fire interval = Rapid fire interval Raw input = Masukan mentah Repeat mode = Repeat mode Reset to defaults = Atur ulang ke pengaturan awal @@ -310,10 +315,14 @@ VFPU = VFPU * PSP res = * PSP res Active = Aktif Back = Kembali +Bottom Center = Tengah bawah +Bottom Left = Kiri bawah +Bottom Right = Kanan bawah Cancel = Batal Center = Tengah -ChangingGPUBackends = Pergantian penyangga GPU memerlukan pemulaian ulang PPSSPP. Mulai ulang sekarang? -ChangingInflightFrames = Mengubah penyangga perintah grafis membutuhkan PPSSPP untuk memulai ulang. Mulai ulang sekarang? +Center Left = Kiri tengah +Center Right = Kanan tengah +Changing this setting requires PPSSPP to restart. = Mengubah pengaturan ini memerlukan memulai ulang PPSSPP. Channel: = Saluran: Choose PPSSPP save folder = Pilih berkas simpanan PPSSPP Confirm Overwrite = Apakah anda ingin menimpa data? @@ -361,6 +370,7 @@ Move Up = Move Up Network Connection = Koneksi Jaringan NEW DATA = DATA BARU No = Tidak +None = Tidak ada ObtainingIP = Mendapatkan alamat IP.\nHarap tunggu... OK = Oke Old savedata detected = Simpanan data lama terdeteksi @@ -370,6 +380,7 @@ Remove = Remove Reset = Atur ulang Resize = Atur ulang ukuran Retry = Coba lagi +Restart = Mengulang kembali Save = Simpan Save completed = Selesai menyimpan. Saving = Menyimpan\nMohon tunggu... @@ -388,6 +399,9 @@ Supported = Didukung There is no data = Tidak ada data. Toggle All = Alihkan semua Toggle List = Daftar pengalihan +Top Center = Tengah atas +Top Left = Kiri atas +Top Right = Kanan atas Unsupported = Tidak didukung Username = Username When you save, it will load on a PSP, but not an older PPSSPP = Setelah anda menyimpan, ini akan dapat dimuat di PSP, namun tidak di PPSSPP lawas @@ -604,6 +618,7 @@ Stereo rendering = Stereo rendering Stretch = Peregangan Texture Filter = Filter tekstur Texture Filtering = Pemfilteran tekstur +Texture replacement pack activated = Texture replacement pack activated Texture Scaling = Penskala tekstur Texture Shader = Tekstur shader Turn off Hardware Tessellation - unsupported = Matikan "Pengujian perangkat keras": Tidak didukung @@ -789,11 +804,6 @@ AdHoc Server = Server Ad Hoc AdhocServer Failed to Bind Port = Server Ad Hoc gagal memuat port AM: Data from Unknown Port = AM: Data dari port tidak dikenal Auto = Otomatis -Bottom Center = Tengah bawah -Bottom Left = Kiri bawah -Bottom Right = Kanan bawah -Center Left = Kiri tengah -Center Right = Kanan tengah Change Mac Address = Ganti alamat MAC Change proAdhocServer Address = Ganti alamat IP pelayan PRO ad hoc (lokal) ChangeMacSaveConfirm = Generate a new MAC address? @@ -827,7 +837,6 @@ Invalid IP or hostname = IP atau nama host tidak valid Minimum Timeout = Batas waktu minimum (batas dalam ms, 0 = bawaan) Misc = Lain-lain (bawaan= kompatibilitas PSP) Network Initialized = Jaringan terinisiasi -None = Tidak ada Please change your Port Offset = Harap ubah letak port Anda Port offset = Letak port (0 = kecocokan dengan PSP) Open PPSSPP Multiplayer Wiki Page = Halaman Wiki PPSSPP Ad-Hoc @@ -838,11 +847,9 @@ Quick Chat 3 = Obrolan cepat 3 Quick Chat 4 = Obrolan cepat 4 Quick Chat 5 = Obrolan cepat 5 QuickChat = Obrolan cepat +Randomize = Randomize Send = Kirim Send Discord Presence information = Kirim informasi "Kritik dan Saran" ke Discord -Top Center = Tengah atas -Top Left = Kiri atas -Top Right = Kanan atas Unable to find UPnP device = Tidak dapat menemukan perangkat UPnP UPnP (port-forwarding) = UPnP (penerusan port) UPnP need to be reinitialized = UPnP perlu diinisialisasi ulang @@ -852,6 +859,23 @@ Validating address... = Validasi alamat... WLAN Channel = Saluran WLAN You're in Offline Mode, go to lobby or online hall = Anda dalam mode offline, pergi ke lobi atau aula online +[PSPSettings] +Auto = Auto +Chinese (simplified) = Chinese (simplified) +Chinese (traditional) = Chinese (traditional) +Dutch = Dutch +English = English +French = French +Game language = Game language +German = German +Italian = Italian +Japanese = Japanese +Korean = Korean +Games often don't support all languages = Games often don't support all languages +Portuguese = Portuguese +Russian = Russian +Spanish = Spanish + [Pause] Cheats = Pengecoh Continue = Lanjutkan diff --git a/assets/lang/it_IT.ini b/assets/lang/it_IT.ini index 87a995f41d..f84d30c366 100644 --- a/assets/lang/it_IT.ini +++ b/assets/lang/it_IT.ini @@ -6,12 +6,15 @@ Account = Account Achievement Unlocked = Achievement Unlocked Achievements = Achievements Achievements are disabled = Achievements are disabled +Can't log in to RetroAchievements right now = Can't log in to RetroAchievements right now Challenge Mode = Challenge Mode Challenge Mode (no savestates) = Challenge Mode (no savestates) +Contacting RetroAchievements server... = Contacting RetroAchievements server... Customize = Customize Earned = You have earned %d of %d achievements, and %d of %d points Encore Mode = Encore Mode How to use RetroAchievements = How to use RetroAchievements +In Encore mode - listings may be wrong below = In Encore mode - listings may be wrong below Leaderboard score submission = Leaderboard score submission Leaderboard submission is enabled = Leaderboard submission is enabled Leaderboards = Leaderboards @@ -88,6 +91,7 @@ Disable diagonal input = Disabilita input diagonale Double tap = Doppio tocco Enable gesture control = Abilita controllo gesti Enable standard shortcut keys = Abilita scorciatoie standard +frames = frames Gesture = Gesti Gesture mapping = Mappatura gesti Glowing borders = Bordi luminosi @@ -116,6 +120,7 @@ OnScreen = Controlli Touchscreen Portrait = Ritratto Portrait Reversed = Ritratto invertito PSP Action Buttons = Pulsanti Azione PSP (△◯✕☐) +Rapid fire interval = Rapid fire interval Raw input = Input grezzo Reset to defaults = Reset ai predefiniti Screen Rotation = Rotazione Schermo @@ -310,10 +315,14 @@ VFPU = VFPU * PSP res = * definizione PSP Active = Attiva Back = Indietro +Bottom Center = In basso al centro +Bottom Left = In basso a sinistra +Bottom Right = In basso a destra Cancel = Annulla Center = Centra -ChangingGPUBackends = Per cambiare il renderer GPU sarà necessario riavviare PPSSPP. Riavviare adesso? -ChangingInflightFrames = La modifica del buffering dei comandi grafici richiede il riavvio di PPSSPP. Riavviare adesso? +Center Left = Al centro a sinistra +Center Right = Al centro a destra +Changing this setting requires PPSSPP to restart. = La modifica di questa impostazione richiede il riavvio di PPSSPP. Channel: = Canale: Choose PPSSPP save folder = Scegli la cartella di salvataggio PPSSPP Confirm Overwrite = Vuoi sovrascrivere i dati? @@ -361,6 +370,7 @@ Move Up = Sposta su Network Connection = Connessione di Rete NEW DATA = NUOVI DATI No = No +None = None ObtainingIP = Cerco di ottenere l'indirizzo IP.\nAttendere, prego... OK = OK Old savedata detected = Rilevati vecchi dati salvati @@ -369,6 +379,7 @@ Password = Password Remove = Rimuovi Reset = Reset Resize = Ridimensiona +Restart = Riavvia Retry = Riprova Save = Salva Save completed = Salvataggio Completato. @@ -388,6 +399,9 @@ Supported = Supportato There is no data = Non ci sono dati. Toggle All = Attiva Tutto Toggle List = Mostra lista +Top Center = In alto al centro +Top Left = In alto a sinistra +Top Right = In alto a destra Unsupported = Non supportato Username = Username When you save, it will load on a PSP, but not an older PPSSPP = Salvando, sarà possibile effettuare il caricamento su una PSP, ma non su un vecchio PPSSPP. @@ -605,6 +619,7 @@ Stereo rendering = Rendering stereo Stretch = Adatta al Display Texture Filter = Filtro delle texture Texture Filtering = Filtraggio delle texture +Texture replacement pack activated = Texture replacement pack activated Texture Scaling = Scalatura delle texture Texture Shader = Shader delle texture Turn off Hardware Tessellation - unsupported = Disattivazione di "tessellazione hardware": non supportata @@ -761,11 +776,6 @@ AdHoc Server = Server Ad hoc AdhocServer Failed to Bind Port = Il server Ad hoc non è riuscito a collegarsi alla porta AM: Data from Unknown Port = AM: Dati da porta sconosciuta Auto = Auto -Bottom Center = In basso al centro -Bottom Left = In basso a sinistra -Bottom Right = In basso a destra -Center Left = Al centro a sinistra -Center Right = Al centro a destra Change Mac Address = Cambia indirizzo MAC Change proAdhocServer Address = Cambia indirizzo server IP PRO ad hoc (localhost = istanze multiple) ChangeMacSaveConfirm = Generare un nuovo indirizzo MAC? @@ -799,7 +809,6 @@ Invalid IP or hostname = Indirizzo IP o nome del dominio non valido Minimum Timeout = Timeout minimo (forza in ms, 0 = predefinito) Misc = Varie (predefinito = compatibilità PSP) Network Initialized = Rete inizializzata -None = None Please change your Port Offset = Prego, cambiare l'offset della porta Port offset = Offset Porta (0 = compatibilità PSP) Open PPSSPP Multiplayer Wiki Page = Pagina Wiki Ad-Hoc PPSSPP @@ -810,11 +819,9 @@ Quick Chat 3 = Chat rapida 3 Quick Chat 4 = Chat rapida 4 Quick Chat 5 = Chat rapida 5 QuickChat = Chat rapida +Randomize = Randomize Send = Invia Send Discord Presence information = Invia informazioni Discord "Rich Presence" -Top Center = In alto al centro -Top Left = In alto a sinistra -Top Right = In alto a destra Unable to find UPnP device = Impossibile trovare il dispositivo UPnP UPnP (port-forwarding) = UPnP (reindirizzamento della porta) UPnP need to be reinitialized = UPnP dev'essere reinizializzato @@ -824,6 +831,23 @@ Validating address... = Convalida indirizzo... WLAN Channel = Canale WLAN You're in Offline Mode, go to lobby or online hall = Sei in modalità offline, vai alla lobby online. +[PSPSettings] +Auto = Auto +Chinese (simplified) = Chinese (simplified) +Chinese (traditional) = Chinese (traditional) +Dutch = Dutch +English = English +French = French +Game language = Game language +German = German +Italian = Italian +Japanese = Japanese +Korean = Korean +Games often don't support all languages = Games often don't support all languages +Portuguese = Portuguese +Russian = Russian +Spanish = Spanish + [Pause] Cheats = Cheat Continue = Continua diff --git a/assets/lang/ja_JP.ini b/assets/lang/ja_JP.ini index fce86c9ebf..04fca0a49a 100644 --- a/assets/lang/ja_JP.ini +++ b/assets/lang/ja_JP.ini @@ -6,12 +6,15 @@ Account = Account Achievement Unlocked = Achievement Unlocked Achievements = Achievements Achievements are disabled = Achievements are disabled +Can't log in to RetroAchievements right now = Can't log in to RetroAchievements right now Challenge Mode = Challenge Mode Challenge Mode (no savestates) = Challenge Mode (no savestates) +Contacting RetroAchievements server... = Contacting RetroAchievements server... Customize = Customize Earned = You have earned %d of %d achievements, and %d of %d points Encore Mode = Encore Mode How to use RetroAchievements = How to use RetroAchievements +In Encore mode - listings may be wrong below = In Encore mode - listings may be wrong below Leaderboard score submission = Leaderboard score submission Leaderboard submission is enabled = Leaderboard submission is enabled Leaderboards = Leaderboards @@ -88,6 +91,7 @@ Disable diagonal input = 斜め入力を無効にする Double tap = ダブルタップ Enable gesture control = ジェスチャー操作を有効にする Enable standard shortcut keys = 標準のショートカットキーを有効にする +frames = frames Gesture = ジェスチャー Gesture mapping = ジェスチャー操作の設定をする Glowing borders = 点灯する枠線 @@ -116,6 +120,7 @@ OnScreen = 画面にタッチ用のコントローラを表示する Portrait = 縦 Portrait Reversed = 縦 (反転) PSP Action Buttons = PSPアクションボタン +Rapid fire interval = Rapid fire interval Raw input = 現実の入力 Repeat mode = Repeat mode Reset to defaults = デフォルトに戻す @@ -310,10 +315,14 @@ VFPU = VFPU * PSP res = * PSP 解像度 Active = アクティブ Back = 戻る +Bottom Center = 下中央 +Bottom Left = 左下 +Bottom Right = 右下 Cancel = キャンセル Center = センタリング -ChangingGPUBackends = GPUバックエンドの変更にはPPSSPPの再起動が必要です。今すぐ再起動しますか? -ChangingInflightFrames = グラフィックスコマンドのバッファリングを変更するにはPPSSPPの再起動が必要です。 再起動しますか? +Center Left = 左中央 +Center Right = 右中央 +Changing this setting requires PPSSPP to restart. = この設定を変更するには、PPSSPPを再起動する必要があります。 Channel: = Channel: Choose PPSSPP save folder = PPSSPPのセーブ用フォルダを選択する Confirm Overwrite = データを上書きしますか? @@ -361,6 +370,7 @@ Move Up = 上へ移動 Network Connection = ネットワーク接続 NEW DATA = 新しいデータ No = いいえ +None = 無し ObtainingIP = IPアドレスの取得中。 \nしばらくお待ちください... OK = OK Old savedata detected = 古いセーブデータを検出しました @@ -369,6 +379,7 @@ Password = Password Remove = 削除 Reset = リセット Resize = リサイズ +Restart = PPSSPPを再起動します。 Retry = リトライ Save = セーブ Save completed = セーブが完了しました。 @@ -388,6 +399,9 @@ Supported = サポートしています There is no data = データがありません。 Toggle All = 全て反転する Toggle List = 切り替えリスト +Top Center = 上中央 +Top Left = 左上 +Top Right = 右上 Unsupported = サポートしていません Username = Username When you save, it will load on a PSP, but not an older PPSSPP = 保存すると、PSPではロードできますが古いPPSSPPではロードできません @@ -604,6 +618,7 @@ Stereo rendering = ステレオレンダリング Stretch = 拡大 Texture Filter = テクスチャフィルタ Texture Filtering = テクスチャフィルタリング +Texture replacement pack activated = Texture replacement pack activated Texture Scaling = テクスチャスケーリング Texture Shader = テクスチャシェーダ Turn off Hardware Tessellation - unsupported = 「ハードウェアテッセレーション」をオフにしてください。サポートされていません @@ -760,11 +775,6 @@ AdHoc Server = アドホックサーバ AdhocServer Failed to Bind Port = アドホックサーバーがポートのバインドに失敗しました AM: Data from Unknown Port = AM: 不明なポートからのデータ Auto = 自動 -Bottom Center = 下中央 -Bottom Left = 左下 -Bottom Right = 右下 -Center Left = 左中央 -Center Right = 右中央 Change Mac Address = MACアドレスを変更する Change proAdhocServer Address = PROアドホックサーバのIPアドレスを変更する (localhost = multiple instances) ChangeMacSaveConfirm = 新しいMACアドレスを生成しますか? @@ -798,7 +808,6 @@ Invalid IP or hostname = 無効なIPまたはホスト名 Minimum Timeout = 最小タイムアウト (低遅延をオーバーライド、単位ms) Misc = その他 (デフォルト = PSP互換) Network Initialized = ネットワークを初期化 -None = 無し Please change your Port Offset = ポートオフセットを変更してください Port offset = ポートオフセット (0 = PSP互換) Open PPSSPP Multiplayer Wiki Page = PPSSPPのアドホック接続Wikiページを開く (英語) @@ -809,11 +818,9 @@ Quick Chat 3 = クイックチャット3 Quick Chat 4 = クイックチャット4 Quick Chat 5 = クイックチャット5 QuickChat = クイックチャット +Randomize = Randomize Send = 送信する Send Discord Presence information = Discordのプレゼンス情報を送信する -Top Center = 上中央 -Top Left = 左上 -Top Right = 右上 Unable to find UPnP device = UPnPデバイスがみつかりません UPnP (port-forwarding) = UPnP(ポートフォワーディング) UPnP need to be reinitialized = UPnPの再初期化が必要です @@ -823,6 +830,23 @@ Validating address... = アドレスを検証しています... WLAN Channel = WLANチャネル You're in Offline Mode, go to lobby or online hall = オフラインモードです。ロビーかホールに移動してください +[PSPSettings] +Auto = Auto +Chinese (simplified) = Chinese (simplified) +Chinese (traditional) = Chinese (traditional) +Dutch = Dutch +English = English +French = French +Game language = Game language +German = German +Italian = Italian +Japanese = Japanese +Korean = Korean +Games often don't support all languages = Games often don't support all languages +Portuguese = Portuguese +Russian = Russian +Spanish = Spanish + [Pause] Cheats = チート Continue = 再開する diff --git a/assets/lang/jv_ID.ini b/assets/lang/jv_ID.ini index a167ea56e6..11d42c3c40 100644 --- a/assets/lang/jv_ID.ini +++ b/assets/lang/jv_ID.ini @@ -6,12 +6,15 @@ Account = Account Achievement Unlocked = Achievement Unlocked Achievements = Achievements Achievements are disabled = Achievements are disabled +Can't log in to RetroAchievements right now = Can't log in to RetroAchievements right now Challenge Mode = Challenge Mode Challenge Mode (no savestates) = Challenge Mode (no savestates) +Contacting RetroAchievements server... = Contacting RetroAchievements server... Customize = Customize Earned = You have earned %d of %d achievements, and %d of %d points Encore Mode = Encore Mode How to use RetroAchievements = How to use RetroAchievements +In Encore mode - listings may be wrong below = In Encore mode - listings may be wrong below Leaderboard score submission = Leaderboard score submission Leaderboard submission is enabled = Leaderboard submission is enabled Leaderboards = Leaderboards @@ -88,6 +91,7 @@ Disable diagonal input = Disable diagonal input Double tap = Double tap Enable gesture control = Enable gesture control Enable standard shortcut keys = Enable standard shortcut keys +frames = frames Gesture = Gesture Gesture mapping = Gesture mapping Glowing borders = Glowing borders @@ -116,6 +120,7 @@ OnScreen = Tombol Layar-sentuh Portrait = Portret Portrait Reversed = Potret kebalik PSP Action Buttons = PSP Aksi tombol +Rapid fire interval = Rapid fire interval Raw input = Raw input Repeat mode = Repeat mode Reset to defaults = Reset to defaults @@ -310,10 +315,14 @@ VFPU = VFPU * PSP res = * PSP res Active = Active Back = Bali +Bottom Center = Bottom center +Bottom Left = Bottom left +Bottom Right = Bottom right Cancel = Mbatalake Center = Center -ChangingGPUBackends = Ganti backends GPU mbutuhake PPSSPP kanggo miwiti maneh. Wiwiti maneh saiki? -ChangingInflightFrames = Changing graphics command buffering requires PPSSPP to restart. Restart now? +Center Left = Center left +Center Right = Center right +Changing this setting requires PPSSPP to restart. = Changing this setting requires PPSSPP to restart. Channel: = Channel: Choose PPSSPP save folder = Peleh folder penyimpenan PPSSPP Confirm Overwrite = Njenengan arep Panganggoan data iki? @@ -361,6 +370,7 @@ Move Up = Move Up Network Connection = Koneksi Jaringan NEW DATA = DATA ANYAR No = Ora +None = None ObtainingIP = Obtaining IP address.\nPlease wait... OK = He.eh Old savedata detected = Simpenan lawas terdeteksi @@ -369,6 +379,7 @@ Password = Password Remove = Remove Reset = Ngreset Resize = Ubah Ukuran +Restart = Restart Retry = Jajal Maning Save = Simpen Save completed = Nyimpen rampung. @@ -388,6 +399,9 @@ Supported = Supported There is no data = Ora ana data. Toggle All = Peleh kabeh Toggle List = Toggle list +Top Center = Top center +Top Left = Top left +Top Right = Top right Unsupported = Unsupported Username = Username When you save, it will load on a PSP, but not an older PPSSPP = Nalika nyimpen, iku bakal mbukak ing PSP,nanging ora PPSSPP lawas @@ -604,6 +618,7 @@ Stereo rendering = Stereo rendering Stretch = Stretch Texture Filter = Penyaring Tekstur Texture Filtering = Penyaring Tekstur +Texture replacement pack activated = Texture replacement pack activated Texture Scaling = Skala Tekstur Texture Shader = Texture shader Turn off Hardware Tessellation - unsupported = Turn off "hardware tessellation": unsupported @@ -789,11 +804,6 @@ AdHoc Server = Ad hoc server AdhocServer Failed to Bind Port = Ad hoc server failed to bind port AM: Data from Unknown Port = AM: Data from Unknown Port Auto = Auto -Bottom Center = Bottom center -Bottom Left = Bottom left -Bottom Right = Bottom right -Center Left = Center left -Center Right = Center right Change Mac Address = Ubah alamat MAC Change proAdhocServer Address = Ubah alamat pelayan IP PRO ad hoc (localhost = multiple instances) ChangeMacSaveConfirm = Generate a new MAC address? @@ -827,7 +837,6 @@ Invalid IP or hostname = Invalid IP or hostname Minimum Timeout = Minimum timeout (override in ms, 0 = default) Misc = Miscellaneous (default = PSP compatibility) Network Initialized = Jaringan diinisiasi -None = None Please change your Port Offset = Please change your port offset Port offset = Port offset(0 = PSP compatibility) Open PPSSPP Multiplayer Wiki Page = Open PPSSPP Multiplayer Wiki Page @@ -838,11 +847,9 @@ Quick Chat 3 = Quick chat 3 Quick Chat 4 = Quick chat 4 Quick Chat 5 = Quick chat 5 QuickChat = Quick chat +Randomize = Randomize Send = Send Send Discord Presence information = Send Discord "Rich Presence" information -Top Center = Top center -Top Left = Top left -Top Right = Top right Unable to find UPnP device = Unable to find UPnP device UPnP (port-forwarding) = UPnP (port forwarding) UPnP need to be reinitialized = UPnP need to be reinitialized @@ -852,6 +859,23 @@ Validating address... = Validating address... WLAN Channel = WLAN channel You're in Offline Mode, go to lobby or online hall = You're in offline mode, go to lobby or online hall +[PSPSettings] +Auto = Auto +Chinese (simplified) = Chinese (simplified) +Chinese (traditional) = Chinese (traditional) +Dutch = Dutch +English = English +French = French +Game language = Game language +German = German +Italian = Italian +Japanese = Japanese +Korean = Korean +Games often don't support all languages = Games often don't support all languages +Portuguese = Portuguese +Russian = Russian +Spanish = Spanish + [Pause] Cheats = Mbeling Continue = Terusno diff --git a/assets/lang/ko_KR.ini b/assets/lang/ko_KR.ini index 073b3d318c..7b2616f055 100644 --- a/assets/lang/ko_KR.ini +++ b/assets/lang/ko_KR.ini @@ -4,23 +4,26 @@ %1: Leaderboard attempt failed = %1: 순위표 시도 실패 #%1: Submitting leaderboard score: %2! = %1: 리더보드 점수 제출: %2! Account = 계정 -Achievement Unlocked = Achievement Unlocked +Achievement Unlocked = 성과 잠금 해제 Achievements = 성과 Achievements are disabled = 성과 비활성화 +Can't log in to RetroAchievements right now = Can't log in to RetroAchievements right now Challenge Mode = 도전 모드 Challenge Mode (no savestates) = 도전 모드 (저장 상태 없음) +Contacting RetroAchievements server... = Contacting RetroAchievements server... Customize = Customize Earned = %d 의 %d 성과 및 %d 의 %d 포인트 획득 Encore Mode = 앙코르 모드 How to use RetroAchievements = RetroAchievements 사용 방법 -Leaderboard score submission = Leaderboard score submission +In Encore mode - listings may be wrong below = In Encore mode - listings may be wrong below +Leaderboard score submission = 순위표 점수 제출 Leaderboard submission is enabled = 순위표 제출 활성화 Leaderboards = 순위표 Links = 링크 Locked achievements = 잠겨진 성과 Log bad memory accesses = 불량 메모리 접속 로그 Mastered %1 = 마스터한 %1 -Submitted %1 for %2 = Submitted %1 for %2 +Submitted %1 for %2 = 2에 %1을(를) 제출 This feature is not available in Challenge Mode = 도전 모드에서는 이 기능을 사용할 수 없음 Save states not available in Challenge Mode = 도전 모드에서 사용할 수 없는 저장 상태 Register on www.retroachievements.org = www.retroachievements.org에 등록 @@ -89,6 +92,7 @@ Disable diagonal input = 대각선 입력 비활성화 Double tap = 두 번 탭 Enable gesture control = 제스처 제어 활성화 Enable standard shortcut keys = 표준 단축키 활성화 +frames = frames Gesture = 제스처 Gesture mapping = 제스처 맵핑 Glowing borders = 광선 테두리 @@ -117,6 +121,7 @@ OnScreen = 화면 터치 제어 Portrait = 세로 방향 Portrait Reversed = 세로 방향 반전 PSP Action Buttons = PSP 액션 버튼 +Rapid fire interval = Rapid fire interval Raw input = 원시 입력 Reset to defaults = 기본값으로 재설정 Screen Rotation = 화면 회전 @@ -311,10 +316,14 @@ VFPU = VFPU * PSP res = * PSP 리소스 Active = 활성화 Back = 뒤로가기 +Bottom Center = 하단 중앙 +Bottom Left = 하단 왼쪽 +Bottom Right = 하단 오른쪽 Cancel = 취소 Center = 중앙 -ChangingGPUBackends = GPU 백엔드를 변경하려면 PPSSPP를 다시 시작해야 합니다. 지금 다시 시작하겠습니까? -ChangingInflightFrames = 그래픽 명령 버퍼링을 변경하려면 PPSSPP를 다시 시작해야 합니다. 지금 다시 시작하겠습니까? +Center Left = 중앙 왼쪽 +Center Right = 중앙 오른쪽 +Changing this setting requires PPSSPP to restart. = 이 설정을 변경하려면 PPSSPP를 다시 시작해야 합니다. Channel: = 채널: Choose PPSSPP save folder = PPSSPP 저장 폴더 선택 Confirm Overwrite = 데이터를 덮어쓰겠습니까? @@ -340,8 +349,8 @@ Edit = 편집 Enable All = 모두 활성화 Enabled = 활성화 Enter = 입력 -Failed to connect to server, check your internet connection. = Failed to connect to server, check your internet connection. -Failed to log in, check your username and password. = Failed to log in, check your username and password. +Failed to connect to server, check your internet connection. = 서버에 연결하지 못했습니다. 인터넷 연결을 확인하세요. +Failed to log in, check your username and password. = 로그인에 실패했습니다. 사용자 이름과 비밀번호를 확인하세요. Filter = 필터 Finish = 마침 GE Frame Dumps = GE 프레임 덤프 @@ -354,14 +363,15 @@ Loading = 불러오기 중\n잠시만 기다려 주세요... LoadingFailed = 데이터를 불러올 수 없습니다. Log in = 로그인 Log out = 로그아웃 -Logged in! = Logged in! -Logging in... = Logging in... +Logged in! = 로그인했습니다! +Logging in... = 로그인 중... Move = 이동 Move Up = 위로 이동 Move Down = 아래로 이동 Network Connection = 네트워크 연결 NEW DATA = 새로운 데이터 No = 아니오 +None = 없음 ObtainingIP = IP 주소를 가져오는 중입니다.\n잠시만 기다려 주세요... OK = 확인 Old savedata detected = 오래된 저장 데이터 감지됨 @@ -370,6 +380,7 @@ Password = 비밀번호 Remove = 제거 Reset = 재설정 Resize = 크기 조정 +Restart = PPSSPP를 다시 시작합니다. Retry = 재시도 Save = 저장 Save completed = 저장이 완료되었습니다. @@ -389,6 +400,9 @@ Supported = 지원 There is no data = 데이터가 없습니다. Toggle All = 모두 토글 Toggle List = 토글 목록 +Top Center = 상단 중앙 +Top Left = 상단 왼쪽 +Top Right = 상단 오른쪽 Unsupported = 미지원 Username = 사용자이름 When you save, it will load on a PSP, but not an older PPSSPP = 저장하면 PSP에 불러오지만 이전 PPSSPP에는 부러오지 않습니다. @@ -605,6 +619,7 @@ Stereo rendering = 스트레오 렌더링 Stretch = 늘이기 Texture Filter = 텍스처 필터링 Texture Filtering = 텍스처 필터링 +Texture replacement pack activated = Texture replacement pack activated Texture Scaling = 텍스처 스케일링 Texture Shader = 텍스처 쉐이더 Turn off Hardware Tessellation - unsupported = "하드웨어 조각화" 끄기: 지원되지 않음 @@ -761,13 +776,8 @@ AdHoc Server = Ad hoc 서버 AdhocServer Failed to Bind Port = Ad Hoc 서버가 포트를 바인딩하지 못했습니다. AM: Data from Unknown Port = AM: 알 수 없는 포트의 데이터 Auto = 자동 -Bottom Center = 하단 중앙 -Bottom Left = 하단 왼쪽 -Bottom Right = 하단 오른쪽 -Center Left = 중앙 왼쪽 -Center Right = 중앙 오른쪽 Change Mac Address = MAC 주소 변경 -ChangeMacSaveConfirm = 새로운 MAC 주소를 생성하시겠습니까? +ChangeMacSaveConfirm = 새로운 MAC 주소를 생성하겠습니까? ChangeMacSaveWarning = 일부 게임은 저장 데이터를 불러올 때 MAC 주소를 확인하므로 이전 저장이 손상될 수 있습니다. Change proAdhocServer Address = PRO ad hoc 서버 IP 주소 변경 (로컬호스트 = 다중 인스턴스) Chat = 채팅 @@ -799,7 +809,6 @@ Invalid IP or hostname = 잘못된 IP 또는 호스트 이름 Minimum Timeout = 최소 시간 초과 (ms 단위로 재정의, 0 = 기본값) Misc = 기타 (기본값 = PSP 호환성) Network Initialized = 네트워크 초기화 -None = 없음 Please change your Port Offset = 포트 오프셋을 변경하세요. Port offset = 포트 오프셋 (0 = PSP 호환성) Open PPSSPP Multiplayer Wiki Page = PPSSPP 멀티플레이어 위키 페이지 열기 @@ -810,11 +819,9 @@ Quick Chat 3 = 빠른 채팅 3 Quick Chat 4 = 빠른 채팅 4 Quick Chat 5 = 빠른 채팅 5 QuickChat = 빠른 채팅 +Randomize = Randomize Send = 보내기 Send Discord Presence information = 디스코드 "리치 프레즌스" 정보 보내기 -Top Center = 상단 중앙 -Top Left = 상단 왼쪽 -Top Right = 상단 오른쪽 Unable to find UPnP device = UPnP 장치를 찾을 수 없습니다. UPnP (port-forwarding) = UPnP (포트 포워딩) UPnP need to be reinitialized = UPnP를 다시 초기화해야 합니다. @@ -824,6 +831,23 @@ Validating address... = 주소 검증하는 중... WLAN Channel = 무선랜 채널 You're in Offline Mode, go to lobby or online hall = 오프라인 모드입니다. 로비 또는 온라인 홀으로 이동합니다. +[PSPSettings] +Auto = Auto +Chinese (simplified) = Chinese (simplified) +Chinese (traditional) = Chinese (traditional) +Dutch = Dutch +English = English +French = French +Game language = Game language +German = German +Italian = Italian +Japanese = Japanese +Korean = Korean +Games often don't support all languages = Games often don't support all languages +Portuguese = Portuguese +Russian = Russian +Spanish = Spanish + [Pause] Cheats = 치트 Continue = 계속 @@ -1049,7 +1073,7 @@ Save State Failed = 상태 저장 실패! Saved State = 저장된 상태 saveNewTextures_false = 텍스처 저장이 비활성화되었습니다. saveNewTextures_true = 이제 텍스처가 저장 공간에 저장됩니다. -SpeedCustom2 = Speed: 대체 2 +SpeedCustom2 = 속도: 대체 2 standard = 속도: 표준 State load undone = 상태 불러오기 취소 Untitled PSP game = 제목 없는 PSP 게임 diff --git a/assets/lang/lo_LA.ini b/assets/lang/lo_LA.ini index 72106d8c7a..d5135f3d7e 100644 --- a/assets/lang/lo_LA.ini +++ b/assets/lang/lo_LA.ini @@ -6,12 +6,15 @@ Account = Account Achievement Unlocked = Achievement Unlocked Achievements = Achievements Achievements are disabled = Achievements are disabled +Can't log in to RetroAchievements right now = Can't log in to RetroAchievements right now Challenge Mode = Challenge Mode Challenge Mode (no savestates) = Challenge Mode (no savestates) +Contacting RetroAchievements server... = Contacting RetroAchievements server... Customize = Customize Earned = You have earned %d of %d achievements, and %d of %d points Encore Mode = Encore Mode How to use RetroAchievements = How to use RetroAchievements +In Encore mode - listings may be wrong below = In Encore mode - listings may be wrong below Leaderboard score submission = Leaderboard score submission Leaderboard submission is enabled = Leaderboard submission is enabled Leaderboards = Leaderboards @@ -88,6 +91,7 @@ Disable diagonal input = Disable diagonal input Double tap = Double tap Enable gesture control = Enable gesture control Enable standard shortcut keys = Enable standard shortcut keys +frames = frames Gesture = Gesture Gesture mapping = Gesture mapping Glowing borders = Glowing borders @@ -116,6 +120,7 @@ OnScreen = ໃຊ້ໜ້າຈໍຄວບຄຸມແບບສຳຜັດ Portrait = ແນວຕັ້ງ Portrait Reversed = ກັບດ້ານແນວຕັ້ງ PSP Action Buttons = ປຸ່ມກົດຂອງ PSP +Rapid fire interval = Rapid fire interval Raw input = Raw input Repeat mode = Repeat mode Reset to defaults = Reset to defaults @@ -310,10 +315,14 @@ VFPU = VFPU * PSP res = * ຂະໜາດຈໍ PSP Active = Active Back = ຍ້ອນກັບ +Bottom Center = Bottom center +Bottom Left = Bottom left +Bottom Right = Bottom right Cancel = ຍົກເລີກ Center = ທາງກາງ -ChangingGPUBackends = ເມື່ອປ່ຽນແປງຮູບແບບການສະໜັບສະໜູນ GPU \n ຈະຕ້ອງເລີ່ມ PPSSPP ໃໝ່ອີກຄັ້ງ. ເລີ່ມໃໝ່ເລີຍບໍ່? -ChangingInflightFrames = Changing graphics command buffering requires PPSSPP to restart. Restart now? +Center Left = Center left +Center Right = Center right +Changing this setting requires PPSSPP to restart. = Changing this setting requires PPSSPP to restart. Channel: = Channel: Choose PPSSPP save folder = ເລືອກໂຟນເດີ້ເຊບ PPSSPP Confirm Overwrite = ຕ້ອງການໃຫ້ຂຽນທັບຂໍ້ມູນເກົ່າຫຼືບໍ່? @@ -361,6 +370,7 @@ Move Up = Move Up Network Connection = ການເຊື່ອມຕໍ່ເຄື່ອຂ່າຍ NEW DATA = ຂໍ້ມູນໃໝ່ No = ບໍ່ແມ່ນ +None = None ObtainingIP = Obtaining IP address.\nPlease wait... OK = ຕົກລົງ Old savedata detected = ກວດພົບຂໍ້ມູນເຊບເກົ່າ @@ -369,6 +379,7 @@ Password = Password Remove = Remove Reset = ຕັ້ງຄ່າໃໝ່ Resize = ປັບຂະໜາດ +Restart = Restart Retry = ລອງໃໝ່ອີກຄັ້ງ Save = ບັນທຶກ Save completed = ບັນທຶກຂໍ້ມູນສຳເລັດ @@ -388,6 +399,9 @@ Supported = Supported There is no data = ບໍ່ມີຂໍ້ມູນ. Toggle All = ເລືອກທັງໝົດ Toggle List = Toggle list +Top Center = Top center +Top Left = Top left +Top Right = Top right Unsupported = Unsupported Username = Username When you save, it will load on a PSP, but not an older PPSSPP = ເມື່ອເຈົ້າເຊບເກມ, ຈະສາມາດນຳໄປໂຫຼດເທິງເຄື່ອງ PSP ໄດ້\nແຕ່ບໍ່ແມ່ນສຳລັບ PPSSPP ທີ່ເກົ່າກວ່າ @@ -604,6 +618,7 @@ Stereo rendering = Stereo rendering Stretch = ດຶງເຕັມຈໍ Texture Filter = ໂຕຕອງພື້ນຜິວ Texture Filtering = ຕອງພື້ນຜິວ +Texture replacement pack activated = Texture replacement pack activated Texture Scaling = ປັບພື້ນຜິວ Texture Shader = Texture shader Turn off Hardware Tessellation - unsupported = Turn off "hardware tessellation": unsupported @@ -789,11 +804,6 @@ AdHoc Server = Ad hoc server AdhocServer Failed to Bind Port = Ad hoc server failed to bind port AM: Data from Unknown Port = AM: Data from Unknown Port Auto = Auto -Bottom Center = Bottom center -Bottom Left = Bottom left -Bottom Right = Bottom right -Center Left = Center left -Center Right = Center right Change Mac Address = ປ່ຽນຄ່າທີ່ຢູ່ MAC Change proAdhocServer Address = ປ່ຽນໄອພີທີ່ຢູ່ຂອງເຊີບເວີ Pro Adhoc (localhost = multiple instances) ChangeMacSaveConfirm = Generate a new MAC address? @@ -827,7 +837,6 @@ Invalid IP or hostname = Invalid IP or hostname Minimum Timeout = Minimum timeout (override in ms, 0 = default) Misc = Miscellaneous (default = PSP compatibility) Network Initialized = ເລີ່ມການເຮັດວຽກລະບົບເຄື່ອຂ່າຍແລ້ວ -None = None Please change your Port Offset = Please change your port offset Port offset = ພອຣ໌ດຊົດເຊີຍ (0 = ຄ່າເລີ່ມຕົ້ນຂອງ PSP) Open PPSSPP Multiplayer Wiki Page = Open PPSSPP Multiplayer Wiki Page @@ -838,11 +847,9 @@ Quick Chat 3 = Quick chat 3 Quick Chat 4 = Quick chat 4 Quick Chat 5 = Quick chat 5 QuickChat = Quick chat +Randomize = Randomize Send = Send Send Discord Presence information = Send Discord "Rich Presence" information -Top Center = Top center -Top Left = Top left -Top Right = Top right Unable to find UPnP device = Unable to find UPnP device UPnP (port-forwarding) = UPnP (port forwarding) UPnP need to be reinitialized = UPnP need to be reinitialized @@ -852,6 +859,23 @@ Validating address... = Validating address... WLAN Channel = WLAN channel You're in Offline Mode, go to lobby or online hall = You're in offline mode, go to lobby or online hall +[PSPSettings] +Auto = Auto +Chinese (simplified) = Chinese (simplified) +Chinese (traditional) = Chinese (traditional) +Dutch = Dutch +English = English +French = French +Game language = Game language +German = German +Italian = Italian +Japanese = Japanese +Korean = Korean +Games often don't support all languages = Games often don't support all languages +Portuguese = Portuguese +Russian = Russian +Spanish = Spanish + [Pause] Cheats = ສູດໂກງ Continue = ຫຼິ້ນຕໍ່ diff --git a/assets/lang/lt-LT.ini b/assets/lang/lt-LT.ini index 9a6b7b6404..ef4daaf46b 100644 --- a/assets/lang/lt-LT.ini +++ b/assets/lang/lt-LT.ini @@ -6,12 +6,15 @@ Account = Account Achievement Unlocked = Achievement Unlocked Achievements = Achievements Achievements are disabled = Achievements are disabled +Can't log in to RetroAchievements right now = Can't log in to RetroAchievements right now Challenge Mode = Challenge Mode Challenge Mode (no savestates) = Challenge Mode (no savestates) +Contacting RetroAchievements server... = Contacting RetroAchievements server... Customize = Customize Earned = You have earned %d of %d achievements, and %d of %d points Encore Mode = Encore Mode How to use RetroAchievements = How to use RetroAchievements +In Encore mode - listings may be wrong below = In Encore mode - listings may be wrong below Leaderboard score submission = Leaderboard score submission Leaderboard submission is enabled = Leaderboard submission is enabled Leaderboards = Leaderboards @@ -88,6 +91,7 @@ Disable diagonal input = Disable diagonal input Double tap = Double tap Enable gesture control = Enable gesture control Enable standard shortcut keys = Enable standard shortcut keys +frames = frames Gesture = Gesture Gesture mapping = Gesture mapping Glowing borders = Glowing borders @@ -116,6 +120,7 @@ OnScreen = Ekrano liečiami kontrolės mygtukai Portrait = Status Portrait Reversed = Status (apsuktas) PSP Action Buttons = PSP konsolės veiksmų mygtukai +Rapid fire interval = Rapid fire interval Raw input = Raw input Repeat mode = Repeat mode Reset to defaults = Reset to defaults @@ -310,10 +315,14 @@ VFPU = VFPU * PSP res = * PSP res Active = Active Back = Atgal +Bottom Center = Bottom center +Bottom Left = Bottom left +Bottom Right = Bottom right Cancel = Atšaukti Center = Center -ChangingGPUBackends = Changing GPU backends requires PPSSPP to restart. Restart now? -ChangingInflightFrames = Changing graphics command buffering requires PPSSPP to restart. Restart now? +Center Left = Center left +Center Right = Center right +Changing this setting requires PPSSPP to restart. = Changing this setting requires PPSSPP to restart. Channel: = Channel: Choose PPSSPP save folder = Pasirinkite PPSSPP išsaugojimo aplanką Confirm Overwrite = Ar norite perrašyti šitus duomenis? @@ -361,6 +370,7 @@ Move Up = Move Up Network Connection = Interneto tinklas NEW DATA = NAUJI DUOMENYS No = Ne +None = None ObtainingIP = Obtaining IP address.\nPlease wait... OK = Gerai Old savedata detected = Aptikti seniau išsaugoti duomenys @@ -369,6 +379,7 @@ Password = Password Remove = Remove Reset = Perkrauti Resize = Perkeisti dydį +Restart = Restart Retry = Pabandyti dar kartą Save = Išsaugoti Save completed = Išsaugojimas baigtas. @@ -388,6 +399,9 @@ Supported = Supported There is no data = Čia nėra jokių duomenų. Toggle All = Perjungti visus Toggle List = Toggle list +Top Center = Top center +Top Left = Top left +Top Right = Top right Unsupported = Unsupported Username = Username When you save, it will load on a PSP, but not an older PPSSPP = When you save, it will load on a PSP, but not an older PPSSPP @@ -604,6 +618,7 @@ Stereo rendering = Stereo rendering Stretch = Stretch Texture Filter = Tekstūrų filtravimas Texture Filtering = Tekstūrų filtravimas +Texture replacement pack activated = Texture replacement pack activated Texture Scaling = Tekstūrų skalė Texture Shader = Texture shader Turn off Hardware Tessellation - unsupported = Turn off "hardware tessellation": unsupported @@ -789,11 +804,6 @@ AdHoc Server = Ad hoc server AdhocServer Failed to Bind Port = Ad hoc server failed to bind port AM: Data from Unknown Port = AM: Data from Unknown Port Auto = Auto -Bottom Center = Bottom center -Bottom Left = Bottom left -Bottom Right = Bottom right -Center Left = Center left -Center Right = Center right Change Mac Address = Change MAC address Change proAdhocServer Address = Change PRO ad hoc server IP address (localhost = multiple instances) ChangeMacSaveConfirm = Generate a new MAC address? @@ -827,7 +837,6 @@ Invalid IP or hostname = Invalid IP or hostname Minimum Timeout = Minimum timeout (override in ms, 0 = default) Misc = Miscellaneous (default = PSP compatibility) Network Initialized = Network initialized -None = None Please change your Port Offset = Please change your port offset Port offset = Port offset(0 = PSP compatibility) Open PPSSPP Multiplayer Wiki Page = Open PPSSPP Multiplayer Wiki Page @@ -838,11 +847,9 @@ Quick Chat 3 = Quick chat 3 Quick Chat 4 = Quick chat 4 Quick Chat 5 = Quick chat 5 QuickChat = Quick chat +Randomize = Randomize Send = Send Send Discord Presence information = Send Discord "Rich Presence" information -Top Center = Top center -Top Left = Top left -Top Right = Top right Unable to find UPnP device = Unable to find UPnP device UPnP (port-forwarding) = UPnP (port forwarding) UPnP need to be reinitialized = UPnP need to be reinitialized @@ -852,6 +859,23 @@ Validating address... = Validating address... WLAN Channel = WLAN channel You're in Offline Mode, go to lobby or online hall = You're in offline mode, go to lobby or online hall +[PSPSettings] +Auto = Auto +Chinese (simplified) = Chinese (simplified) +Chinese (traditional) = Chinese (traditional) +Dutch = Dutch +English = English +French = French +Game language = Game language +German = German +Italian = Italian +Japanese = Japanese +Korean = Korean +Games often don't support all languages = Games often don't support all languages +Portuguese = Portuguese +Russian = Russian +Spanish = Spanish + [Pause] Cheats = Kodai Continue = Tęsti diff --git a/assets/lang/ms_MY.ini b/assets/lang/ms_MY.ini index f98bab69f8..bf75fe60c2 100644 --- a/assets/lang/ms_MY.ini +++ b/assets/lang/ms_MY.ini @@ -6,12 +6,15 @@ Account = Account Achievement Unlocked = Achievement Unlocked Achievements = Achievements Achievements are disabled = Achievements are disabled +Can't log in to RetroAchievements right now = Can't log in to RetroAchievements right now Challenge Mode = Challenge Mode Challenge Mode (no savestates) = Challenge Mode (no savestates) +Contacting RetroAchievements server... = Contacting RetroAchievements server... Customize = Customize Earned = You have earned %d of %d achievements, and %d of %d points Encore Mode = Encore Mode How to use RetroAchievements = How to use RetroAchievements +In Encore mode - listings may be wrong below = In Encore mode - listings may be wrong below Leaderboard score submission = Leaderboard score submission Leaderboard submission is enabled = Leaderboard submission is enabled Leaderboards = Leaderboards @@ -88,6 +91,7 @@ Disable diagonal input = Disable diagonal input Double tap = Double tap Enable gesture control = Enable gesture control Enable standard shortcut keys = Enable standard shortcut keys +frames = frames Gesture = Gesture Gesture mapping = Gesture mapping Glowing borders = Glowing borders @@ -116,6 +120,7 @@ OnScreen = Kawalan sentuh di skrin Portrait = Portrait Portrait Reversed = Portrait reversed PSP Action Buttons = PSP action buttons +Rapid fire interval = Rapid fire interval Raw input = Raw input Repeat mode = Repeat mode Reset to defaults = Reset to defaults @@ -310,10 +315,14 @@ VFPU = VFPU * PSP res = * PSP res Active = Active Back = Balik +Bottom Center = Bottom center +Bottom Left = Bottom left +Bottom Right = Bottom right Cancel = Batal Center = Center -ChangingGPUBackends = Penukaran enjin GPU memerlukan PPSSPP untuk dimulakan semula. Mula Semula? -ChangingInflightFrames = Changing graphics command buffering requires PPSSPP to restart. Restart now? +Center Left = Center left +Center Right = Center right +Changing this setting requires PPSSPP to restart. = Changing this setting requires PPSSPP to restart. Channel: = Channel: Choose PPSSPP save folder = Choose PPSSPP save folder Confirm Overwrite = Anda hendak menulis ganti data ini? @@ -361,6 +370,7 @@ Move Up = Move Up Network Connection = Network Connection NEW DATA = DATA BAHARU No = Tidak +None = None ObtainingIP = Obtaining IP address.\nPlease wait... OK = OK Old savedata detected = Old savedata detected @@ -369,6 +379,7 @@ Password = Password Remove = Remove Reset = Mula semula Resize = Resize +Restart = Restart Retry = Cuba semula Save = Simpan Save completed = Menyimpan selesai. @@ -388,6 +399,9 @@ Supported = Supported There is no data = Tiada data disini. Toggle All = Periksa semua Toggle List = Toggle list +Top Center = Top center +Top Left = Top left +Top Right = Top right Unsupported = Tidak disokong Username = Username When you save, it will load on a PSP, but not an older PPSSPP = When you save, it will load on a PSP, but not an older PPSSPP @@ -604,6 +618,7 @@ Stereo rendering = Stereo rendering Stretch = Stretch Texture Filter = Penapis Tekstur Texture Filtering = Penapisan Tekstur +Texture replacement pack activated = Texture replacement pack activated Texture Scaling = Penskalaan Tekstur Texture Shader = Texture shader Turn off Hardware Tessellation - unsupported = Turn off "hardware tessellation": unsupported @@ -789,11 +804,6 @@ AdHoc Server = Ad hoc server AdhocServer Failed to Bind Port = Ad hoc server failed to bind port AM: Data from Unknown Port = AM: Data from Unknown Port Auto = Auto -Bottom Center = Bottom center -Bottom Left = Bottom left -Bottom Right = Bottom right -Center Left = Center left -Center Right = Center right Change Mac Address = Change MAC address Change proAdhocServer Address = Change PRO ad hoc server IP address (localhost = multiple instances) ChangeMacSaveConfirm = Generate a new MAC address? @@ -827,7 +837,6 @@ Invalid IP or hostname = Invalid IP or hostname Minimum Timeout = Minimum timeout (override in ms, 0 = default) Misc = Miscellaneous (default = PSP compatibility) Network Initialized = Network initialized -None = None Please change your Port Offset = Please change your port offset Port offset = Port offset(0 = PSP compatibility) Open PPSSPP Multiplayer Wiki Page = Open PPSSPP Multiplayer Wiki Page @@ -838,11 +847,9 @@ Quick Chat 3 = Quick chat 3 Quick Chat 4 = Quick chat 4 Quick Chat 5 = Quick chat 5 QuickChat = Quick chat +Randomize = Randomize Send = Send Send Discord Presence information = Send Discord "Rich Presence" information -Top Center = Top center -Top Left = Top left -Top Right = Top right Unable to find UPnP device = Unable to find UPnP device UPnP (port-forwarding) = UPnP (port forwarding) UPnP need to be reinitialized = UPnP need to be reinitialized @@ -852,6 +859,23 @@ Validating address... = Validating address... WLAN Channel = WLAN channel You're in Offline Mode, go to lobby or online hall = You're in offline mode, go to lobby or online hall +[PSPSettings] +Auto = Auto +Chinese (simplified) = Chinese (simplified) +Chinese (traditional) = Chinese (traditional) +Dutch = Dutch +English = English +French = French +Game language = Game language +German = German +Italian = Italian +Japanese = Japanese +Korean = Korean +Games often don't support all languages = Games often don't support all languages +Portuguese = Portuguese +Russian = Russian +Spanish = Spanish + [Pause] Cheats = Cheat Continue = Teruskan diff --git a/assets/lang/nl_NL.ini b/assets/lang/nl_NL.ini index 18b389c4d1..05ccf32aa3 100644 --- a/assets/lang/nl_NL.ini +++ b/assets/lang/nl_NL.ini @@ -6,12 +6,15 @@ Account = Account Achievement Unlocked = Achievement Unlocked Achievements = Achievements Achievements are disabled = Achievements are disabled +Can't log in to RetroAchievements right now = Can't log in to RetroAchievements right now Challenge Mode = Challenge Mode Challenge Mode (no savestates) = Challenge Mode (no savestates) +Contacting RetroAchievements server... = Contacting RetroAchievements server... Customize = Customize Earned = You have earned %d of %d achievements, and %d of %d points Encore Mode = Encore Mode How to use RetroAchievements = How to use RetroAchievements +In Encore mode - listings may be wrong below = In Encore mode - listings may be wrong below Leaderboard score submission = Leaderboard score submission Leaderboard submission is enabled = Leaderboard submission is enabled Leaderboards = Leaderboards @@ -88,6 +91,7 @@ Disable diagonal input = Disable diagonal input Double tap = Double tap Enable gesture control = Enable gesture control Enable standard shortcut keys = Enable standard shortcut keys +frames = frames Gesture = Gesture Gesture mapping = Gesture mapping Glowing borders = Glowing borders @@ -116,6 +120,7 @@ OnScreen = Aanraakbesturing op het scherm Portrait = Portret Portrait Reversed = Omgekeerd portret PSP Action Buttons = PSP-actietoetsen +Rapid fire interval = Rapid fire interval Raw input = Raw input Repeat mode = Repeat mode Reset to defaults = Reset to defaults @@ -310,10 +315,14 @@ VFPU = VFPU * PSP res = * PSP-resolutie Active = Active Back = Terug +Bottom Center = Bottom center +Bottom Left = Bottom left +Bottom Right = Bottom right Cancel = Annuleren Center = Centreren -ChangingGPUBackends = Als u de GPU-backend wijzigt moet u PPSSPP opnieuw\nopstarten. Nu opnieuw opstarten? -ChangingInflightFrames = Changing graphics command buffering requires PPSSPP to restart. Restart now? +Center Left = Center left +Center Right = Center right +Changing this setting requires PPSSPP to restart. = Changing this setting requires PPSSPP to restart. Channel: = Channel: Choose PPSSPP save folder = PPSSPP-opslagmap selecteren Confirm Overwrite = Wilt u de opgeslagen data overschrijven? @@ -361,6 +370,7 @@ Move Up = Move Up Network Connection = Netwerkverbinding NEW DATA = NIEUWE DATA No = Nee +None = None ObtainingIP = Obtaining IP address.\nPlease wait... OK = OK Old savedata detected = Oude savedata gedetecteerd @@ -369,6 +379,7 @@ Password = Password Remove = Remove Reset = Herstellen Resize = Schalen +Restart = Restart Retry = Opnieuw Save = Opslaan Save completed = Het opslaan is voltooid. @@ -388,6 +399,9 @@ Supported = Ondersteund There is no data = Er zijn geen data. Toggle All = Alles schakelen Toggle List = Toggle list +Top Center = Top center +Top Left = Top left +Top Right = Top right Unsupported = Niet ondersteund Username = Username When you save, it will load on a PSP, but not an older PPSSPP = Als u opslaat kunt u de opgeslagen data laden op\nPSP-systemen, maar niet op oudere PPSSPP-versies. @@ -604,6 +618,7 @@ Stereo rendering = Stereo rendering Stretch = Uitrekken Texture Filter = Texturefilter Texture Filtering = Texturefilter +Texture replacement pack activated = Texture replacement pack activated Texture Scaling = Textures schalen Texture Shader = Texture shader Turn off Hardware Tessellation - unsupported = Schakel "hardwarebetegeling" uit: niet ondersteund @@ -789,11 +804,6 @@ AdHoc Server = Ad hoc server AdhocServer Failed to Bind Port = Ad hoc server failed to bind port AM: Data from Unknown Port = AM: Data from Unknown Port Auto = Auto -Bottom Center = Bottom center -Bottom Left = Bottom left -Bottom Right = Bottom right -Center Left = Center left -Center Right = Center right Change Mac Address = MAC-adres wijzigen Change proAdhocServer Address = IP-adres van PRO Ad hoc-server wijzigen (localhost = multiple instances) ChangeMacSaveConfirm = Generate a new MAC address? @@ -827,7 +837,6 @@ Invalid IP or hostname = Invalid IP or hostname Minimum Timeout = Minimum timeout (override in ms, 0 = default) Misc = Miscellaneous (default = PSP compatibility) Network Initialized = Netwerk geïnitialiseerd -None = None Please change your Port Offset = Please change your port offset Port offset = Poortoffset (0 = PSP-compatibiliteit) Open PPSSPP Multiplayer Wiki Page = Open PPSSPP Multiplayer Wiki Page @@ -838,11 +847,9 @@ Quick Chat 3 = Quick chat 3 Quick Chat 4 = Quick chat 4 Quick Chat 5 = Quick chat 5 QuickChat = Quick chat +Randomize = Randomize Send = Send Send Discord Presence information = Send Discord "Rich Presence" information -Top Center = Top center -Top Left = Top left -Top Right = Top right Unable to find UPnP device = Unable to find UPnP device UPnP (port-forwarding) = UPnP (port forwarding) UPnP need to be reinitialized = UPnP need to be reinitialized @@ -852,6 +859,23 @@ Validating address... = Validating address... WLAN Channel = WLAN channel You're in Offline Mode, go to lobby or online hall = You're in offline mode, go to lobby or online hall +[PSPSettings] +Auto = Auto +Chinese (simplified) = Chinese (simplified) +Chinese (traditional) = Chinese (traditional) +Dutch = Dutch +English = English +French = French +Game language = Game language +German = German +Italian = Italian +Japanese = Japanese +Korean = Korean +Games often don't support all languages = Games often don't support all languages +Portuguese = Portuguese +Russian = Russian +Spanish = Spanish + [Pause] Cheats = Cheats Continue = Doorgaan diff --git a/assets/lang/no_NO.ini b/assets/lang/no_NO.ini index bd09ed8688..69199fd326 100644 --- a/assets/lang/no_NO.ini +++ b/assets/lang/no_NO.ini @@ -6,12 +6,15 @@ Account = Account Achievement Unlocked = Achievement Unlocked Achievements = Achievements Achievements are disabled = Achievements are disabled +Can't log in to RetroAchievements right now = Can't log in to RetroAchievements right now Challenge Mode = Challenge Mode Challenge Mode (no savestates) = Challenge Mode (no savestates) +Contacting RetroAchievements server... = Contacting RetroAchievements server... Customize = Customize Earned = You have earned %d of %d achievements, and %d of %d points Encore Mode = Encore Mode How to use RetroAchievements = How to use RetroAchievements +In Encore mode - listings may be wrong below = In Encore mode - listings may be wrong below Leaderboard score submission = Leaderboard score submission Leaderboard submission is enabled = Leaderboard submission is enabled Leaderboards = Leaderboards @@ -88,6 +91,7 @@ Disable diagonal input = Disable diagonal input Double tap = Double tap Enable gesture control = Enable gesture control Enable standard shortcut keys = Enable standard shortcut keys +frames = frames Gesture = Gesture Gesture mapping = Gesture mapping Glowing borders = Glowing borders @@ -116,6 +120,7 @@ OnScreen = Touch-kontroller på skjermen Portrait = Portrait Portrait Reversed = Portrait reversed PSP Action Buttons = PSP action buttons +Rapid fire interval = Rapid fire interval Raw input = Raw input Repeat mode = Repeat mode Reset to defaults = Reset to defaults @@ -310,10 +315,14 @@ VFPU = VFPU * PSP res = * PSP res Active = Active Back = Tilbake +Bottom Center = Bottom center +Bottom Left = Bottom left +Bottom Right = Bottom right Cancel = Cancel Center = Center -ChangingGPUBackends = Changing GPU backends requires PPSSPP to restart. Restart now? -ChangingInflightFrames = Changing graphics command buffering requires PPSSPP to restart. Restart now? +Center Left = Center left +Center Right = Center right +Changing this setting requires PPSSPP to restart. = Changing this setting requires PPSSPP to restart. Channel: = Channel: Choose PPSSPP save folder = Choose PPSSPP save folder Confirm Overwrite = Do you want to overwrite the data? @@ -361,6 +370,7 @@ Move Up = Move Up Network Connection = Network Connection NEW DATA = NEW DATA No = Nei +None = None ObtainingIP = Obtaining IP address.\nPlease wait... OK = OK Old savedata detected = Old savedata detected @@ -369,6 +379,7 @@ Password = Password Remove = Remove Reset = Reset Resize = Resize +Restart = Restart Retry = Retry Save = Save Save completed = Ferdig lagret. @@ -388,6 +399,9 @@ Supported = Supported There is no data = Det finnes ingen data. Toggle All = Toggle all Toggle List = Toggle list +Top Center = Top center +Top Left = Top left +Top Right = Top right Unsupported = Unsupported Username = Username When you save, it will load on a PSP, but not an older PPSSPP = When you save, it will load on a PSP, but not an older PPSSPP @@ -604,6 +618,7 @@ Stereo rendering = Stereo rendering Stretch = Stretch Texture Filter = Tekstur filtrering Texture Filtering = Tekstur filtrering +Texture replacement pack activated = Texture replacement pack activated Texture Scaling = Teksturskalning Texture Shader = Texture shader Turn off Hardware Tessellation - unsupported = Turn off "hardware tessellation": unsupported @@ -789,11 +804,6 @@ AdHoc Server = Ad hoc server AdhocServer Failed to Bind Port = Ad hoc server failed to bind port AM: Data from Unknown Port = AM: Data from Unknown Port Auto = Auto -Bottom Center = Bottom center -Bottom Left = Bottom left -Bottom Right = Bottom right -Center Left = Center left -Center Right = Center right Change Mac Address = Change MAC address Change proAdhocServer Address = Change PRO ad hoc server IP address (localhost = multiple instances) ChangeMacSaveConfirm = Generate a new MAC address? @@ -827,7 +837,6 @@ Invalid IP or hostname = Invalid IP or hostname Minimum Timeout = Minimum timeout (override in ms, 0 = default) Misc = Miscellaneous (default = PSP compatibility) Network Initialized = Network initialized -None = None Please change your Port Offset = Please change your port offset Port offset = Port offset(0 = PSP compatibility) Open PPSSPP Multiplayer Wiki Page = Open PPSSPP Multiplayer Wiki Page @@ -838,11 +847,9 @@ Quick Chat 3 = Quick chat 3 Quick Chat 4 = Quick chat 4 Quick Chat 5 = Quick chat 5 QuickChat = Quick chat +Randomize = Randomize Send = Send Send Discord Presence information = Send Discord "Rich Presence" information -Top Center = Top center -Top Left = Top left -Top Right = Top right Unable to find UPnP device = Unable to find UPnP device UPnP (port-forwarding) = UPnP (port forwarding) UPnP need to be reinitialized = UPnP need to be reinitialized @@ -852,6 +859,23 @@ Validating address... = Validating address... WLAN Channel = WLAN channel You're in Offline Mode, go to lobby or online hall = You're in offline mode, go to lobby or online hall +[PSPSettings] +Auto = Auto +Chinese (simplified) = Chinese (simplified) +Chinese (traditional) = Chinese (traditional) +Dutch = Dutch +English = English +French = French +Game language = Game language +German = German +Italian = Italian +Japanese = Japanese +Korean = Korean +Games often don't support all languages = Games often don't support all languages +Portuguese = Portuguese +Russian = Russian +Spanish = Spanish + [Pause] Cheats = Cheats Continue = Fortsettelse diff --git a/assets/lang/pl_PL.ini b/assets/lang/pl_PL.ini index a3007551db..a38b7e3077 100644 --- a/assets/lang/pl_PL.ini +++ b/assets/lang/pl_PL.ini @@ -6,12 +6,15 @@ Account = Konto Achievement Unlocked = Achievement Unlocked Achievements = Osiągnięcia Achievements are disabled = Achievements are disabled +Can't log in to RetroAchievements right now = Can't log in to RetroAchievements right now Challenge Mode = Tryb Wyzwania Challenge Mode (no savestates) = Tryb Wyzwania (bez zapisów stanu) +Contacting RetroAchievements server... = Contacting RetroAchievements server... Customize = Customize Earned = Zdobyłeś %d z %d osiągnięć i %d z %d punktów Encore Mode = Encore Mode How to use RetroAchievements = Jak używać RetroAchievements +In Encore mode - listings may be wrong below = In Encore mode - listings may be wrong below Leaderboard score submission = Leaderboard score submission Leaderboard submission is enabled = Leaderboard submission is enabled Leaderboards = Tabele wyników @@ -88,6 +91,7 @@ Disable diagonal input = Wyłącz wejście cyfrowe Double tap = Dwukrotne dotknięcie Enable gesture control = Włącz kontrolę kestami Enable standard shortcut keys = Włącz standardowe klawisze skrótów +frames = frames Gesture = Gesty Gesture mapping = Konfiguracja gestów Glowing borders = Świecące krawędzie @@ -116,6 +120,7 @@ OnScreen = Przyciski ekranowe Portrait = Pionowo Portrait Reversed = Odwrócone pionowo PSP Action Buttons = Klawisze akcji PSP +Rapid fire interval = Rapid fire interval Raw input = Zwykłe wejście Repeat mode = Tryb powtarzania Reset to defaults = Domyślne @@ -310,10 +315,14 @@ VFPU = VFPU * PSP res = * rozdz. PSP Active = Aktywny Back = Powrót +Bottom Center = Dolny środek +Bottom Left = Dolne lewo +Bottom Right = Dolne prawo Cancel = Anuluj Center = Wyśrodkuj -ChangingGPUBackends = Zmiana sterownika grafiki wymaga restartu PPSSPP. Zrestartować teraz? -ChangingInflightFrames = Zmiana komend buffera grafiki wymaka restartu PPSSPP. Zrestartować teraz? +Center Left = Środkowe lewo +Center Right = Środkowe prawo +Changing this setting requires PPSSPP to restart. = Zmiana tego ustawienia wymaga ponownego uruchomienia PPSSPP. Channel: = Kanał: Choose PPSSPP save folder = Wybierz folder do zapisu stanów PPSSPP Confirm Overwrite = Czy chcesz nadpisać te dane? @@ -361,6 +370,7 @@ Move Up = Przenieś w górę Network Connection = Połączenie sieciowe NEW DATA = NOWE DANE No = Nie +None = Nic ObtainingIP = Uzyskiwanie adresu IP.\nProszę czekać... OK = OK Old savedata detected = Wykryto stary zapis gry @@ -369,6 +379,7 @@ Password = Hasło Remove = Usuń Reset = Resetuj Resize = Przeskaluj +Restart = Uruchom ponownie Retry = Spróbuj ponownie Save = Zapisz Save completed = Zapisywanie zakończone. @@ -388,6 +399,9 @@ Supported = Wspierane There is no data = Brak danych. Toggle All = Wybierz wszystko Toggle List = Wybierz listę +Top Center = Górny środek +Top Left = Górne lewo +Top Right = Górne prawo Unsupported = Niewspierane Username = Nazwa użytkownika When you save, it will load on a PSP, but not an older PPSSPP = Jeśli zapiszesz, PSP wczyta ten zapis gry, ale starszy PPSSPP już nie. @@ -608,6 +622,7 @@ Stereo rendering = Renderowanie stereoskopowe Stretch = Rozciąganie Texture Filter = Filtrowanie tekstur Texture Filtering = Filtrowanie tekstur +Texture replacement pack activated = Texture replacement pack activated Texture Scaling = Skalowanie tekstur Texture Shader = Shadery tekstur Turn off Hardware Tessellation - unsupported = Wyłącz sprzętową teselację: opcja nie jest wspierana @@ -793,11 +808,6 @@ AdHoc Server = Serwer Ad-Hoc AdhocServer Failed to Bind Port = Błąd przy przypisywaniu portu na serwerze Ad-Hoc AM: Data from Unknown Port = AM: Dane z nieznanego portu Auto = Automatycznie -Bottom Center = Dolny środek -Bottom Left = Dolne lewo -Bottom Right = Dolne prawo -Center Left = Środkowe lewo -Center Right = Środkowe prawo Change Mac Address = Zmień adres MAC Change proAdhocServer Address = Zmień adres IP serwera PRO dla trybu ad hoc (localhost = multiple instances) ChangeMacSaveConfirm = Wygenerować nowy adres MAC? @@ -831,7 +841,6 @@ Invalid IP or hostname = Niepoprawny adres IP lub nazwa hosta Minimum Timeout = Minimalny czas oczekiwania (podaj w ms, domyślnie 0) Misc = Inne (domyślnie = Kompatybilność z PSP) Network Initialized = Sieć została zainicjowana -None = Nic Please change your Port Offset = Proszę zmienić port Port offset = Zmień port (0 = zgodne z PSP) Open PPSSPP Multiplayer Wiki Page = Przejdź do strony PPSSPP pomocy dla wielu graczy. @@ -842,11 +851,9 @@ Quick Chat 3 = Szybki czat 3 Quick Chat 4 = Szybki czat 4 Quick Chat 5 = Szybki czat 5 QuickChat = Szybki czat +Randomize = Randomize Send = Wyślij Send Discord Presence information = Przesyłaj informacje Discord Rich Presence -Top Center = Górny środek -Top Left = Górne lewo -Top Right = Górne prawo Unable to find UPnP device = Nie udało się zlokalizować urządzenia UPnP UPnP (port-forwarding) = UPnP (przekierowanie portów) UPnP need to be reinitialized = Należy ponownie zainicjować UPnP @@ -856,6 +863,23 @@ Validating address... = Uwierzytelnianie addresu... WLAN Channel = Kanał WLAN You're in Offline Mode, go to lobby or online hall = Jesteś w trybie offline; wejdź do lobby lub pokoju online +[PSPSettings] +Auto = Auto +Chinese (simplified) = Chinese (simplified) +Chinese (traditional) = Chinese (traditional) +Dutch = Dutch +English = English +French = French +Game language = Game language +German = German +Italian = Italian +Japanese = Japanese +Korean = Korean +Games often don't support all languages = Games often don't support all languages +Portuguese = Portuguese +Russian = Russian +Spanish = Spanish + [Pause] Cheats = Kody Continue = Kontynuuj diff --git a/assets/lang/pt_BR.ini b/assets/lang/pt_BR.ini index 4f48b855e5..a476532e69 100644 --- a/assets/lang/pt_BR.ini +++ b/assets/lang/pt_BR.ini @@ -30,12 +30,15 @@ Account = Conta Achievement Unlocked = Achievement Unlocked Achievements = Conquistas Achievements are disabled = Achievements are disabled +Can't log in to RetroAchievements right now = Can't log in to RetroAchievements right now Challenge Mode = Modo de Desafio Challenge Mode (no savestates) = Modo de Desafio (sem save states) +Contacting RetroAchievements server... = Contacting RetroAchievements server... Customize = Customize Earned = Você ganhou %d de %d conquistas e %d de %d pontos Encore Mode = Encore Mode How to use RetroAchievements = Como usar RetroAchievements +In Encore mode - listings may be wrong below = In Encore mode - listings may be wrong below Leaderboard score submission = Leaderboard score submission Leaderboard submission is enabled = A submissão a tabela de classificação está ativada Leaderboards = Tabelas de Classificação @@ -112,6 +115,7 @@ Disable diagonal input = Desativar entrada diagonal Double tap = Toque duplo Enable gesture control = Ativar controle dos gestos Enable standard shortcut keys = Ativar teclas padrão dos atalhos +frames = frames Gesture = Gesto Gesture mapping = Mapeamento dos gestos Glowing borders = Bordas brilhantes @@ -140,6 +144,7 @@ OnScreen = Controles do toque na tela Portrait = Retrato Portrait Reversed = Retrato invertido PSP Action Buttons = Botões de ação do PSP +Rapid fire interval = Rapid fire interval Raw input = Entrada natural dos dados Reset to defaults = Resetar para os padrões Screen Rotation = Rotação da tela @@ -334,10 +339,14 @@ VFPU = VFPU * PSP res = * Resolução do PSP Active = Ativo Back = Voltar +Bottom Center = Centro do rodapé +Bottom Left = A esquerda do rodapé +Bottom Right = A direita do rodapé Cancel = Cancelar Center = Centro -ChangingGPUBackends = Mudar os backends da GPU requer que o PPSSPP reinicie. Reiniciar agora? -ChangingInflightFrames = Mudar o buffer do comando dos gráficos requer que o PPSSPP reinicie. Reiniciar agora? +Center Left = Centralizar a esquerda +Center Right = Centralizar a direita +Changing this setting requires PPSSPP to restart. = A alteração dessa configuração requer a reinicialização do PPSSPP. Channel: = Canal: Choose PPSSPP save folder = Escolha a pasta dos saves do PPSSPP Confirm Overwrite = Você quer sobrescrever os dados? @@ -385,6 +394,7 @@ Move Down = Mover pra Baixo Network Connection = Conexão de rede NEW DATA = NOVOS DADOS No = Não +None = Nenhum ObtainingIP = Obtendo endereço de IP.\nPor favor espere... OK = Ok Old savedata detected = Dados antigos do save detectados @@ -393,6 +403,7 @@ Password = Senha Remove = Remover Reset = Resetar Resize = Redimensionar +Restart = Reiniciar Retry = Tentar de novo Save = Salvar Save completed = Salvamento completado. @@ -412,6 +423,9 @@ Supported = Suportado There is no data = Não há dados. Toggle All = Alternar todos Toggle List = Alternar lista +Top Center = Centro superior +Top Left = Superior esquerdo +Top Right = Superior direito Unsupported = Não suportado Username = Nome de usuário When you save, it will load on a PSP, but not an older PPSSPP = Quando você salvar ele carregará num PSP mas não num PPSSPP mais antigo @@ -628,6 +642,7 @@ Stereo rendering = Renderização do estéreo Stretch = Esticar Texture Filter = Filtragem das texturas Texture Filtering = Filtragem das texturas +Texture replacement pack activated = Texture replacement pack activated Texture Scaling = Dimensionamento das texturas Texture Shader = Shader das texturas Turn off Hardware Tessellation - unsupported = Desliga a "Tesselação por hardware": não suportado. @@ -784,11 +799,6 @@ AdHoc Server = Servidor Ad-hoc AdhocServer Failed to Bind Port = O servidor Ad-hoc falhou em se conectar a porta AM: Data from Unknown Port = AM: Dados de uma Porta Desconhecida Auto = Auto -Bottom Center = Centro do rodapé -Bottom Left = A esquerda do rodapé -Bottom Right = A direita do rodapé -Center Left = Centralizar a esquerda -Center Right = Centralizar a direita Change Mac Address = Mudar o endereço do MAC ChangeMacSaveConfirm = Gerar um novo endereço pro MAC? ChangeMacSaveWarning = Alguns jogos verificam o endereço do MAC quando carregam os dados do save então isto pode quebrar os saves antigos. @@ -822,7 +832,6 @@ Invalid IP or hostname = IP ou nome do hospedeiro inválido Minimum Timeout = Tempo mínimo pra encerrar (substituição em ms, 0 = padrão) Misc = Miscelânea (padrão = compatibilidade com o PSP) Network Initialized = Rede inicializada -None = Nenhum Please change your Port Offset = Por favor mude seu deslocamento da porta Port offset = Deslocamento da porta (0 = Compatibilidade com o PSP) Open PPSSPP Multiplayer Wiki Page = Abrir a Página do Multiplayer do Wiki do PPSSPP @@ -833,11 +842,9 @@ Quick Chat 3 = Bate-papo rápido 3 Quick Chat 4 = Bate-papo rápido 4 Quick Chat 5 = Bate-papo rápido 5 QuickChat = Bate-papo rápido +Randomize = Randomize Send = Enviar Send Discord Presence information = Enviar informação da "Presença Rica" no Discord -Top Center = Centro superior -Top Left = Superior esquerdo -Top Right = Superior direito Unable to find UPnP device = Incapaz de achar o dispositivo UPnP UPnP (port-forwarding) = UPnP (abertura das portas) UPnP need to be reinitialized = O UPnP precisa ser reinicializado @@ -847,6 +854,23 @@ Validating address... = Validando endereço... WLAN Channel = Canal do WLAN You're in Offline Mode, go to lobby or online hall = Você está no modo offline, vá pro lobby ou pro salão online +[PSPSettings] +Auto = Auto +Chinese (simplified) = Chinese (simplified) +Chinese (traditional) = Chinese (traditional) +Dutch = Dutch +English = English +French = French +Game language = Game language +German = German +Italian = Italian +Japanese = Japanese +Korean = Korean +Games often don't support all languages = Games often don't support all languages +Portuguese = Portuguese +Russian = Russian +Spanish = Spanish + [Pause] Cheats = Trapaças Continue = Continuar diff --git a/assets/lang/pt_PT.ini b/assets/lang/pt_PT.ini index 487416f978..1f9da3e337 100644 --- a/assets/lang/pt_PT.ini +++ b/assets/lang/pt_PT.ini @@ -30,12 +30,15 @@ Account = Account Achievement Unlocked = Achievement Unlocked Achievements = Achievements Achievements are disabled = Achievements are disabled +Can't log in to RetroAchievements right now = Can't log in to RetroAchievements right now Challenge Mode = Challenge Mode Challenge Mode (no savestates) = Challenge Mode (no savestates) +Contacting RetroAchievements server... = Contacting RetroAchievements server... Customize = Customize Earned = You have earned %d of %d achievements, and %d of %d points Encore Mode = Encore Mode How to use RetroAchievements = How to use RetroAchievements +In Encore mode - listings may be wrong below = In Encore mode - listings may be wrong below Leaderboard score submission = Leaderboard score submission Leaderboard submission is enabled = Leaderboard submission is enabled Leaderboards = Leaderboards @@ -112,6 +115,7 @@ Disable diagonal input = Desativar entrada diagonal Double tap = Toque duplo Enable gesture control = Ativar controlo dos gestos Enable standard shortcut keys = Ativar teclas padrão dos atalhos +frames = frames Gesture = Gesto Gesture mapping = Mapeamento dos Gestos Glowing borders = Bordas brilhantes @@ -140,6 +144,7 @@ OnScreen = Controlos no ecrã Portrait = Retrato Portrait Reversed = Retrato invertido PSP Action Buttons = Botões de ação do PSP +Rapid fire interval = Rapid fire interval Raw input = Entrada natural dos dados Reset to defaults = Restaurar para os padrões Screen Rotation = Rotação do ecrã @@ -334,10 +339,14 @@ VFPU = VFPU * PSP res = * Resolução do PSP Active = Ativo Back = Voltar +Bottom Center = Centro do rodapé +Bottom Left = A esquerda do rodapé +Bottom Right = A direita do rodapé Cancel = Cancelar Center = Centro -ChangingGPUBackends = Mudar os backends da GPU requer que o PPSSPP reinicie. Reiniciar agora? -ChangingInflightFrames = Mudar o buffer do comando dos gráficos requer que o PPSSPP reinicie. Reiniciar agora? +Center Left = Centralizar a esquerda +Center Right = Centralizar a direita +Changing this setting requires PPSSPP to restart. = A alteração dessa configuração requer a reinicialização do PPSSPP. Channel: = Canal: Choose PPSSPP save folder = Escolha a pasta de save do PPSSPP Confirm Overwrite = Queres sobrescrever os dados? @@ -385,6 +394,7 @@ Move Up = Move Up Network Connection = Conexão de rede NEW DATA = NOVOS DADOS No = Não +None = Nenhum ObtainingIP = Obtendo o Endereço de IP.\nPor favor espere... OK = Ok Old savedata detected = Dados antigos do save detectados @@ -393,6 +403,7 @@ Password = Password Remove = Remove Reset = Reiniciar Resize = Redimensionar +Restart = Reiniciar Retry = Tentar de novo Save = Salvar Save completed = Salvamento completado. @@ -412,6 +423,9 @@ Supported = Suportado There is no data = Não existem dados. Toggle All = Alternar todos Toggle List = Alternar lista +Top Center = Centro superior +Top Left = Superior esquerdo +Top Right = Superior direito Unsupported = Não suportado Username = Username When you save, it will load on a PSP, but not an older PPSSPP = Quando gravar, os dados vão carregar numa PSP, mas não num PPSSPP antigo. @@ -628,6 +642,7 @@ Stereo rendering = Stereo rendering Stretch = Esticamento Texture Filter = Filtração das texturas Texture Filtering = Filtração das texturas +Texture replacement pack activated = Texture replacement pack activated Texture Scaling = Dimensionamento das texturas Texture Shader = Shader das texturas Turn off Hardware Tessellation - unsupported = Desliga a "Tesselação por hardware": não suportado. @@ -784,11 +799,6 @@ AdHoc Server = Servidor Ad-hoc AdhocServer Failed to Bind Port = O servidor Ad-hoc falhou a conectar-se à porta. AM: Data from Unknown Port = AM: Dados de uma Porta Desconhecida. Auto = Automático -Bottom Center = Centro do rodapé -Bottom Left = A esquerda do rodapé -Bottom Right = A direita do rodapé -Center Left = Centralizar a esquerda -Center Right = Centralizar a direita Change Mac Address = Mudar Endereço de MAC Change proAdhocServer Address = Mudar o Endereço de IP do servidor Ad Hoc PRO (localhost = múltiplas instâncias) ChangeMacSaveConfirm = Generate a new MAC address? @@ -822,7 +832,6 @@ Invalid IP or hostname = IP ou nome do Hospedeiro inválido Minimum Timeout = Tempo mínimo para encerrar (substituição em ms, 0 = padrão) Misc = Miscelânea (padrão = compatibilidade com o PSP) Network Initialized = Rede inicializada -None = Nenhum Please change your Port Offset = Por favor mude o seu deslocamento da porta Port offset = Deslocamento da porta (0 = Compatibilidade com o PSP) Open PPSSPP Multiplayer Wiki Page = Abrir a Página do Multiplayer da Wiki do PPSSPP @@ -833,11 +842,9 @@ Quick Chat 3 = Bate-papo rápido 3 Quick Chat 4 = Bate-papo rápido 4 Quick Chat 5 = Bate-papo rápido 5 QuickChat = Bate-papo rápido +Randomize = Randomize Send = Enviar Send Discord Presence information = Enviar informação de "Presença Rica" no Discord -Top Center = Centro superior -Top Left = Superior esquerdo -Top Right = Superior direito Unable to find UPnP device = Não é possível encontrar o dispositivo UPnP UPnP (port-forwarding) = UPnP (abertura das portas) UPnP need to be reinitialized = O UPnP precisa ser reiniciado @@ -847,6 +854,23 @@ Validating address... = A validar Endereço... WLAN Channel = Canal do WLAN You're in Offline Mode, go to lobby or online hall = Estás no modo Offline, vá para o lobby ou pro salão Online +[PSPSettings] +Auto = Auto +Chinese (simplified) = Chinese (simplified) +Chinese (traditional) = Chinese (traditional) +Dutch = Dutch +English = English +French = French +Game language = Game language +German = German +Italian = Italian +Japanese = Japanese +Korean = Korean +Games often don't support all languages = Games often don't support all languages +Portuguese = Portuguese +Russian = Russian +Spanish = Spanish + [Pause] Cheats = Cheats Continue = Continuar diff --git a/assets/lang/ro_RO.ini b/assets/lang/ro_RO.ini index 21c2ff79f7..8355f16c1b 100644 --- a/assets/lang/ro_RO.ini +++ b/assets/lang/ro_RO.ini @@ -6,12 +6,15 @@ Account = Account Achievement Unlocked = Achievement Unlocked Achievements = Achievements Achievements are disabled = Achievements are disabled +Can't log in to RetroAchievements right now = Can't log in to RetroAchievements right now Challenge Mode = Challenge Mode Challenge Mode (no savestates) = Challenge Mode (no savestates) +Contacting RetroAchievements server... = Contacting RetroAchievements server... Customize = Customize Earned = You have earned %d of %d achievements, and %d of %d points Encore Mode = Encore Mode How to use RetroAchievements = How to use RetroAchievements +In Encore mode - listings may be wrong below = In Encore mode - listings may be wrong below Leaderboard score submission = Leaderboard score submission Leaderboard submission is enabled = Leaderboard submission is enabled Leaderboards = Leaderboards @@ -88,6 +91,7 @@ Disable diagonal input = Disable diagonal input Double tap = Double tap Enable gesture control = Enable gesture control Enable standard shortcut keys = Enable standard shortcut keys +frames = frames Gesture = Gesture Gesture mapping = Gesture mapping Glowing borders = Glowing borders @@ -116,6 +120,7 @@ OnScreen = Controale Touch pe Ecran Portrait = Portret Portrait Reversed = Portret intors PSP Action Buttons = Butoane acțiune PSP +Rapid fire interval = Rapid fire interval Raw input = Raw input Repeat mode = Repeat mode Reset to defaults = Reset to defaults @@ -311,10 +316,14 @@ VFPU = VFPU * PSP res = * PSP res Active = Active Back = Înapoi +Bottom Center = Bottom center +Bottom Left = Bottom left +Bottom Right = Bottom right Cancel = Ignoră Center = Center -ChangingGPUBackends = Schimbarea modului GPU necesită restart. Restart acum? -ChangingInflightFrames = Changing graphics command buffering requires PPSSPP to restart. Restart now? +Center Left = Center left +Center Right = Center right +Changing this setting requires PPSSPP to restart. = Changing this setting requires PPSSPP to restart. Channel: = Channel: Choose PPSSPP save folder = Alege folder pt. salvări PPSSPP Confirm Overwrite = Vrei să suprascrii datele? @@ -362,6 +371,7 @@ Move Up = Move Up Network Connection = Conexiune la rețea NEW DATA = DATE NOI No = Nu +None = None ObtainingIP = Obtaining IP address.\nPlease wait... OK = OK Old savedata detected = Date vechi salvate detectate @@ -370,6 +380,7 @@ Password = Password Remove = Remove Reset = Resetare Resize = Modificare marime +Restart = Restart Retry = Reîncearcă Save = Salvează Save completed = Salvare Completă @@ -389,6 +400,9 @@ Supported = Supported There is no data = Salvare inexistentă Toggle All = Schimbă tot Toggle List = Toggle list +Top Center = Top center +Top Left = Top left +Top Right = Top right Unsupported = Unsupported Username = Username When you save, it will load on a PSP, but not an older PPSSPP = Când salvezi, va încărca pe un PSP, dar nu un PPSSPP mai vechi @@ -605,6 +619,7 @@ Stereo rendering = Stereo rendering Stretch = Stretch Texture Filter = Filtru texturi Texture Filtering = Filtrare texturi +Texture replacement pack activated = Texture replacement pack activated Texture Scaling = Scalare texturi Texture Shader = Texture shader Turn off Hardware Tessellation - unsupported = Turn off "hardware tessellation": unsupported @@ -790,11 +805,6 @@ AdHoc Server = Ad hoc server AdhocServer Failed to Bind Port = Ad hoc server failed to bind port AM: Data from Unknown Port = AM: Data from Unknown Port Auto = Auto -Bottom Center = Bottom center -Bottom Left = Bottom left -Bottom Right = Bottom right -Center Left = Center left -Center Right = Center right Change Mac Address = Schimbă adresa MAC Change proAdhocServer Address = Schimbă adresa IP a serverului PRO ad hoc (localhost = multiple instances) ChangeMacSaveConfirm = Generate a new MAC address? @@ -828,7 +838,6 @@ Invalid IP or hostname = Invalid IP or hostname Minimum Timeout = Minimum timeout (override in ms, 0 = default) Misc = Miscellaneous (default = PSP compatibility) Network Initialized = Rețea inițializată -None = None Please change your Port Offset = Please change your port offset Port offset = Port offset(0 = PSP compatibility) Open PPSSPP Multiplayer Wiki Page = Open PPSSPP Multiplayer Wiki Page @@ -839,11 +848,9 @@ Quick Chat 3 = Quick chat 3 Quick Chat 4 = Quick chat 4 Quick Chat 5 = Quick chat 5 QuickChat = Quick chat +Randomize = Randomize Send = Send Send Discord Presence information = Send Discord "Rich Presence" information -Top Center = Top center -Top Left = Top left -Top Right = Top right Unable to find UPnP device = Unable to find UPnP device UPnP (port-forwarding) = UPnP (port forwarding) UPnP need to be reinitialized = UPnP need to be reinitialized @@ -853,6 +860,23 @@ Validating address... = Validating address... WLAN Channel = WLAN channel You're in Offline Mode, go to lobby or online hall = You're in offline mode, go to lobby or online hall +[PSPSettings] +Auto = Auto +Chinese (simplified) = Chinese (simplified) +Chinese (traditional) = Chinese (traditional) +Dutch = Dutch +English = English +French = French +Game language = Game language +German = German +Italian = Italian +Japanese = Japanese +Korean = Korean +Games often don't support all languages = Games often don't support all languages +Portuguese = Portuguese +Russian = Russian +Spanish = Spanish + [Pause] Cheats = Trișare Continue = Continuă diff --git a/assets/lang/ru_RU.ini b/assets/lang/ru_RU.ini index 9f6c0ad72c..1f656a96d1 100644 --- a/assets/lang/ru_RU.ini +++ b/assets/lang/ru_RU.ini @@ -6,12 +6,15 @@ Account = Account Achievement Unlocked = Achievement Unlocked Achievements = Achievements Achievements are disabled = Achievements are disabled +Can't log in to RetroAchievements right now = Can't log in to RetroAchievements right now Challenge Mode = Challenge Mode Challenge Mode (no savestates) = Challenge Mode (no savestates) +Contacting RetroAchievements server... = Contacting RetroAchievements server... Customize = Customize Earned = You have earned %d of %d achievements, and %d of %d points Encore Mode = Encore Mode How to use RetroAchievements = How to use RetroAchievements +In Encore mode - listings may be wrong below = In Encore mode - listings may be wrong below Leaderboard score submission = Leaderboard score submission Leaderboard submission is enabled = Leaderboard submission is enabled Leaderboards = Leaderboards @@ -88,6 +91,7 @@ Disable diagonal input = Отключить диагональный ввод Double tap = Двойное нажатие Enable gesture control = Включить настройку жестов Enable standard shortcut keys = Включить стандартные сочетания клавиш +frames = frames Gesture = Жест Gesture mapping = Настройка жестов Glowing borders = Подсветка краёв @@ -116,6 +120,7 @@ OnScreen = Элементы управления на экране Portrait = Портретная Portrait Reversed = Портретная (перевернутая) PSP Action Buttons = Кнопки действий PSP +Rapid fire interval = Rapid fire interval Raw input = Прямой ввод Repeat mode = Режим повтора Reset to defaults = По умолчанию @@ -310,10 +315,14 @@ VFPU = VFPU * PSP res = * разр. PSP Active = Активно Back = Назад +Bottom Center = Внизу в центре +Bottom Left = Внизу слева +Bottom Right = Внизу справа Cancel = Отмена Center = По центру -ChangingGPUBackends = Для изменения бэкенда ГП требуется перезапуск. Перезапустить сейчас? -ChangingInflightFrames = Для изменения буферирования команд графики требуется перезапуск. Перезапустить сейчас? +Center Left = В центре слева +Center Right = В центре справа +Changing this setting requires PPSSPP to restart. = Изменение этого параметра требует перезапуска PPSSPP. Channel: = Канал: Choose PPSSPP save folder = Выбрать папку с сохранениями Confirm Overwrite = Вы хотите перезаписать данные? @@ -361,6 +370,7 @@ Move Up = Сдвинуть вниз Network Connection = Сетевое подключение NEW DATA = Новое сохранение No = Нет +None = Нет ObtainingIP = Получение IP-адреса.\nПожалуйста, подождите... OK = OK Old savedata detected = Старый формат сохранений обнаружен @@ -369,6 +379,7 @@ Password = Password Remove = Удалить Reset = Сбросить Resize = Размер +Restart = Перезапуск Retry = Снова Save = Сохранить Save completed = Сохранение завершено. @@ -388,6 +399,9 @@ Supported = Поддерживается There is no data = Нет данных Toggle All = Переключить все Toggle List = Переключить список +Top Center = Вверху в центре +Top Left = Вверху слева +Top Right = Вверу справа Unsupported = Не поддерживается Username = Username When you save, it will load on a PSP, but not an older PPSSPP = Когда вы сохранитесь, оно может быть загружено на PSP, но не на старой версии PPSSPP @@ -604,6 +618,7 @@ Stereo rendering = Стереорендеринг Stretch = Растягивание Texture Filter = Текстурный фильтр Texture Filtering = Фильтрация текстур +Texture replacement pack activated = Texture replacement pack activated Texture Scaling = Масштабирование текстур Texture Shader = Текстурный шейдер Turn off Hardware Tessellation - unsupported = Выключите настройку "Аппаратная тесселяция": не поддерживается @@ -760,11 +775,6 @@ AdHoc Server = Ad-hoc сервер AdhocServer Failed to Bind Port = Не удалось привязать порт ad-hoc сервера AM: Data from Unknown Port = AM: Данные от неизвестного порта Auto = Авто -Bottom Center = Внизу в центре -Bottom Left = Внизу слева -Bottom Right = Внизу справа -Center Left = В центре слева -Center Right = В центре справа Change Mac Address = Изменить MAC-адрес Change proAdhocServer Address = Изменить IP-адрес ad-hoc сервера (localhost = множество экземпляров) ChangeMacSaveConfirm = Сгенерировать новый MAC-адрес? @@ -798,7 +808,6 @@ Invalid IP or hostname = Некорректный IP или имя хоста Minimum Timeout = Минимальный таймаут (задержка в мс, 0 = по умолчанию) Misc = Прочее (по умолчанию = совместимость с PSP) Network Initialized = Сеть активирована -None = Нет Please change your Port Offset = Пожалуйста, измените ваше смещение порта Port offset = Смещение порта (0 = совместимость с PSP) Open PPSSPP Multiplayer Wiki Page = Открыть wiki-страницу мультиплеера PPSSPP @@ -809,11 +818,9 @@ Quick Chat 3 = Быстрый чат 3 Quick Chat 4 = Быстрый чат 4 Quick Chat 5 = Быстрый чат 5 QuickChat = Быстрый чат +Randomize = Randomize Send = Отправить Send Discord Presence information = Отправлять информацию об игре в Discord -Top Center = Вверху в центре -Top Left = Вверху слева -Top Right = Вверу справа Unable to find UPnP device = Невозможно найти устройство UPnP UPnP (port-forwarding) = UPnP (проброс портов) UPnP need to be reinitialized = Необходимо перезапустить UPnP @@ -823,6 +830,23 @@ Validating address... = Проверка адреса... WLAN Channel = Канал WLAN You're in Offline Mode, go to lobby or online hall = Вы в автономном режиме, зайдите в лобби или в комнату в Интернете +[PSPSettings] +Auto = Auto +Chinese (simplified) = Chinese (simplified) +Chinese (traditional) = Chinese (traditional) +Dutch = Dutch +English = English +French = French +Game language = Game language +German = German +Italian = Italian +Japanese = Japanese +Korean = Korean +Games often don't support all languages = Games often don't support all languages +Portuguese = Portuguese +Russian = Russian +Spanish = Spanish + [Pause] Cheats = Читы Continue = Продолжить diff --git a/assets/lang/sv_SE.ini b/assets/lang/sv_SE.ini index c0131f1b3b..6a03562234 100644 --- a/assets/lang/sv_SE.ini +++ b/assets/lang/sv_SE.ini @@ -6,8 +6,10 @@ Account = Inloggning Achievement Unlocked = Achievement avklarat! Achievements = Achievements Achievements are disabled = Achievements är avstängda +Can't log in to RetroAchievements right now = Can't log in to RetroAchievements right now Challenge Mode = Utmanings-läge Challenge Mode (no savestates) = Utmanings-läge (inga sparade state) +Contacting RetroAchievements server... = Contacting RetroAchievements server... Customize = Inställningar Earned = Du har tjänat %d av %d achievements, och %d of %d poäng Encore Mode = Encore-läge (kan ta achievements igen) @@ -89,6 +91,7 @@ Disable diagonal input = Blockera diagonal input Double tap = Dubbel-tap Enable gesture control = Gest-kontroll Enable standard shortcut keys = Enable standard shortcut keys +frames = frames Gesture = Gest Gesture mapping = Gest-mappning Glowing borders = Lysande kanter @@ -117,6 +120,7 @@ OnScreen = Touch-kontroller på skärmen Portrait = Porträtt Portrait Reversed = Porträtt omvänt PSP Action Buttons = PSP action-knappar +Rapid fire interval = Rapid fire interval Raw input = Rå input Repeat mode = Repetitionsläge Reset to defaults = Återställ till standardvärden @@ -311,10 +315,14 @@ VFPU = VFPU * PSP res = * PSP res Active = Aktiv Back = Tillbaka +Bottom Center = Underkant centrerad +Bottom Left = Underkant vänster +Bottom Right = Underkant höger Cancel = Avbryt Center = Centrera -ChangingGPUBackends = PPSSPP måste starta om för att byta GPU-backend. Vill du starta om nu? -ChangingInflightFrames = Changing graphics command buffering requires PPSSPP to restart. Restart now? +Center Left = Centrerad vänster +Center Right = Centrerad höger +Changing this setting requires PPSSPP to restart. = Att ändra denna inställning kräver att PPSSPP startas om. Channel: = Kanal: Choose PPSSPP save folder = Välj folder att spara i Confirm Overwrite = Vill du skriva över datan? @@ -363,6 +371,7 @@ Move Up = Flytta upp Network Connection = Nätverksuppkoppling NEW DATA = NY DATA No = Nej +None = Ingen ObtainingIP = Hämtar IP-address.\nVänta... OK = OK Old savedata detected = Gammal savedata upptäckt @@ -371,6 +380,7 @@ Password = Lösenord Remove = Ta bort Reset = Återställ Resize = Ändra storlek +Restart = Starta om Retry = Försök igen Save = Spara Save completed = Sparat. @@ -390,6 +400,9 @@ Supported = Stödd There is no data = Det finns ingen data. Toggle All = Vänd alla Toggle List = Toggle list +Top Center = Överkant centrerad +Top Left = Överkant vänster +Top Right = Överkant höger Unsupported = Stöds ej Username = Användarnamn When you save, it will load on a PSP, but not an older PPSSPP = When you save, it will load on a PSP, but not an older PPSSPP @@ -606,6 +619,7 @@ Stereo rendering = Stereo-rendering Stretch = Sträck ut Texture Filter = Texturfilter Texture Filtering = Texturfiltrering +Texture replacement pack activated = Texture replacement pack activated Texture Scaling = Texturskalning Texture Shader = Texture shader Turn off Hardware Tessellation - unsupported = Turn off "hardware tessellation": unsupported @@ -762,60 +776,52 @@ AdHoc Server = Ad hoc server AdhocServer Failed to Bind Port = Ad hoc server failed to bind port AM: Data from Unknown Port = AM: Data from Unknown Port Auto = Auto -Bottom Center = Bottom center -Bottom Left = Bottom left -Bottom Right = Bottom right -Center Left = Center left -Center Right = Center right Change Mac Address = Ändra Mac-adress Change proAdhocServer Address = Ändra proAdhocServer-adress (localhost = multiple instances) -ChangeMacSaveConfirm = Generate a new MAC address? -ChangeMacSaveWarning = Some games verify the MAC address when loading savedata, so this may break old saves. +ChangeMacSaveConfirm = Generera en ny MAC-address? +ChangeMacSaveWarning = Vissa spel kollar MAC-address när de laddar savedata, så detta kan förstöra saves Chat = Chat -Chat Button Position = Chat button position -Chat Here = Chat here -Chat message = Chat message -Chat Screen Position = Chat screen position -Disconnected from AdhocServer = Disconnected from ad hoc server -DNS Error Resolving = DNS error resolving +Chat Button Position = Chatt-knappens position +Chat Here = Chatta här +Chat message = Chatt-meddelande +Chat Screen Position = Chatt-rutans position +Disconnected from AdhocServer = Bortkopplad från Adhoc-servern +DNS Error Resolving = DNS fel vid addressresolving Enable built-in PRO Adhoc Server = Enable built-in PRO Adhoc Server -Enable network chat = Enable network chat +Enable network chat = Nätverkschatt Enable networking = Nätverk/wlan (beta) Enable UPnP = Enable UPnP (need a few seconds to detect) -EnableQuickChat = Enable quick chat -Enter a new PSP nickname = Enter a new PSP nickname +EnableQuickChat = Snabbchat +Enter a new PSP nickname = Mata in ett nytt PSP-nickname Enter Quick Chat 1 = Snabbchat 1 Enter Quick Chat 2 = Snabbchat 2 Enter Quick Chat 3 = Snabbchat 3 Enter Quick Chat 4 = Snabbchat 4 Enter Quick Chat 5 = Snabbchat 5 Error = Error -Failed to Bind Localhost IP = Failed to bind localhost IP -Failed to Bind Port = Failed to bind port -Failed to connect to Adhoc Server = Failed to connect to ad hoc server +Failed to Bind Localhost IP = Misslyckades binda localhost-IP +Failed to Bind Port = Misslyckades binda port +Failed to connect to Adhoc Server = Misslyckades koppla upp till adhoc-servern Forced First Connect = Forced first connect (faster connect) -GM: Data from Unknown Port = GM: Data from Unknown Port +GM: Data from Unknown Port = GM: Data från okänd port Hostname = Hostname Invalid IP or hostname = Ogiltigt IP eller hostname -Minimum Timeout = Minimum timeout (override in ms, 0 = default) -Misc = Miscellaneous (default = PSP compatibility) +Minimum Timeout = Minimal timeout (override in ms, 0 = default) +Misc = Miscellaneous (default = PSP-kompatibilitet) Network Initialized = Nätverk initierat -None = None -Please change your Port Offset = Please change your port offset -Port offset = Port offset(0 = PSP compatibility) -Open PPSSPP Multiplayer Wiki Page = Open PPSSPP Multiplayer Wiki Page -proAdhocServer Address: = Ad hoc server address: +Please change your Port Offset = Vänligen ändra din port-offset +Port offset = Port offset (0 = PSP-kompatibilitet) +Open PPSSPP Multiplayer Wiki Page = Öppna PPSSPP Multiplayer Wiki Page +proAdhocServer Address: = Adhoc server-address: Quick Chat 1 = Snabbchat 1 Quick Chat 2 = Snabbchat 2 Quick Chat 3 = Snabbchat 3 Quick Chat 4 = Snabbchat 4 Quick Chat 5 = Snabbchat 5 QuickChat = Snabbchat +Randomize = Randomize Send = Skicka Send Discord Presence information = Visa Discord "Rich Presence"-information -Top Center = Top center -Top Left = Top left -Top Right = Top right Unable to find UPnP device = Unable to find UPnP device UPnP (port-forwarding) = UPnP (port forwarding) UPnP need to be reinitialized = UPnP need to be reinitialized @@ -873,7 +879,7 @@ RedBlue = Röd/Blå glasögon Saturation = Färgmättnad Scanlines = Scan-linjer Sharpen = Skärpa -SideBySide = Side by side (SBS) +SideBySide = Sida vid sida (SBS) SSAA(Gauss) = Supersampling AA (Gauss) Strength = Styrka Tex4xBRZ = 4xBRZ @@ -922,6 +928,23 @@ Twitter @PPSSPP_emu = Twitter @PPSSPP_emu website = Kolla in webbsidan: written = Skriven i C++ - prestanda och portabilitet +[PSPSettings] +Auto = Auto +Chinese (simplified) = Kinesiska (förenklad) +Chinese (traditional) = Kinesiska (traditionell) +Dutch = Holländska +English = Engelska +French = Franska +Game language = Språk i spel +Games often don't support all languages = Games often don't support all languages +German = Tyska +Italian = Italienska +Japanese = Japanska +Korean = Koreanska +Portuguese = Portugisiska +Russian = Ryska +Spanish = Spanska + [MemStick] Already contains PSP data = Innehåller redan PSP-data Create or Choose a PSP folder = Skapa eller välj en PSP-mapp @@ -1056,12 +1079,12 @@ State load undone = State load ogjort Untitled PSP game = Untitled PSP game [Search] -Clear filter = Clear filter +Clear filter = Rensa filter Filter = Filter -Filtering settings by '%1' = Filtering settings by '%1' -Find settings = Find settings -No settings matched '%1' = No settings matched '%1' -Search term = Search term +Filtering settings by '%1' = Filtrerar inställningar efter '%1' +Find settings = Sök inställningar +No settings matched '%1' = Inga inställningar matchade '%1' +Search term = Sökterm [Store] Already Installed = Redan installerad @@ -1295,7 +1318,7 @@ Game camera uses rotation smoothing = Game camera uses rotation smoothing Heads-up display detection = Heads-up display detection Heads-up display scale = Heads-up display scale Manual switching between flat screen and VR using SCREEN key = Manual switching between flat screen and VR using SCREEN key -Map controller movements to keys = Map controller movements to keys +Map controller movements to keys = Mappa kontroller-rörelser till knappar Map HMD rotations on keys instead of VR camera = Map HMD rotations on keys instead of VR camera Motion needed to generate action = Motion needed to generate action Stereoscopic vision (Experimental) = Stereoscopic vision (Experimental) diff --git a/assets/lang/tg_PH.ini b/assets/lang/tg_PH.ini index 30db7e95d5..ff72bcf87e 100644 --- a/assets/lang/tg_PH.ini +++ b/assets/lang/tg_PH.ini @@ -6,12 +6,15 @@ Account = Account Achievement Unlocked = Achievement Unlocked Achievements = Achievements Achievements are disabled = Achievements are disabled +Can't log in to RetroAchievements right now = Can't log in to RetroAchievements right now Challenge Mode = Challenge Mode Challenge Mode (no savestates) = Challenge Mode (no savestates) +Contacting RetroAchievements server... = Contacting RetroAchievements server... Customize = Customize Earned = You have earned %d of %d achievements, and %d of %d points Encore Mode = Encore Mode How to use RetroAchievements = How to use RetroAchievements +In Encore mode - listings may be wrong below = In Encore mode - listings may be wrong below Leaderboard score submission = Leaderboard score submission Leaderboard submission is enabled = Leaderboard submission is enabled Leaderboards = Leaderboards @@ -88,6 +91,7 @@ Disable diagonal input = Disable diagonal input Double tap = Double tap Enable gesture control = Enable gesture control Enable standard shortcut keys = Enable standard shortcut keys +frames = frames Gesture = Gesture Gesture mapping = Gesture mapping Glowing borders = Glowing borders @@ -116,6 +120,7 @@ OnScreen = Pindutan sa screen Portrait = Patayo Portrait Reversed = Pabaliktad na patayo PSP Action Buttons = PSP action buttons +Rapid fire interval = Rapid fire interval Raw input = Raw input Repeat mode = Repeat mode Reset to defaults = Reset to defaults @@ -310,10 +315,14 @@ VFPU = VFPU * PSP res = * PSP res Active = Active Back = Bumalik +Bottom Center = Bottom center +Bottom Left = Bottom left +Bottom Right = Bottom right Cancel = Kanselahin Center = Gitna -ChangingGPUBackends = Kapag pinalitan ang GPU backends,\nKailangan mong I-restart ang PPSSPP. Magpatuloy? -ChangingInflightFrames = Changing graphics command buffering requires PPSSPP to restart. Restart now? +Center Left = Center left +Center Right = Center right +Changing this setting requires PPSSPP to restart. = Changing this setting requires PPSSPP to restart. Channel: = Channel: Choose PPSSPP save folder = Piliin ang PPSSPP save folder Confirm Overwrite = Nais mo bang patungan ang datos? @@ -361,6 +370,7 @@ Move Up = Move Up Network Connection = Network Connection NEW DATA = BAGONG DATOS No = Hindi +None = None ObtainingIP = Obtaining IP address.\nPlease wait... OK = Sige Old savedata detected = Nahanap ang lumang savedata @@ -369,6 +379,7 @@ Password = Password Remove = Remove Reset = Ibalik Resize = Resize +Restart = Restart Retry = Ulitin Save = Save Save completed = Kumpletong na-i save. @@ -388,6 +399,9 @@ Supported = Supported There is no data = Walang Datos Toggle All = Toggle all Toggle List = Toggle list +Top Center = Top center +Top Left = Top left +Top Right = Top right Unsupported = Unsupported Username = Username When you save, it will load on a PSP, but not an older PPSSPP = Kapag iyong na-i save, gagana ito mismo sa PSP\npero hindi sa lumang bersyon ng PPSSPP @@ -604,6 +618,7 @@ Stereo rendering = Stereo rendering Stretch = Iunat Texture Filter = Panala ng Texture Texture Filtering = Pagsala sa Texture +Texture replacement pack activated = Texture replacement pack activated Texture Scaling = Pagsukat sa Texture Texture Shader = Texture shader Turn off Hardware Tessellation - unsupported = Turn off "hardware tessellation": hindi suportado @@ -789,11 +804,6 @@ AdHoc Server = Ad hoc server AdhocServer Failed to Bind Port = Ad hoc server failed to bind port AM: Data from Unknown Port = AM: Data from Unknown Port Auto = Auto -Bottom Center = Bottom center -Bottom Left = Bottom left -Bottom Right = Bottom right -Center Left = Center left -Center Right = Center right Change Mac Address = Baguhin ang MAC Address Change proAdhocServer Address = Change PRO ad hoc server IP address (localhost = multiple instances) ChangeMacSaveConfirm = Generate a new MAC address? @@ -827,7 +837,6 @@ Invalid IP or hostname = Invalid IP or hostname Minimum Timeout = Minimum timeout (override in ms, 0 = default) Misc = Miscellaneous (default = PSP compatibility) Network Initialized = Network initialized -None = None Please change your Port Offset = Please change your port offset Port offset = Port offset(0 = PSP compatibility) Open PPSSPP Multiplayer Wiki Page = Open PPSSPP Multiplayer Wiki Page @@ -838,11 +847,9 @@ Quick Chat 3 = Quick chat 3 Quick Chat 4 = Quick chat 4 Quick Chat 5 = Quick chat 5 QuickChat = Quick chat +Randomize = Randomize Send = Send Send Discord Presence information = Send Discord "Rich Presence" information -Top Center = Top center -Top Left = Top left -Top Right = Top right Unable to find UPnP device = Unable to find UPnP device UPnP (port-forwarding) = UPnP (port forwarding) UPnP need to be reinitialized = UPnP need to be reinitialized @@ -852,6 +859,23 @@ Validating address... = Validating address... WLAN Channel = WLAN channel You're in Offline Mode, go to lobby or online hall = You're in offline mode, go to lobby or online hall +[PSPSettings] +Auto = Auto +Chinese (simplified) = Chinese (simplified) +Chinese (traditional) = Chinese (traditional) +Dutch = Dutch +English = English +French = French +Game language = Game language +German = German +Italian = Italian +Japanese = Japanese +Korean = Korean +Games often don't support all languages = Games often don't support all languages +Portuguese = Portuguese +Russian = Russian +Spanish = Spanish + [Pause] Cheats = Mandaya Continue = Ituloy diff --git a/assets/lang/th_TH.ini b/assets/lang/th_TH.ini index 6059949d5f..b91f659068 100644 --- a/assets/lang/th_TH.ini +++ b/assets/lang/th_TH.ini @@ -1,39 +1,48 @@ [Achievements] -%1: Leaderboard attempt failed = %1: Leaderboard attempt failed -%1: Leaderboard attempt started = %1: Leaderboard attempt started -%d achievements, %d points = %d achievements, %d points -Account = Account -Achievement Unlocked = Achievement Unlocked -Achievements = Achievements -Achievements are disabled = Achievements are disabled -Challenge Mode = Challenge Mode -Challenge Mode (no savestates) = Challenge Mode (no savestates) -Customize = Customize -Earned = You have earned %d of %d achievements, and %d of %d points -Encore Mode = Encore Mode -How to use RetroAchievements = How to use RetroAchievements -Leaderboard score submission = Leaderboard score submission -Leaderboard submission is enabled = Leaderboard submission is enabled -Leaderboards = Leaderboards -Links = Links -Locked achievements = Locked achievements -Log bad memory accesses = Log bad memory accesses -Mastered %1 = Mastered %1 -Register on www.retroachievements.org = Register on www.retroachievements.org -RetroAchievements are not available for this game = RetroAchievements are not available for this game -RetroAchievements website = RetroAchievements website +%1: Leaderboard attempt failed = %1: ส่งข้อมูลไปยังกระดานแต้มผู้นำล้มเหลว +%1: Leaderboard attempt started = %1: เริ่มการส่งข้อมูลไปยังกระดานแต้มผู้นำแล้ว +%d achievements, %d points = %d เป้าหมายความสำเร็จ, %d แต้ม +Account = บัญชีผู้ใช้งาน +Achievement unlocked = ตอนเป้าหมายความสำเร็จถูกปลดล็อค +Achievements = เป้าหมายความสำเร็จ +Achievements enabled = เปิดใช้งาน เป้าหมายความสำเร็จ +Achievements are disabled = ปิดใช้งาน เป้าหมายความสำเร็จ +Can't log in to RetroAchievements right now = ไม่สามารถล็อกอินเข้าไปยัง RetroAchievements ได้ในขณะนี้ +Challenge Mode = โหมดท้าทาย +Challenge Mode (no savestates) = โหมดท้าทาย (ไม่มีเซฟสเตท) +Contacting RetroAchievements server... = กำลังเชื่อมต่อกับเซิร์ฟเวอร์ RetroAchievements... +Customize = ปรับแต่ง +Earned = คุณได้รับ %d of %d เป้าหมายความสำเร็จ, และ %d ของ %d แต้ม +Encore Mode = โหมดคำราม +Failed logging in to RetroAchievements = ล้มเหลวในการเข้าใช้งานไปยัง RetroAchievements +How to use RetroAchievements = วิธีใช้งาน RetroAchievements +In Encore mode - listings may be wrong below = ในโหมดคำราม - รายชื่อด้านล่างอาจจะผิด +Leaderboard score submission = ตอนส่งคะแนนไปยังกระดานแต้มผู้นำ +Leaderboard submission is enabled = เปิดใช้งานการส่งคะแนนไปยังกระดานแต้มผู้นำ +Leaderboards = กระดานแต้มผู้นำ +Links = ลิงก์ +Locked achievements = เป้าหมายความสำเร็จที่ยังถูกล็อคเอาไว้ +Log bad memory accesses = เก็บค่าการเข้าถึงหน่วยความจำที่ไม่ดี +Log in = เข้าใช้งาน +Mastered %1 = เชี่ยวชาญ %1 +Register on www.retroachievements.org = สมัครได้ที่ www.retroachievements.org +RetroAchievements are not available for this game = ไม่สามารถใช้งาน RetroAchievements กับเกมนี้ได้ +RetroAchievements website = เว็บไซต์ RetroAchievements Rich Presence = Rich Presence -Save state loaded without achievement data = Save state loaded without achievement data -Save states not available in Challenge Mode = Save states not available in Challenge Mode -Sound Effects = Sound Effects -Statistics = Statistics -Submitted %1 for %2 = Submitted %1 for %2 -Syncing achievements data... = Syncing achievements data... -Test Mode = Test Mode -This feature is not available in Challenge Mode = This feature is not available in Challenge Mode -This game has no achievements = This game has no achievements -Unlocked achievements = Unlocked achievements -Unofficial achievements = Unofficial achievements +Save state loaded without achievement data = โหลดเซฟสเตทแล้วโดยที่ไม่มีข้อมูลเป้าหมายความสำเร็จ +Save states not available in Challenge Mode = ไม่สามารถใช้งานเซฟสเตทได้ในโหมดท้าทาย +Settings = การตั้งค่า +Sound effects = เสียงเอฟเฟ็คท์ +Sound Effects = เสียงเอฟเฟ็คท์ +Statistics = สถิติ +Submitted %1 for %2 = ส่ง %1 สำหรับ %2 +Syncing achievements data... = กำลังซิงก์ข้อมูล เป้าหมายความสำเร็จ... +Test Mode = โหมดทดสอบ +This feature is not available in Challenge Mode = ฟีเจอร์นี้ไม่สามารถปรับใช้งานได้ในโหมดท้าทาย +This game has no achievements = เกมนี้ไม่มีเป้าหมายความสำเร็จ +Unlocked achievements = เป้าหมายความสำเร็จที่ถูกปลดล็อคแล้ว +Unofficial achievements = เป้าหมายความสำเร็จอย่างไม่เป็นทางการ +Username = ชื่อผู้ใช้งาน [Audio] Alternate speed volume = ระดับเสียงในโหมดเร่งความเร็ว @@ -73,11 +82,11 @@ Calibrate Analog Stick = ปรับค่าความคลาดเคล Calibrate = สอบเทียบสมดุล Calibrated = ค่าความคลาดเคลื่อนที่ปรับ Calibration = ปรับค่าความคลาดเคลื่อน -Circular low end radius = Circular low end radius -Circular stick input = ค่านำเข้าหมุนปุ่ม +Circular low end radius = รัศมีต่ำสุดของการหมุนปุ่ม +Circular stick input = ค่านำเข้าการหมุนปุ่ม Classic = แบบดั้งเดิม Confine Mouse = จำกัดเมาส์ภายในหน้าต่าง/พื้นที่แสดงผล -Control Mapping = เปลี่ยนแปลงค่าควบคุม +Control Mapping = เปลี่ยนแปลงค่าการควบคุม Custom Key Setting = ติดตั้งปุ่มคอมโบปรับแต่ง Customize = ปรับแต่ง Customize Touch Controls = ปรับแต่งปุ่มควบคุมสัมผัสหน้าจอ @@ -88,6 +97,7 @@ Disable diagonal input = ปิดการใช้งานอนาล็อ Double tap = แตะสองครั้ง Enable gesture control = เปิดการควบคุมปัดหน้าจอสัมผัส Enable standard shortcut keys = เปิดการใช้งานปุ่มคีย์ลัดแบบพื้นฐาน +frames = เฟรม Gesture = ปัดจอ Gesture mapping = เซ็ตปุ่มควบคุมปัดหน้าจอสัมผัส Glowing borders = แบบขอบทึบแสง @@ -116,6 +126,7 @@ OnScreen = ปรับใช้หน้าจอควบคุมแบบป Portrait = แนวตั้ง Portrait Reversed = แนวตั้งกลับด้าน PSP Action Buttons = ปุ่มแอ็คชั่นของ PSP +Rapid fire interval = ปุ่มยิงรัวอย่างรวดเร็ว Raw input = ค่าตั้งต้น Repeat mode = โหมดปุ่มย้ำ Reset to defaults = คืนค่าเริ่มต้น @@ -143,7 +154,6 @@ X = แกน X Y = แกน Y [CwCheats] -#Add Cheat = เพิ่มสูตรโกง # This one is not used Cheats = การใช้สูตรโกง Edit Cheat File = ปรับแต่งไฟล์สูตรโกง Import Cheats = นำเข้าจากไฟล์สูตรโกง @@ -281,6 +291,7 @@ Log Dropped Frame Statistics = เก็บสถิติค่าเฟรม Log Level = เก็บค่าระดับ Log View = เก็บค่ามุมมอง Logging Channels = ช่องทางเก็บค่าข้อมูล +Multi-threaded rendering = การเรนเดอร์แบบมัลติเธรด Next = ถัดไป No block = ไม่ต้องบล็อค Prev = ถอยกลับ @@ -311,10 +322,14 @@ VFPU = VFPU * PSP res = * ขนาดจอ PSP Active = เปิดการทำงาน Back = ย้อนกลับ +Bottom Center = มุมล่างกลางจอ +Bottom Left = มุมล่างซ้ายจอ +Bottom Right = มุมล่างขวาจอ Cancel = ยกเลิก Center = กึ่งกลาง -ChangingGPUBackends = เมื่อเปลี่ยนแปลงรูปแบบการสนับสนุนของจีพียู\nกรุณาเริ่ม PPSSPP ใหม่อีกครั้งนึง ต้องการรีสตาร์ทเลยไหม? -ChangingInflightFrames = การเปลี่ยนแปลงค่าควบคุมกราฟิกบัฟเฟอร์จำเป็นต้องเริ่ม PPSSPP\nใหม่อีกครั้งนึง ต้องการรีสตาร์ทเลยไหม? +Center Left = มุมกลางซ้ายจอ +Center Right = มุมกลางขวาจอ +Changing this setting requires PPSSPP to restart. = การเปลี่ยนการตั้งค่านี้จำเป็นต้องรีสตาร์ท PPSSPP Channel: = แชลนอล: Choose PPSSPP save folder = เลือกโฟลเดอร์สำหรับเก็บข้อมูลเซฟของ PPSSPP Confirm Overwrite = ต้องการให้เขียนทับซ้ำกับข้อมูลเดิมเลยหรือไม่? @@ -334,14 +349,14 @@ DeleteConfirmGameConfig = คุณแน่ใจนะว่าต้องก DeleteFailed = ไม่สามารถลบข้อมูลนี้ได้ Deleting = กำลังลบข้อมูล\nโปรดรอ... Disable All = ปิดใช้งานทั้งหมด -Disabled = Disabled +Disabled = ปิดการใช้งาน Dumps = ไฟล์ดั๊มพ์ Edit = แก้ไข Enable All = เปิดใช้งานทั้งหมด Enabled = Enabled Enter = ยืนยัน -Failed to connect to server, check your internet connection. = Failed to connect to server, check your internet connection. -Failed to log in, check your username and password. = Failed to log in, check your username and password. +Failed to connect to server, check your internet connection. = ล้มเหลวในการเชื่อมต่อกับเซิร์ฟเวอร์ โปรดเช็คการเชื่อมต่ออินเตอร์เน็ต +Failed to log in, check your username and password. = ล้มเหลวในการเข้าใช้งาน โปรดเช็ค ชื่อผู้ใช้งาน กับ รหัสผ่าน ดูก่อน Filter = ตัวกรอง Finish = สิ้นสุด GE Frame Dumps = ไฟล์กราฟิกเอนจิ้นเฟรมดั๊มพ์ @@ -352,24 +367,26 @@ Load = โหลด Load completed = โหลดข้อมูลสำเร็จ Loading = กำลังโหลด\nโปรดรอ... LoadingFailed = ไม่สามารถโหลดข้อมูลนี้ได้ -Log in = Log in -Log out = Log out -Logged in! = Logged in! -Logging in... = Logging in... +Log in = เข้าใช้งาน +Log out = ออกจากการใช้งาน +Logged in! = เข้าใช้งานแล้ว! +Logging in... = กำลังเข้าใช้งาน... Move = ย้าย Move Down = ย้ายลงล่าง Move Up = ย้ายขึ้นบน Network Connection = การเชื่อมต่อเครือข่าย NEW DATA = ข้อมูลใหม่ No = ไม่ใช่ +None = ไม่มี ObtainingIP = กำลังรับไอพี\nโปรดรอสักครู่... OK = ตกลง Old savedata detected = ตรวจพบข้อมูลเซฟเก่า Options = ตัวเลือก -Password = Password +Password = รหัสผ่าน Remove = ลบออก Reset = รีเซ็ต Resize = ขนาด +Restart = เริ่มต้นใหม่ Retry = ลองใหม่ Save = บันทึก Save completed = บันทึกข้อมูลสำเร็จ @@ -378,7 +395,7 @@ SavingFailed = ไม่สามารถบันทึกข้อมูล Search = ค้นหา seconds, 0:off = วินาที, 0 = ปิด Select = เลือก -Settings = Settings +Settings = การตั้งค่า Shift = เปลี่ยน Skip = ข้าม Snap = ขยับ @@ -389,8 +406,11 @@ Supported = รองรับ There is no data = ไม่มีข้อมูล Toggle All = ทั้งหมด Toggle List = รายการ +Top Center = มุมบนกลางจอ +Top Left = มุมบนซ้ายจอ +Top Right = มุมบนขวาจอ Unsupported = ไม่รองรับ -Username = Username +Username = ชื่อผู้ใช้งาน When you save, it will load on a PSP, but not an older PPSSPP = เซฟเกมของคุณนั้น สามารถนำไปโหลดผ่านบนเครื่อง PSP ได้\nแต่นำไปใช้กับ PPSSPP ที่เวอร์ชั่นเก่ากว่าไม่ได้ When you save, it will not work on outdated PSP Firmware anymore = เซฟเกมของคุณนั้น ไม่สามารถนำไปใช้กับเครื่อง PSP ที่ใช้เฟิร์มแวร์รุ่นเก่าได้ Yes = ใช่ @@ -605,6 +625,7 @@ Stereo rendering = การแสดงผลแบบสามมิติ Stretch = ดึงเต็มจอ Texture Filter = ตัวกรองพื้นผิว Texture Filtering = การกรองพื้นผิว +Texture replacement pack activated = Texture replacement pack activated Texture Scaling = การปรับสเกลพื้นผิว Texture Shader = โทนแสงสีพื้นผิว Turn off Hardware Tessellation - unsupported = ปิด "ฮาร์ดแวร์ เทสเซลเลชั่น" แล้ว เนื่องจากไม่สนับสนุนการทำงาน @@ -633,12 +654,12 @@ Zip archive corrupt = ไฟล์ ZIP ไม่สมบูรณ์ (เสี Zip file does not contain PSP software = ไฟล์ ZIP นี้ ไม่มีข้อมูลซอฟแวร์ของเกม PSP [KeyMapping] -Allow combo mappings = Allow combo mappings +Allow combo mappings = เซ็ตปุมคอมโบ Autoconfigure = ค่าอัตโนมัติ Autoconfigure for device = กำหนดค่าให้อัตโนมัติสำหรับอุปกรณ์นี้ Bind All = เซ็ตปุ่มทั้งหมด Clear All = ล้างทั้งหมด -Combo mappings are not enabled = Combo mappings are not enabled +Combo mappings are not enabled = เซ็ตปุ่มคอมโบยังไม่ได้เปิดใช้งาน Default All = คืนค่าเดิมทั้งหมด Map a new key for = ปรับปุ่มใหม่สำหรับ Map Key = ตั้งค่าปุ่ม @@ -721,6 +742,7 @@ None = ไม่มี Note = หมายเหตุ OpenChat = เปิดแชท Pause = หยุดชั่วคราว +Previous Slot = ช่องสเตทอันที่แล้ว R = อาร์ RapidFire = กดรัวต่อเนื่อง Record = บันทึกหน้าจอ @@ -790,11 +812,6 @@ AdHoc Server = เซิร์ฟเวอร์ Adhoc AdhocServer Failed to Bind Port = เซิร์ฟเวอร์ Adhoc ล้มเหลวในการเชื่อมโยงพอร์ต AM: Data from Unknown Port = AM: ข้อมูลมาจากพอร์ตที่ไม่รู้จัก Auto = อัตโนมัติ -Bottom Center = มุมล่างกลางจอ -Bottom Left = มุมล่างซ้ายจอ -Bottom Right = มุมล่างขวาจอ -Center Left = มุมกลางซ้ายจอ -Center Right = มุมกลางขวาจอ Change Mac Address = เปลี่ยนค่าที่อยู่ Mac Change proAdhocServer Address = เปลี่ยนไอพีที่อยู่ของเซิร์ฟเวอร์ Pro Adhoc (localhost = แลนในเครื่องเดียวกัน) ChangeMacSaveConfirm = ต้องการสร้างค่า MAC address ใหม่? @@ -828,7 +845,6 @@ Invalid IP or hostname = ไอพี หรือ ชื่อโฮสต์ Minimum Timeout = เวลาขั้นต่ำที่ใช้เชื่อมต่อ (แทนที่ค่าความหน่วงในมิลลิวินาที) Misc = อื่นๆ (ค่าดั้งเดิม = ดีสุดแล้ว) Network Initialized = เริ่มการทำงานของเครือข่ายเรียบร้อย -None = ไม่มี Please change your Port Offset = กรุณาเปลี่ยนค่าของพอร์ตชดเชย Port offset = พอร์ตชดเชย (0 = ค่าเริ่มต้นของ PSP) Open PPSSPP Multiplayer Wiki Page = ไปที่หน้าเว็บ PPSSPP Ad-Hoc Wiki @@ -839,11 +855,9 @@ Quick Chat 3 = แชทด่วน 3 Quick Chat 4 = แชทด่วน 4 Quick Chat 5 = แชทด่วน 5 QuickChat = แชทด่วน +Randomize = กดสุ่มค่า Send = ส่ง Send Discord Presence information = แสดงชื่อเกมที่กำลังเล่นอยู่ไปยังแอพ Discord -Top Center = มุมบนกลางจอ -Top Left = มุมบนซ้ายจอ -Top Right = มุมบนขวาจอ Unable to find UPnP device = ไม่พบอุปกรณ์ UPnP UPnP (port-forwarding) = โพรโตคอล UPnP (ฟอร์เวิร์ด พอร์ต อัตโนมัติ) UPnP need to be reinitialized = UPnP จำเป็นต้องเริ่มการทำงานใหม่ @@ -853,7 +867,25 @@ Validating address... = กำลังตรวจสอบที่อยู WLAN Channel = ช่องสัญญาณ WLAN You're in Offline Mode, go to lobby or online hall = คุณอยู่ในโหมดออฟไลน์ ต้องเข้าไปในล็อบบี้หรือออนไลน์ฮอลล์ก่อน +[PSPSettings] +Auto = อัตโนมัติ +Chinese (simplified) = จีน (ตัวย่อ) +Chinese (traditional) = จีน (ตัวเต็ม) +Dutch = เนเธอร์แลนด์ +English = อังกฤษ +French = ฝรั่งเศส +Game language = ภาษาที่ใช้กับเกม +German = เยอรมัน +Italian = อิตาลี +Japanese = ญี่ปุ่น +Korean = เกาหลี +Games often don't support all languages = ในบางเกมอาจจะไม่รองรับทุกภาษา +Portuguese = โปรตุเกส +Russian = รัสเซีย +Spanish = สเปน + [Pause] +Achievements = เป้าหมายความสำเร็จ Cheats = สูตรโกง Continue = ดำเนินการต่อ Create Game Config = สร้างการตั้งค่าเฉพาะเกม @@ -879,19 +911,19 @@ Animation speed (0 -> disable) = ความเร็วอนิเมชั Aspect = ทิศทาง Black border = ขอบดำ Bloom = ภาพสว่างขึ้น +BloomNoBlur = ภาพสว่างขึ้น (ไม่เบลอ) Brightness = แสงสว่าง Cartoon = ภาพออกแนวการ์ตูน -CatmullRom = ภาพอัพสเกลแบบไบคิวบิค (แคทมิว-รอม) ColorCorrection = ภาพสีสมจริง ColorPreservation = การเก็บรักษาสีเดิม Contrast = ความคมชัด CRT = ภาพออกโทนเหลืองขึ้นเส้น +FakeReclections = ภาพเงาสะท้อนลวงตา FXAA = ภาพเน้นลบขอบรอยหยัก Gamma = แกมม่า GreenLevel = ระดับสีเขียว Intensity = ความเข้มของแสง LCDPersistence = ภาพ LCD แบบค้างคา -MitchellNetravali = ภาพอัพสเกลแบบไบคิวบิค (มิทเชลล์-เนทราวาลิ) Natural = ภาพเน้นสีธรรมชาติ NaturalA = ภาพเน้นสีธรรมชาติ (ไม่เบลอ) Off = ปิด @@ -906,6 +938,7 @@ SSAA(Gauss) = ภาพเน้นความละเอียดให้ส Strength = ความเด่นชัด Tex4xBRZ = 4xBRZ TexMMPX = MMPX +UpscaleBicubic = ภาพอัพสเกลแบบไบคิวบิค UpscaleSpline36 = ภาพลดความละเอียด เน้นสเกลคมขึ้น VideoSmoothingAA = ภาพในฉากคัตซีนดูเนียนยิ่งขึ้น Vignette = ภาพเน้นขอบหม่น @@ -1080,12 +1113,14 @@ Uninstall = ถอนการติดตั้ง (none detected) = (ไม่มีการตรวจพบ) 3D API = แอพพลิเคชั่น โปรแกรม อินเตอร์เฟซ สามมิติ ABI = แอพพลิเคชั่น ไบนารี่ อินเตอร์เฟซ +Achievement tests = ทดสอบเป้าหมายความสำเร็จ API Version = เวอร์ชั่นของแอพพลิเคชั่น โปรแกรม อินเตอร์เฟซ Audio Information = ข้อมูลของระบบเสียง Board = ชื่อบอร์ด Build Config = การกำหนดค่า Build Configuration = การกำหนดค่าที่ถูกสร้างไว้ Built by = สร้างโดย +Clear = ล้างแคช Compressed texture formats = รูปแบบการบีบอัดเท็คเจอร์ Core Context = บริบทของแกน Cores = จำนวนแกน @@ -1093,6 +1128,7 @@ CPU Extensions = ส่วนขยายซีพียู CPU Information = ข้อมูลของหน่วยประมวลผลหลัก CPU Name = ชื่อหน่วยประมวลผลหลัก D3DCompiler Version = เวอร์ชั่นตัวคอมไพล์ของ D3D +Data size = ขนาดข้อมูล Debug = แก้ไขบั๊ก Debugger Present = ตัวช่วยแก้ไขบั๊ก Depth buffer format = รูปแบบบัฟเฟอร์ความลึก @@ -1104,16 +1140,26 @@ DPI = DPI Driver bugs = ไดรเวอร์บั๊ก Driver Version = เวอร์ชั่นของไดรเวอร์ EGL Extensions = ส่วนขยายอีจีแอล +Error = เอ๋อ +Failure = ล้มเหลว Frames per buffer = จำนวนเฟรมต่อบัฟเฟอร์ GPU Flags = แฟล็กของหน่วยประมวลผลกราฟิก GPU Information = ข้อมูลของหน่วยประมวลผลกราฟิก High precision float range = ขอบเขตจำนวนทศนิยมช่วงค่าความแม่นยำสูง High precision int range = ขอบเขตจำนวนเต็มช่วงค่าความแม่นยำสูง +Icon cache = แคชไอคอน +Image data count = จำนวนข้อมูลรูปภาพ +Info = ข้อมูล +Internals = ข้อมูลภายใน JIT available = JIT ใช้งานได้ Lang/Region = ภาษา/ภูมิภาค +Leaderboard tracker: Hide = การติดตามกระดานแต้มผู้นำ: ซ่อน +Leaderboard tracker: Show = การติดตามกระดานแต้มผู้นำ: แสดง +Leaderboard tracker: Update = การติดตามกระดานแต้มผู้นำ: อัพเดท Memory Page Size = ขนาดหน้าของหน่วยความจำ Native Resolution = ความละเอียดดั้งเดิม No GPU driver bugs detected = ไม่พบไดรเวอร์บั๊กใน GPU รุ่นนี้ +Notification tests = ทดสอบการแจ้งเตือน OGL Extensions = ส่วนขยายโอจีแอล OpenGL ES 2.0 Extensions = ส่วนขยายของโอเพ่นจีแอล อีเอส 2.0 OpenGL ES 3.0 Extensions = ส่วนขยายของโอเพ่นจีแอล อีเอส 3.0 @@ -1122,23 +1168,27 @@ Optimal frames per buffer = จำนวนเฟรมต่อบัฟเฟ Optimal sample rate = อัตราค่าความถี่เสียงที่เหมาะสม OS Information = ข้อมูลของระบบปฏิบัติการ PPSSPP build = รูปแบบ PPSSPP +Progress tests = ทดสอบความคืบหน้า Refresh rate = อัตราการรีเฟรชของหน้าจอ Release = ใช้งาน RW/RX exclusive = RW/RX แบบพิเศษ Sample rate = อัตราค่าความถี่เสียง Shading Language = ภาษาของเฉดเดอร์ Storage = พื้นที่เก็บข้อมูล +Success = สำเร็จ Sustained perf mode = โหมดประสิทธิภาพที่ยั่งยืน System Information = ข้อมูลโดยรวมของระบบ System Name = ชื่อระบบ System Version = เวอร์ชั่นของระบบ +Texture count = จำนวนเท็คเจอร์ Threads = จำนวนเธรด +UI Resolution = ความละเอียดของอินเตอร์เฟซ Vendor = ชื่อหน่วยประมวลผลกราฟิก Vendor (detected) = ผู้จัดจำหน่าย (ที่ตรวจพบ) Version Information = ข้อมูลของเวอร์ชั่น Vulkan Extensions = ส่วนขยายของวัลแคน Vulkan Features = ฟีเจอร์วัลแคน -UI Resolution = ความละเอียดของอินเตอร์เฟซ +Warning = คำเตือน [System] (broken) = (เสียหาย) @@ -1164,7 +1214,7 @@ Date Format = รูปแบบวันที่ Day Light Saving = เวลาออมแสง DDMMYYYY = วัน/เดือน/ปี Decrease size = ลดขนาดไอคอน -Developer Tools = เครื่องมือสำหรับนักพัฒนา +Developer Tools = เครื่องมือผู้พัฒนา Display Extra Info = แสดงข้อมูลเพิ่มเติม Display Games on a grid = แสดงแบบกริดในแถบ "เกมอื่นๆ" Display Homebrew on a grid = แสดงแบบกริดในแถบ "เกมโฮมบริว" @@ -1216,6 +1266,7 @@ Record Audio = อัดบันทึกเสียง Record Display = อัดบันทึกวีดีโอ Reset Recording on Save/Load State = เริ่มการอัดบันทึกไฟล์ใหม่ เมื่อกดเซฟ/โหลดสเตทเกม Restore Default Settings = รีเซ็ตการตั้งค่าของ PPSSPP ทั้งหมด +RetroAchievements = RetroAchievements Rewind Snapshot Interval = เซฟสเตทพื้นหลังแบบอัตโนมัติ (สูบแรม) Save path in installed.txt = บันทึกข้อมูลตามเส้นทางที่เลือกไว้ Save path in My Documents = บันทึกข้อมูลไว้ที่ My Documents @@ -1285,7 +1336,9 @@ New version of PPSSPP available = PPSSPP เวอร์ชั่นใหม่ [VR] % of native FoV = % ของค่า FoV ดั้งเดิม 6DoF movement = การเคลื่อนไหวแบบ 6DoF +Camera type = ปรับแต่งประเภทมุมกล้อง Distance to 2D menus and scenes = ระยะห่างจากเมนูสองมิติ และฉาก +Distance to 3D scenes when VR disabled = ระยะห่างจากฉากสามมิติ เมื่อ VR ถูกปิดใช้งาน Experts only = สำหรับผู้เชี่ยวชาญเท่านั้น Field of view scale = สเกลของมุมมองพื้นที่รอบตัว Force 72Hz update = บังคับรีเฟรชเรทไปที่ 72Hz @@ -1296,7 +1349,6 @@ Heads-up display scale = สเกลขนาดของ HUD Manual switching between flat screen and VR using SCREEN key = สลับโหมดระหว่าง จอแบน และ VR โดยใช้ปุ่ม SCREEN Map controller movements to keys = เซ็ตปุ่มควบคุมด้วยท่าทางผ่านจอย Map HMD rotations on keys instead of VR camera = เซ็ตการควบคุมการหมุน HMD โดยใช้ปุ่มกด แทนการใช้กล้อง VR -Camera type = ปรับแต่งประเภทมุมกล้อง Motion needed to generate action = จำเป็นต้องขยับท่าทางเพื่อเซ็ตปุ่มที่ต้องการ Stereoscopic vision (Experimental) = มุมมองภาพแบบสามมิติ (ขั้นทดสอบ) Virtual reality = ความเป็นจริงเสมือน diff --git a/assets/lang/tr_TR.ini b/assets/lang/tr_TR.ini index 23bbc45862..f880929e75 100644 --- a/assets/lang/tr_TR.ini +++ b/assets/lang/tr_TR.ini @@ -6,12 +6,15 @@ Account = Account Achievement Unlocked = Achievement Unlocked Achievements = Achievements Achievements are disabled = Achievements are disabled +Can't log in to RetroAchievements right now = Can't log in to RetroAchievements right now Challenge Mode = Challenge Mode Challenge Mode (no savestates) = Challenge Mode (no savestates) +Contacting RetroAchievements server... = Contacting RetroAchievements server... Customize = Customize Earned = You have earned %d of %d achievements, and %d of %d points Encore Mode = Encore Mode How to use RetroAchievements = How to use RetroAchievements +In Encore mode - listings may be wrong below = In Encore mode - listings may be wrong below Leaderboard score submission = Leaderboard score submission Leaderboard submission is enabled = Leaderboard submission is enabled Leaderboards = Leaderboards @@ -88,6 +91,7 @@ Disable diagonal input = Diyagonal girişi devre dışı bırak Double tap = Çift dokunma Enable gesture control = Hareket kontrolünü etkinleştir Enable standard shortcut keys = Standart kısayol tuşlarını etkinleştir +frames = frames Gesture = Hareket Gesture mapping = Hareket kontrollerini düzenle Glowing borders = Parlayan kenarlar @@ -116,6 +120,7 @@ OnScreen = Dokunmatik ekran kontrolleri Portrait = Dikey Portrait Reversed = Ters dikey PSP Action Buttons = PSP eylem tuşları +Rapid fire interval = Rapid fire interval Raw input = Saf girdi Repeat mode = Tekrar modu Reset to defaults = Varsayılanlara sıfırla @@ -312,10 +317,14 @@ VFPU = VFPU * PSP res = * PSP Çözünürlüğü Active = Etkin Back = Geri +Bottom Center = Orta alt +Bottom Left = Sol alt +Bottom Right = Sağ alt Cancel = İptal Center = Merkez -ChangingGPUBackends = GPU arkaucunu değiştirmek için PPSSPP'nin yeniden başlatılması gerek. Şimdi yeniden başlatılsın mı? -ChangingInflightFrames = Grafik komut arabelleğini değiştirmek, PPSSPP'nin yeniden başlatılmasını gerektirir. Şimdi yeniden başlat? +Center Left = Sol orta +Center Right = Sağ orta +Changing this setting requires PPSSPP to restart. = Bu ayarın değiştirilmesi, PPSSPP'nin yeniden başlatılmasını gerektirir. Channel: = Kanal: Choose PPSSPP save folder = PPSSPP kayıt dosyası seç Confirm Overwrite = Veriyi üstüne yazmak istiyor musun? @@ -363,6 +372,7 @@ Move Up = Move Up Network Connection = Ağ Bağlantısı NEW DATA = YENİ VERİ No = Hayır +None = Hiçbiri ObtainingIP = IP adresi alınıyor.\nLütfen bekleyin... OK = Tamam Old savedata detected = Eski kayıt bulundu. @@ -371,6 +381,7 @@ Password = Password Remove = Remove Reset = Sıfırla Resize = Yeniden Boyutlandır +Restart = Tekrar başlat Retry = Yeniden Dene Save = Kaydet Save completed = Kaydedildi. @@ -390,6 +401,9 @@ Supported = Destekleniyor There is no data = Veri yok. Toggle All = Hepsini değiştir Toggle List = Listeyi değiştir +Top Center = Orta üst +Top Left = Sol üst +Top Right = Sağ üst Unsupported = Desteklenmiyor Username = Username When you save, it will load on a PSP, but not an older PPSSPP = Kayıt aldığında almış olduğunuz kayıt PSP'de yüklenebilecek ama eski bir PPSSPP sürümünde yüklenemeyecek. @@ -606,6 +620,7 @@ Stereo rendering = Stereo rendering Stretch = Serbest tam ekran (sünme) Texture Filter = Doku filtreleme Texture Filtering = Doku filtreleme +Texture replacement pack activated = Texture replacement pack activated Texture Scaling = Doku ölçekleme Texture Shader = Doku gölgelendiricisi Turn off Hardware Tessellation - unsupported = "Donanım mozaikleme"yi kapatın: desteklenmiyor @@ -791,11 +806,6 @@ AdHoc Server = Ad hoc sunucusu AdhocServer Failed to Bind Port = Ad hoc sunucusu portu bağlayamadı AM: Data from Unknown Port = AM: Bilinmeyen Porttan Veri Auto = Otomatik -Bottom Center = Orta alt -Bottom Left = Sol alt -Bottom Right = Sağ alt -Center Left = Sol orta -Center Right = Sağ orta Change Mac Address = MAC adresini değiştir Change proAdhocServer Address = PRO Ad Hoc Server IP adresini değiştir (localhost = çoklu örnek) ChangeMacSaveConfirm = Generate a new MAC address? @@ -829,7 +839,6 @@ Invalid IP or hostname = Geçersiz IP veya host adı Minimum Timeout = En düşük zaman aşımı (ms cinsinden geçersiz kılma, 0 = varsayılan) Misc = Çeşitli (varsayılan = PSP uyumluluğu) Network Initialized = Network Switch -None = Hiçbiri Please change your Port Offset = Lütfen Port Offset'i değiştirin Port offset = Port offset(0 = PSP uyumluluğu) Open PPSSPP Multiplayer Wiki Page = PPSSPP Ad-Hoc Wiki Sayfası @@ -840,11 +849,9 @@ Quick Chat 3 = Hızlı sohbet 3 Quick Chat 4 = Hızlı sohbet 4 Quick Chat 5 = Hızlı sohbet 5 QuickChat = Hızlı sohbet +Randomize = Randomize Send = Gönder Send Discord Presence information = Discord "Rich Presence" bilgisi gönder -Top Center = Orta üst -Top Left = Sol üst -Top Right = Sağ üst Unable to find UPnP device = UPnP cihazı bulunamıyor UPnP (port-forwarding) = UPnP (port yönlendirme) UPnP need to be reinitialized = UPnP'nin yeniden başlatılması gerekiyor @@ -854,6 +861,23 @@ Validating address... = Adres doğrulanıyor... WLAN Channel = WLAN Kanalı You're in Offline Mode, go to lobby or online hall = Çevrimdışı moddasınız, lobiye veya çevrimiçi salona gidin +[PSPSettings] +Auto = Auto +Chinese (simplified) = Chinese (simplified) +Chinese (traditional) = Chinese (traditional) +Dutch = Dutch +English = English +French = French +Game language = Game language +German = German +Italian = Italian +Japanese = Japanese +Korean = Korean +Games often don't support all languages = Games often don't support all languages +Portuguese = Portuguese +Russian = Russian +Spanish = Spanish + [Pause] Cheats = Hileler Continue = Devam diff --git a/assets/lang/uk_UA.ini b/assets/lang/uk_UA.ini index b1862b48b5..0f77d1cbc1 100644 --- a/assets/lang/uk_UA.ini +++ b/assets/lang/uk_UA.ini @@ -6,12 +6,15 @@ Account = Account Achievement Unlocked = Achievement Unlocked Achievements = Achievements Achievements are disabled = Achievements are disabled +Can't log in to RetroAchievements right now = Can't log in to RetroAchievements right now Challenge Mode = Challenge Mode Challenge Mode (no savestates) = Challenge Mode (no savestates) +Contacting RetroAchievements server... = Contacting RetroAchievements server... Customize = Customize Earned = You have earned %d of %d achievements, and %d of %d points Encore Mode = Encore Mode How to use RetroAchievements = How to use RetroAchievements +In Encore mode - listings may be wrong below = In Encore mode - listings may be wrong below Leaderboard score submission = Leaderboard score submission Leaderboard submission is enabled = Leaderboard submission is enabled Leaderboards = Leaderboards @@ -88,6 +91,7 @@ Disable diagonal input = Disable diagonal input Double tap = Double tap Enable gesture control = Enable gesture control Enable standard shortcut keys = Enable standard shortcut keys +frames = frames Gesture = Gesture Gesture mapping = Gesture mapping Glowing borders = Тонкі границі (підсвічування) @@ -116,6 +120,7 @@ OnScreen = Елементи управління на екрані Portrait = Портретна Portrait Reversed = Портретна (перевернута) PSP Action Buttons = Кнопки дій PSP +Rapid fire interval = Rapid fire interval Raw input = Raw input Repeat mode = Repeat mode Reset to defaults = Reset to defaults @@ -310,10 +315,14 @@ VFPU = VFPU * PSP res = * дозв. PSP Active = Активно Back = Назад +Bottom Center = Знизу центрально +Bottom Left = Знизу ліворуч +Bottom Right = Знизу праворуч Cancel = Відміна Center = По центру -ChangingGPUBackends = Зміна бекенду GPU вимагає перезавантажити PPSSPP. Перезавантажити зараз? -ChangingInflightFrames = Зміна буферизації графічних команд вимагає перезавантажити PPSSPP. Перезавантажити зараз? +Center Left = Центр ліворуч +Center Right = Центр праворуч +Changing this setting requires PPSSPP to restart. = Зміна буферизації графічних команд вимагає перезавантажити PPSSPP. Перезавантажити зараз? Channel: = Channel: Choose PPSSPP save folder = Виберіть папку для збереження Confirm Overwrite = Ви хочете перезаписати дані? @@ -361,6 +370,7 @@ Move Up = Move Up Network Connection = Підключення до мережі NEW DATA = Нове збереження No = Ні +None = None ObtainingIP = Obtaining IP address.\nPlease wait... OK = OK Old savedata detected = Виявлено старий формат збережень @@ -369,6 +379,7 @@ Password = Password Remove = Remove Reset = Скинути Resize = Розмір +Restart = Restart Retry = Повторити Save = Зберегти Save completed = Збереження завершено. @@ -388,6 +399,9 @@ Supported = Підтримується There is no data = Немає даних Toggle All = Переключити все Toggle List = Toggle list +Top Center = Top center +Top Left = Top left +Top Right = Top right Unsupported = Не підтримується Username = Username When you save, it will load on a PSP, but not an older PPSSPP = Коли ви збережетесь, це може завантажуватись на PSP, але не старій версії PPSSPP @@ -604,6 +618,7 @@ Stereo rendering = Stereo rendering Stretch = Розтягнення Texture Filter = Фільтр текстур Texture Filtering = Фільтрація текстур +Texture replacement pack activated = Texture replacement pack activated Texture Scaling = Масштабування текстур Texture Shader = Texture shader Turn off Hardware Tessellation - unsupported = Вимкніть "апаратна тесселляція": не підтримується @@ -789,11 +804,6 @@ AdHoc Server = Ad hoc server AdhocServer Failed to Bind Port = Ad hoc server failed to bind port AM: Data from Unknown Port = AM: Data from Unknown Port Auto = Auto -Bottom Center = Знизу центрально -Bottom Left = Знизу ліворуч -Bottom Right = Знизу праворуч -Center Left = Центр ліворуч -Center Right = Центр праворуч Change Mac Address = Змінити Mac-адресу Change proAdhocServer Address = Спеціальна адреса серверу (localhost = multiple instances) ChangeMacSaveConfirm = Generate a new MAC address? @@ -827,7 +837,6 @@ Invalid IP or hostname = Недійсне IP або ім'я хоста Minimum Timeout = Minimum timeout (override in ms, 0 = default) Misc = Miscellaneous (default = PSP compatibility) Network Initialized = Мережа ініціалізована -None = None Please change your Port Offset = Please change your port offset Port offset = Зсув порту (0 = сумісність з PSP) Open PPSSPP Multiplayer Wiki Page = Open PPSSPP Multiplayer Wiki Page @@ -838,11 +847,9 @@ Quick Chat 3 = Швидкий чат 3 Quick Chat 4 = Швидкий чат 4 Quick Chat 5 = Швидкий чат 5 QuickChat = Швидкий чат +Randomize = Randomize Send = Надіслати Send Discord Presence information = Відправляти інформацію про гру в Discord -Top Center = Top center -Top Left = Top left -Top Right = Top right Unable to find UPnP device = Unable to find UPnP device UPnP (port-forwarding) = UPnP (port forwarding) UPnP need to be reinitialized = UPnP need to be reinitialized @@ -852,6 +859,23 @@ Validating address... = Підтвердження адреси... WLAN Channel = WLAN channel You're in Offline Mode, go to lobby or online hall = Ви перебуваєте в офлайн-режимі, переходите у лоббі чи онлайн-режим +[PSPSettings] +Auto = Auto +Chinese (simplified) = Chinese (simplified) +Chinese (traditional) = Chinese (traditional) +Dutch = Dutch +English = English +French = French +Game language = Game language +German = German +Italian = Italian +Japanese = Japanese +Korean = Korean +Games often don't support all languages = Games often don't support all languages +Portuguese = Portuguese +Russian = Russian +Spanish = Spanish + [Pause] Cheats = Чит-коди Continue = Продовжити diff --git a/assets/lang/vi_VN.ini b/assets/lang/vi_VN.ini index eb99b7c0e2..fd8c4f06ec 100644 --- a/assets/lang/vi_VN.ini +++ b/assets/lang/vi_VN.ini @@ -6,12 +6,15 @@ Account = Account Achievement Unlocked = Achievement Unlocked Achievements = Achievements Achievements are disabled = Achievements are disabled +Can't log in to RetroAchievements right now = Can't log in to RetroAchievements right now Challenge Mode = Challenge Mode Challenge Mode (no savestates) = Challenge Mode (no savestates) +Contacting RetroAchievements server... = Contacting RetroAchievements server... Customize = Customize Earned = You have earned %d of %d achievements, and %d of %d points Encore Mode = Encore Mode How to use RetroAchievements = How to use RetroAchievements +In Encore mode - listings may be wrong below = In Encore mode - listings may be wrong below Leaderboard score submission = Leaderboard score submission Leaderboard submission is enabled = Leaderboard submission is enabled Leaderboards = Leaderboards @@ -88,6 +91,7 @@ Disable diagonal input = Disable diagonal input Double tap = Double tap Enable gesture control = Enable gesture control Enable standard shortcut keys = Enable standard shortcut keys +frames = frames Gesture = Gesture Gesture mapping = Gesture mapping Glowing borders = Ánh sáng đường viền @@ -116,6 +120,7 @@ OnScreen = Hiện điều khiển cảm ứng trên màn hình Portrait = Chiều dọc Portrait Reversed = Chiều dọc đảo ngược PSP Action Buttons = Nút hành động của PSP +Rapid fire interval = Rapid fire interval Raw input = Raw input Repeat mode = Repeat mode Reset to defaults = Reset to defaults @@ -310,10 +315,14 @@ VFPU = VFPU * PSP res = * PSP như thật Active = Hoạt động Back = Trở lại +Bottom Center = Bottom center +Bottom Left = Bottom left +Bottom Right = Bottom right Cancel = Hủy Center = Trung tâm -ChangingGPUBackends = Đổi thư viện đồ họa cho GPU yêu cầu phải khởi động lại PPSSPP.\nBạn có muốn khởi động lại ngay không? -ChangingInflightFrames = Changing graphics command buffering requires PPSSPP to restart. Restart now? +Center Left = Center left +Center Right = Center right +Changing this setting requires PPSSPP to restart. = Changing this setting requires PPSSPP to restart. Channel: = Channel: Choose PPSSPP save folder = Chọn thư mục save của PPSSPP Confirm Overwrite = Bạn có muốn ghi đè lên dữ liệu này không? @@ -361,6 +370,7 @@ Move Up = Move Up Network Connection = Kết nối mạng NEW DATA = DỮ LIỆU MỚI No = Không +None = None ObtainingIP = Obtaining IP address.\nPlease wait... OK = OK Old savedata detected = Phát hiện dữ liệu cũ @@ -369,6 +379,7 @@ Password = Password Remove = Remove Reset = Mặc định Resize = Thay đổi kích thước +Restart = Restart Retry = Thử lại Save = Lưu Save completed = Đã lưu xong. @@ -388,6 +399,9 @@ Supported = Được hỗ trợ There is no data = Không có dữ liệu. Toggle All = Tất cả Toggle List = Toggle list +Top Center = Top center +Top Left = Top left +Top Right = Top right Unsupported = không hổ trợ Username = Username When you save, it will load on a PSP, but not an older PPSSPP = Khi bạn save, nó có thể load được trên máy PSP, nhưng lại không thể load được trên PPSSPP phiên bản cũ @@ -604,6 +618,7 @@ Stereo rendering = Stereo rendering Stretch = Stretch Texture Filter = Bộ lọc họa tiết Texture Filtering = Lọc texture +Texture replacement pack activated = Texture replacement pack activated Texture Scaling = Họa tiết rộng Texture Shader = Texture shader Turn off Hardware Tessellation - unsupported = Tắt "phần cứng tessellation": không hổ trợ @@ -789,11 +804,6 @@ AdHoc Server = Ad hoc server AdhocServer Failed to Bind Port = Ad hoc server failed to bind port AM: Data from Unknown Port = AM: Data from Unknown Port Auto = Auto -Bottom Center = Bottom center -Bottom Left = Bottom left -Bottom Right = Bottom right -Center Left = Center left -Center Right = Center right Change Mac Address = Đổi địa chỉ MAC Change proAdhocServer Address = Change PRO ad hoc server IP address (localhost = multiple instances) ChangeMacSaveConfirm = Generate a new MAC address? @@ -827,7 +837,6 @@ Invalid IP or hostname = Invalid IP or hostname Minimum Timeout = Minimum timeout (override in ms, 0 = default) Misc = Miscellaneous (default = PSP compatibility) Network Initialized = Mạng đã được khởi tạo -None = None Please change your Port Offset = Please change your port offset Port offset = Port offset(0 = PSP compatibility) Open PPSSPP Multiplayer Wiki Page = Open PPSSPP Multiplayer Wiki Page @@ -838,11 +847,9 @@ Quick Chat 3 = Quick chat 3 Quick Chat 4 = Quick chat 4 Quick Chat 5 = Quick chat 5 QuickChat = Quick chat +Randomize = Randomize Send = Send Send Discord Presence information = Send Discord "Rich Presence" information -Top Center = Top center -Top Left = Top left -Top Right = Top right Unable to find UPnP device = Unable to find UPnP device UPnP (port-forwarding) = UPnP (port forwarding) UPnP need to be reinitialized = UPnP need to be reinitialized @@ -852,6 +859,23 @@ Validating address... = Validating address... WLAN Channel = WLAN channel You're in Offline Mode, go to lobby or online hall = You're in offline mode, go to lobby or online hall +[PSPSettings] +Auto = Auto +Chinese (simplified) = Chinese (simplified) +Chinese (traditional) = Chinese (traditional) +Dutch = Dutch +English = English +French = French +Game language = Game language +German = German +Italian = Italian +Japanese = Japanese +Korean = Korean +Games often don't support all languages = Games often don't support all languages +Portuguese = Portuguese +Russian = Russian +Spanish = Spanish + [Pause] Cheats = cheat Continue = Tiếp tục diff --git a/assets/lang/zh_CN.ini b/assets/lang/zh_CN.ini index 84e8ce7aaa..d25bb7f341 100644 --- a/assets/lang/zh_CN.ini +++ b/assets/lang/zh_CN.ini @@ -1,39 +1,42 @@ [Achievements] -%1: Leaderboard attempt failed = %1: Leaderboard attempt failed -%1: Leaderboard attempt started = %1: Leaderboard attempt started -%d achievements, %d points = %d achievements, %d points -Account = Account -Achievement Unlocked = Achievement Unlocked -Achievements = Achievements -Achievements are disabled = Achievements are disabled -Challenge Mode = Challenge Mode -Challenge Mode (no savestates) = Challenge Mode (no savestates) -Customize = Customize -Earned = You have earned %d of %d achievements, and %d of %d points -Encore Mode = Encore Mode -How to use RetroAchievements = How to use RetroAchievements -Leaderboard score submission = Leaderboard score submission -Leaderboard submission is enabled = Leaderboard submission is enabled -Leaderboards = Leaderboards -Links = Links -Locked achievements = Locked achievements -Log bad memory accesses = Log bad memory accesses +%1: Leaderboard attempt failed = %1: 排名失败 +%1: Leaderboard attempt started = %1: 开始尝试排名 +%d achievements, %d points = %d 成就, %d 点数 +Account = 账号 +Achievement Unlocked = 成就达成! +Achievements = 成就 +Achievements are disabled = 未启用成就功能 +Can't log in to RetroAchievements right now = 暂时无法登录到RetroAchievements +Challenge Mode = 挑战模式 +Challenge Mode (no savestates) = 挑战模式 (无即时存档) +Contacting RetroAchievements server... = 连接RetroAchievements服务器... +Customize = 自定义 +Earned = 你已经达成%d个成就,总共有%d个可解锁, %d点数,总共%d点 +Encore Mode = 回味模式 +How to use RetroAchievements = 如何使用RetroAchievements +In Encore mode - listings may be wrong below = 回味模式 - 下面列表中可能错误 +Leaderboard score submission = 排名得分提交 +Leaderboard submission is enabled = 排名上传已启用 +Leaderboards = 排行榜 +Links = 链接 +Locked achievements = 可解锁的成就 +Log bad memory accesses = 记录损坏的内存访问 Mastered %1 = Mastered %1 -Register on www.retroachievements.org = Register on www.retroachievements.org -RetroAchievements are not available for this game = RetroAchievements are not available for this game -RetroAchievements website = RetroAchievements website -Rich Presence = Rich Presence -Save state loaded without achievement data = Save state loaded without achievement data -Save states not available in Challenge Mode = Save states not available in Challenge Mode -Sound Effects = Sound Effects -Statistics = Statistics -Submitted %1 for %2 = Submitted %1 for %2 -Syncing achievements data... = Syncing achievements data... -Test Mode = Test Mode -This feature is not available in Challenge Mode = This feature is not available in Challenge Mode -This game has no achievements = This game has no achievements -Unlocked achievements = Unlocked achievements -Unofficial achievements = Unofficial achievements +Register on www.retroachievements.org = 在www.retroachievements.org注册 +RetroAchievements are not available for this game = RetroAchievements暂不适用于当前游戏 +RetroAchievements website = RetroAchievements网站 +Rich Presence = 展示详细信息 +Save state loaded without achievement data = 读取即时存档时不带成就数据 +Save states not available in Challenge Mode = 挑战模式下禁用即时存档 +Sound Effects = 音效 +Statistics = 统计 +Submitted %1 for %2 = 对%2提交%1中 +Syncing achievements data... = 同步成就数据中... +Test Mode = 测试模式 +This feature is not available in Challenge Mode = 挑战模式下此功能不可用 +This game has no achievements = 此游戏没有成就列表 +Unlocked achievements = 解锁的成就 +Unofficial achievements = 自定义成就 [Audio] Alternate speed volume = 备选速度时的音量 @@ -88,6 +91,7 @@ Disable diagonal input = 禁用对角线方向输入 Double tap = 双击 Enable gesture control = 启用体感操作 Enable standard shortcut keys = 启用标准快捷键 +frames = 帧 Gesture = 体感 Gesture mapping = 体感映射 Glowing borders = 细边框(发光) @@ -116,6 +120,7 @@ OnScreen = 屏幕虚拟键 Portrait = 纵向 Portrait Reversed = 纵向反转 PSP Action Buttons = PSP 动作键 +Rapid fire interval = 连发间隔 Raw input = 原始输入 Repeat mode = 连发模式 Reset to defaults = 重置为默认 @@ -204,7 +209,7 @@ Log Console = 控制台日志 (&L) Memory View... = 查看内存 (&V)... More Settings... = 更多设置 (&M)... Nearest = 邻近取样 (&N) -Recent = &Recent +Recent = 最近游戏(&R) Skip Buffer Effects = 跳过缓冲效果(更快) (&S) Off = 关闭 (&O) Open Chat = 聊天窗口 @@ -310,10 +315,14 @@ VFPU = VFPU * PSP res = * PSP分辨率 Active = 激活 Back = 返回 +Bottom Center = 正下方 +Bottom Left = 左下方 +Bottom Right = 右下方 Cancel = 取消 Center = 中心 -ChangingGPUBackends = 更改引擎模式需要重启PPSSPP。立刻重启吗? -ChangingInflightFrames = 更改缓冲图形命令需要重启PPSSPP。立刻重启吗? +Center Left = 左中间 +Center Right = 右中间 +Changing this setting requires PPSSPP to restart. = 设置生效需要重新启动 PPSSPP。 Channel: = 信道: Choose PPSSPP save folder = 选择PPSSPP存档的文件夹 Confirm Overwrite = 您要覆盖这个存档吗? @@ -333,14 +342,14 @@ DeleteConfirmGameConfig = 您确定要删除此游戏的设置吗? DeleteFailed = 无法删除存档。 Deleting = 删除中\n请稍候... Disable All = 全部禁用 -Disabled = Disabled +Disabled = 禁用 Dumps = 转储 Edit = 编辑 Enable All = 全部启用 -Enabled = Enabled +Enabled = 启用 Enter = 确定 -Failed to connect to server, check your internet connection. = Failed to connect to server, check your internet connection. -Failed to log in, check your username and password. = Failed to log in, check your username and password. +Failed to connect to server, check your internet connection. = 未连接到服务器,请检查网络情况 +Failed to log in, check your username and password. = 无法登录,请检查账号密码 Filter = 筛选 Finish = 完成 GE Frame Dumps = GE帧转储 @@ -351,24 +360,26 @@ Load = 载入 Load completed = 已载入。 Loading = 载入中\n请稍候... LoadingFailed = 无法载入存档。 -Log in = Log in -Log out = Log out -Logged in! = Logged in! -Logging in... = Logging in... +Log in = 登录 +Log out = 退出 +Logged in! = 登录成功! +Logging in... = 登录中... Move = 移动位置 Move Down = 移至下层 Move Up = 移至上层 Network Connection = 网络连接 NEW DATA = 新建存档 No = 否 +None = 无 ObtainingIP = 获取IP地址.\n请稍候... OK = 确定 Old savedata detected = 检测到旧版本的存档数据 Options = 选项 -Password = Password +Password = 密码 Remove = 删除 Reset = 重置 Resize = 调整大小 +Restart = 重新启动 Retry = 重试 Save = 保存 Save completed = 保存完成。 @@ -377,7 +388,7 @@ SavingFailed = 无法保存存档。 Search = 搜索 seconds, 0:off = 经过几秒,0为"从不" Select = 选择 -Settings = Settings +Settings = 设置 Shift = 翻页 Skip = 跳过 Snap = 网格对齐 @@ -388,8 +399,11 @@ Supported = 支持 There is no data = 存档不存在。 Toggle All = 全选/不选 Toggle List = 显示列表 +Top Center = 正上方 +Top Left = 左上方 +Top Right = 右上方 Unsupported = 不支持 -Username = Username +Username = 用户名 When you save, it will load on a PSP, but not an older PPSSPP = 当您保存后, 它只会在PSP实机上载入, 而不是旧版本的PPSSPP When you save, it will not work on outdated PSP Firmware anymore = 当您保存后, 它无法在过时的PSP固件上运行 Yes = 是 @@ -604,6 +618,7 @@ Stereo rendering = 立体渲染 Stretch = 拉伸 Texture Filter = 纹理过滤方法 Texture Filtering = 纹理过滤 +Texture replacement pack activated = 已使用纹理替换包 Texture Scaling = 纹理缩放 Texture Shader = 纹理着色器 Turn off Hardware Tessellation - unsupported = 关闭"硬件曲面细分":不支持 @@ -632,12 +647,12 @@ Zip archive corrupt = ZIP 文档损坏 Zip file does not contain PSP software = ZIP文件不包含PSP软件 [KeyMapping] -Allow combo mappings = Allow combo mappings +Allow combo mappings = 多键合一 Autoconfigure = 自动配置 Autoconfigure for device = 根据设备配置 Bind All = 全部绑定 Clear All = 全部清除 -Combo mappings are not enabled = Combo mappings are not enabled +Combo mappings are not enabled = 请打开多键合一 Default All = 恢复默认 Map a new key for = 输入一个新按键: Map Key = 按键映射 @@ -789,11 +804,6 @@ AdHoc Server = Ad Hoc服务器 AdhocServer Failed to Bind Port = 无法绑定Adhoc服务器端口 AM: Data from Unknown Port = AM:来自未知端口的数据 Auto = 自动 -Bottom Center = 正下方 -Bottom Left = 左下方 -Bottom Right = 右下方 -Center Left = 左中间 -Center Right = 右中间 Change Mac Address = 更改MAC地址 Change proAdhocServer Address = 更改 PRO Ad Hoc 服务器IP地址 ChangeMacSaveConfirm = 确认随机生成新的MAC地址? @@ -827,7 +837,6 @@ Invalid IP or hostname = IP或主机名是无效的 Minimum Timeout = 最小超时(以毫秒为单位,0=默认值) Misc = 其他(默认=兼容PSP) Network Initialized = 网络已初始化 -None = 无 Please change your Port Offset = 请更改您的端口偏移 Port offset = 端口偏移(0 = 兼容PSP) Open PPSSPP Multiplayer Wiki Page = PPSSPP多人联机Wiki页面 @@ -839,11 +848,9 @@ Quick Chat 3 = 快速聊天3 Quick Chat 4 = 快速聊天4 Quick Chat 5 = 快速聊天5 QuickChat = 快速聊天 +Randomize = Randomize Send = 发送 Send Discord Presence information = 发送 Discord 在线状态 -Top Center = 正上方 -Top Left = 左上方 -Top Right = 右上方 Unable to find UPnP device = 找不到UPnP设备 UPnP (port-forwarding) = UPnP(端口转发) UPnP need to be reinitialized = UPnP 需要重新初始化 @@ -853,6 +860,23 @@ Validating address... = 正在验证地址... WLAN Channel = WLAN信道 You're in Offline Mode, go to lobby or online hall = 您现在处于离线模式, 请进入房间或在线大厅 +[PSPSettings] +Auto = Auto +Chinese (simplified) = 简体中文 +Chinese (traditional) = 繁体中文 +Dutch = 荷兰语 +English = 英语 +French = 法语 +Game language = 游戏语言 +German = 德语 +Italian = 意大利语 +Japanese = 日语 +Korean = 韩语 +Games often don't support all languages = 仅较少游戏支持切换语言 +Portuguese = 葡萄牙语 +Russian = 俄语 +Spanish = 西班牙语 + [Pause] Cheats = 金手指 Continue = 继续游戏 @@ -900,7 +924,7 @@ Saturation = 色彩 Scanlines = 扫描线(CRT) Sharpen = 锐化 SideBySide = 双目渲染 -SSAA(Gauss) = SSAA(超级采样抗锯齿-高斯) +SSAA(Gauss) = SSAA(超级采样抗锯齿-高斯采样) Strength = 强度 Tex4xBRZ = 4xBRZ TexMMPX = MMPX diff --git a/assets/lang/zh_TW.ini b/assets/lang/zh_TW.ini index 52a9b85a4c..4629c1d6c4 100644 --- a/assets/lang/zh_TW.ini +++ b/assets/lang/zh_TW.ini @@ -6,12 +6,15 @@ Account = 帳戶 Achievement Unlocked = Achievement Unlocked Achievements = 成就 Achievements are disabled = Achievements are disabled +Can't log in to RetroAchievements right now = Can't log in to RetroAchievements right now Challenge Mode = 挑戰模式 Challenge Mode (no savestates) = 挑戰模式 (無存檔) +Contacting RetroAchievements server... = Contacting RetroAchievements server... Customize = Customize Earned = 您已經取得 %d/%d 個成就,以及 %d/%d 個點數 Encore Mode = Encore Mode How to use RetroAchievements = 如何使用 RetroAchievements +In Encore mode - listings may be wrong below = In Encore mode - listings may be wrong below Leaderboard score submission = Leaderboard score submission Leaderboard submission is enabled = 排行榜提交已啟用 Leaderboards = 排行榜 @@ -88,6 +91,7 @@ Disable diagonal input = 停用對角線輸入 Double tap = 點兩下 Enable gesture control = 啟用手勢控制 Enable standard shortcut keys = 啟用標準快速鍵 +frames = frames Gesture = 手勢 Gesture mapping = 手勢對應 Glowing borders = 發光框線 @@ -116,6 +120,7 @@ OnScreen = 螢幕觸控控制 Portrait = 直向 Portrait Reversed = 直向反轉 PSP Action Buttons = PSP 動作按鈕 +Rapid fire interval = Rapid fire interval Raw input = 原始輸入 Reset to defaults = 重設為預設值 Screen Rotation = 螢幕旋轉 @@ -310,10 +315,14 @@ VFPU = VFPU * PSP res = * PSP 解析度 Active = 啟用 Back = 返回 +Bottom Center = 正下 +Bottom Left = 左下 +Bottom Right = 右下 Cancel = 取消 Center = 中心 -ChangingGPUBackends = 變更 GPU 後端需要重新啟動 PPSSPP,要立即重新啟動嗎? -ChangingInflightFrames = 變更圖形命令緩衝處理需要重新啟動 PPSSPP,要立即重新啟動嗎? +Center Left = 正左 +Center Right = 正右 +Changing this setting requires PPSSPP to restart. = 更改此設置需要重新啟動 PPSSPP。 Channel: = 頻道: Choose PPSSPP save folder = 選擇 PPSSPP 存檔資料夾 Confirm Overwrite = 要覆寫這個存檔的資料嗎? @@ -361,6 +370,7 @@ Move Up = 上移 Network Connection = 網路連線 NEW DATA = 新存檔 No = 否 +None = 無 ObtainingIP = 正在取得 IP 位址\n請稍候… OK = 確定 Old savedata detected = 偵測到舊版存檔資料 @@ -369,6 +379,7 @@ Password = 密碼 Remove = 移除 Reset = 重設 Resize = 調整大小 +Restart = 重新開始 Retry = 重試 Save = 儲存 Save completed = 儲存完成 @@ -388,6 +399,9 @@ Supported = 支援 There is no data = 沒有資料 Toggle All = 切換全部 Toggle List = 切換清單 +Top Center = 正上 +Top Left = 左上 +Top Right = 右上 Unsupported = 不支援 Username = 使用者名稱 When you save, it will load on a PSP, but not an older PPSSPP = 在您儲存後,它將會只能在 PSP 上載入,而非舊版 PPSSPP @@ -604,6 +618,7 @@ Stereo rendering = 立體轉譯 Stretch = 延伸 Texture Filter = 紋理濾鏡 Texture Filtering = 紋理濾鏡 +Texture replacement pack activated = Texture replacement pack activated Texture Scaling = 紋理縮放 Texture Shader = 紋理著色器 Turn off Hardware Tessellation - unsupported = 關閉「硬體鑲嵌」:不支援 @@ -760,11 +775,6 @@ AdHoc Server = 臨機操作伺服器 AdhocServer Failed to Bind Port = 臨機操作伺服器無法繫結連接埠 AM: Data from Unknown Port = AM:來自不明連接埠的資料 Auto = 自動 -Bottom Center = 正下 -Bottom Left = 左下 -Bottom Right = 右下 -Center Left = 正左 -Center Right = 正右 Change Mac Address = 變更 MAC 位址 ChangeMacSaveConfirm = 產生新的 MAC 位址? ChangeMacSaveWarning = 某些遊戲在載入儲存資料時會驗證 MAC 位址,這可能會損毀舊存檔 @@ -798,7 +808,6 @@ Invalid IP or hostname = 無效的 IP 或主機名稱 Minimum Timeout = 最小逾時 (單位為毫秒,0 = 預設) Misc = 雜項 (預設 = PSP 相容性) Network Initialized = 網路已初始化 -None = 無 Please change your Port Offset = 請變更您的連接埠位移 Port offset = 連接埠位移 (0 = PSP 相容性) Open PPSSPP Multiplayer Wiki Page = 開啟 PPSSPP 多人遊戲維基頁面 @@ -809,11 +818,9 @@ Quick Chat 3 = 快速聊天 3 Quick Chat 4 = 快速聊天 4 Quick Chat 5 = 快速聊天 5 QuickChat = 快速聊天 +Randomize = Randomize Send = 傳送 Send Discord Presence information = 傳送 Discord「Rich Presence」資訊 -Top Center = 正上 -Top Left = 左上 -Top Right = 右上 Unable to find UPnP device = 找不到 UPnP 裝置 UPnP (port-forwarding) = UPnP (連接埠轉送) UPnP need to be reinitialized = UPnP 需要重新初始化 @@ -823,6 +830,23 @@ Validating address... = 正在驗證位址… WLAN Channel = WLAN 頻道 You're in Offline Mode, go to lobby or online hall = 您目前處於離線模式,請前往大廳或線上大廳 +[PSPSettings] +Auto = Auto +Chinese (simplified) = Chinese (simplified) +Chinese (traditional) = Chinese (traditional) +Dutch = Dutch +English = English +French = French +Game language = Game language +German = German +Italian = Italian +Japanese = Japanese +Korean = Korean +Games often don't support all languages = Games often don't support all languages +Portuguese = Portuguese +Russian = Russian +Spanish = Spanish + [Pause] Cheats = 密技 Continue = 繼續 diff --git a/assets/shaders/defaultshaders.ini b/assets/shaders/defaultshaders.ini index 7f9e7766ec..73217d45e9 100644 --- a/assets/shaders/defaultshaders.ini +++ b/assets/shaders/defaultshaders.ini @@ -38,6 +38,18 @@ SettingDefaultValue2=1.0 SettingMaxValue2=2.0 SettingMinValue2=0.5 SettingStep2=0.1 +[FakeReclections] +Name=Fake Reflections +Fragment=fakereclections.fsh +Vertex=fxaa.vsh +SettingName1=Amount +SettingDefaultValue1=0.6 +SettingMaxValue1=1.0 +SettingMinValue1=0.0 +SettingName2=Power +SettingDefaultValue2=0.5 +SettingMaxValue2=1.0 +SettingMinValue2=0.0 [Bloom] Name=Bloom Shader Fragment=bloom.fsh diff --git a/assets/shaders/fakereclections.fsh b/assets/shaders/fakereclections.fsh new file mode 100644 index 0000000000..b2302edd92 --- /dev/null +++ b/assets/shaders/fakereclections.fsh @@ -0,0 +1,36 @@ +// Simple fake reflection shader; created to use in PPSSPP. +// Without excessive samples and complex nested loops +// (to make it compatible with low-end GPUs and to ensure ps_2_0 compatibility). + +#ifdef GL_ES +precision mediump float; +precision mediump int; +#endif + +uniform sampler2D sampler0; +varying vec2 v_texcoord0; + +uniform vec4 u_setting; + +void main() +{ + //get the pixel color + vec3 color = texture2D(sampler0, v_texcoord0.xy).rgb; + float gray = (color.r + color.g + color.b) / 3.0; + float saturation = (abs(color.r - gray) + abs(color.g - gray) + abs(color.b - gray)) / 3.0; + + //persistent random offset to hide that the reflection is wrong + float rndx = mod(v_texcoord0.x + gray, 0.03) + mod(v_texcoord0.y + saturation, 0.05); + float rndy = mod(v_texcoord0.y + saturation, 0.03) + mod(v_texcoord0.x + gray, 0.05); + + //show the effect mainly on the bottom part of the screen + float step = (max(gray, saturation) + 0.1) * v_texcoord0.y; + + //the fake reflection is just a watered copy of the frame moved slightly lower + vec3 reflection = texture2D(sampler0, v_texcoord0 + vec2(rndx, rndy - min(v_texcoord0.y, 0.25)) * step).rgb; + + //apply parameters and mix the colors + reflection *= 4.0 * (1.0 - gray) * u_setting.x; + reflection *= reflection * step * u_setting.y; + gl_FragColor = vec4(color + reflection, 1.0); +} diff --git a/assets/ui_atlas.meta b/assets/ui_atlas.meta index f2df6bfd1d..b969735e1c 100644 Binary files a/assets/ui_atlas.meta and b/assets/ui_atlas.meta differ diff --git a/assets/ui_atlas.zim b/assets/ui_atlas.zim index 3ab85b7e22..d47766ad49 100644 Binary files a/assets/ui_atlas.zim and b/assets/ui_atlas.zim differ diff --git a/ext/CMakeLists.txt b/ext/CMakeLists.txt index 8fc0581d45..9bbb9b7a74 100644 --- a/ext/CMakeLists.txt +++ b/ext/CMakeLists.txt @@ -31,6 +31,9 @@ add_subdirectory(snappy) add_subdirectory(udis86) add_subdirectory(SPIRV-Cross-build) add_subdirectory(rcheevos-build) +if(NOT HTTPS_NOT_AVAILABLE) + add_subdirectory(naett-build) +endif() if(USE_DISCORD AND NOT IOS AND NOT LIBRETRO) add_subdirectory(discord-rpc-build) endif() diff --git a/ext/naett b/ext/naett new file mode 160000 index 0000000000..8c3a5d9278 --- /dev/null +++ b/ext/naett @@ -0,0 +1 @@ +Subproject commit 8c3a5d92780eacacc24ab34a12cf585997e08ff9 diff --git a/ext/naett-build/CMakeLists.txt b/ext/naett-build/CMakeLists.txt new file mode 100644 index 0000000000..6b3b489346 --- /dev/null +++ b/ext/naett-build/CMakeLists.txt @@ -0,0 +1,10 @@ +cmake_minimum_required (VERSION 3.2.0) +project (naett) + +set(SRC_DIR ../naett) + +set(ALL_SOURCE_FILES + ${SRC_DIR}/naett.c + ) + +add_library(naett STATIC ${ALL_SOURCE_FILES}) diff --git a/ext/rcheevos b/ext/rcheevos index 97db619aa4..13333347f0 160000 --- a/ext/rcheevos +++ b/ext/rcheevos @@ -1 +1 @@ -Subproject commit 97db619aa4de15fe19887255e75899ae65d2e432 +Subproject commit 13333347f0809aa751856c849ffe751d612ae0a4 diff --git a/ext/riscv-disas.cpp b/ext/riscv-disas.cpp index 282bfa77bb..7eba6b51eb 100644 --- a/ext/riscv-disas.cpp +++ b/ext/riscv-disas.cpp @@ -674,6 +674,49 @@ const rv_opcode_data opcode_data[] = { { "fsflags", rv_codec_i_csr, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, { "fsrmi", rv_codec_i_csr, rv_fmt_rd_zimm, NULL, 0, 0, 0 }, { "fsflagsi", rv_codec_i_csr, rv_fmt_rd_zimm, NULL, 0, 0, 0 }, + { "add.uw", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, + { "andn", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, + { "bclr", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, + { "bclri", rv_codec_i_sh7, rv_fmt_rd_rs1_imm, NULL, 0, 0, 0 }, + { "bext", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, + { "bexti", rv_codec_i_sh7, rv_fmt_rd_rs1_imm, NULL, 0, 0, 0 }, + { "binv", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, + { "binvi", rv_codec_i_sh7, rv_fmt_rd_rs1_imm, NULL, 0, 0, 0 }, + { "bset", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, + { "bseti", rv_codec_i_sh7, rv_fmt_rd_rs1_imm, NULL, 0, 0, 0 }, + { "clmul", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, + { "clmulh", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, + { "clmulr", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, + { "clz", rv_codec_i, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, + { "clzw", rv_codec_i, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, + { "cpop", rv_codec_i, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, + { "cpopw", rv_codec_i, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, + { "ctz", rv_codec_i, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, + { "ctzw", rv_codec_i, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, + { "max", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, + { "maxu", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, + { "min", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, + { "minu", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, + { "orc.b", rv_codec_i, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, + { "orn", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, + { "rev8", rv_codec_i, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, + { "rol", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, + { "rolw", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, + { "ror", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, + { "rori", rv_codec_i_sh7, rv_fmt_rd_rs1_imm, NULL, 0, 0, 0 }, + { "roriw", rv_codec_i_sh7, rv_fmt_rd_rs1_imm, NULL, 0, 0, 0 }, + { "rorw", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, + { "sext.b", rv_codec_i, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, + { "sext.h", rv_codec_i, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, + { "sh1add", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, + { "sh1add.uw", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, + { "sh2add", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, + { "sh2add.uw", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, + { "sh3add", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, + { "sh3add.uw", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, + { "slli.uw", rv_codec_i_sh6, rv_fmt_rd_rs1_imm, NULL, 0, 0, 0 }, + { "xnor", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, + { "zext.h", rv_codec_i, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, }; /* CSR names */ @@ -1091,6 +1134,18 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) case 1: switch (((inst >> 27) & 0b11111)) { case 0: op = rv_op_slli; break; + case 5: op = rv_op_bseti; break; + case 9: op = rv_op_bclri; break; + case 12: + switch (((inst >> 20) & 0b11111)) { + case 0: op = rv_op_clz; break; + case 1: op = rv_op_ctz; break; + case 2: op = rv_op_cpop; break; + case 4: op = rv_op_sext_b; break; + case 5: op = rv_op_sext_h; break; + } + break; + case 13: op = rv_op_binvi; break; } break; case 2: op = rv_op_slti; break; @@ -1099,7 +1154,20 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) case 5: switch (((inst >> 27) & 0b11111)) { case 0: op = rv_op_srli; break; + case 5: + switch (((inst >> 20) & 0b1111111)) { + case 7: op = rv_op_orc_b; break; + } + break; case 8: op = rv_op_srai; break; + case 9: op = rv_op_bexti; break; + case 12: op = rv_op_rori; break; + case 13: + switch (((inst >> 20) & 0b1111111)) { + case 24: if (isa == rv32) op = rv_op_rev8; break; + case 56: if (isa == rv64) op = rv_op_rev8; break; + } + break; } break; case 6: op = rv_op_ori; break; @@ -1113,12 +1181,22 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) case 1: switch (((inst >> 25) & 0b1111111)) { case 0: op = rv_op_slliw; break; + case 4: op = rv_op_slli_uw; break; + case 5: op = rv_op_slli_uw; break; + case 48: + switch (((inst >> 20) & 0b11111)) { + case 0: op = rv_op_clzw; break; + case 1: op = rv_op_ctzw; break; + case 2: op = rv_op_cpopw; break; + } + break; } break; case 5: switch (((inst >> 25) & 0b1111111)) { case 0: op = rv_op_srliw; break; case 32: op = rv_op_sraiw; break; + case 48: op = rv_op_roriw; break; } break; } @@ -1207,8 +1285,32 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) case 13: op = rv_op_divu; break; case 14: op = rv_op_rem; break; case 15: op = rv_op_remu; break; + case 36: + switch (((inst >> 20) & 0b11111)) { + case 0: if (isa == rv32) op = rv_op_zext_h; break; + } + break; + case 41: op = rv_op_clmul; break; + case 42: op = rv_op_clmulr; break; + case 43: op = rv_op_clmulh; break; + case 44: op = rv_op_min; break; + case 45: op = rv_op_minu; break; + case 46: op = rv_op_max; break; + case 47: op = rv_op_maxu; break; + case 130: op = rv_op_sh1add; break; + case 132: op = rv_op_sh2add; break; + case 134: op = rv_op_sh3add; break; + case 161: op = rv_op_bset; break; case 256: op = rv_op_sub; break; + case 260: op = rv_op_xnor; break; case 261: op = rv_op_sra; break; + case 262: op = rv_op_orn; break; + case 263: op = rv_op_andn; break; + case 289: op = rv_op_bclr; break; + case 293: op = rv_op_bext; break; + case 385: op = rv_op_rol; break; + case 389: op = rv_op_ror; break; + case 417: op = rv_op_binv; break; } break; case 13: op = rv_op_lui; break; @@ -1222,8 +1324,19 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) case 13: op = rv_op_divuw; break; case 14: op = rv_op_remw; break; case 15: op = rv_op_remuw; break; + case 32: op = rv_op_add_uw; break; + case 36: + switch (((inst >> 20) & 0b11111)) { + case 0: if (isa == rv64) op = rv_op_zext_h; break; + } + break; + case 130: op = rv_op_sh1add_uw; break; + case 132: op = rv_op_sh2add_uw; break; + case 134: op = rv_op_sh3add_uw; break; case 256: op = rv_op_subw; break; case 261: op = rv_op_sraw; break; + case 385: op = rv_op_rolw; break; + case 389: op = rv_op_rorw; break; } break; case 16: @@ -2195,7 +2308,7 @@ static void decode_inst_lift_pseudo(rv_decode *dec) static char *append(char *buf, const char *src, const char *end) { while (buf < end && *src) - *buf++ = *src++; + *buf++ = *src++; return buf; } diff --git a/ext/riscv-disas.h b/ext/riscv-disas.h index ebf5571743..b7fcdfa509 100644 --- a/ext/riscv-disas.h +++ b/ext/riscv-disas.h @@ -524,6 +524,49 @@ typedef enum { rv_op_fsflags, rv_op_fsrmi, rv_op_fsflagsi, + rv_op_add_uw, + rv_op_andn, + rv_op_bclr, + rv_op_bclri, + rv_op_bext, + rv_op_bexti, + rv_op_binv, + rv_op_binvi, + rv_op_bset, + rv_op_bseti, + rv_op_clmul, + rv_op_clmulh, + rv_op_clmulr, + rv_op_clz, + rv_op_clzw, + rv_op_cpop, + rv_op_cpopw, + rv_op_ctz, + rv_op_ctzw, + rv_op_max, + rv_op_maxu, + rv_op_min, + rv_op_minu, + rv_op_orc_b, + rv_op_orn, + rv_op_rev8, + rv_op_rol, + rv_op_rolw, + rv_op_ror, + rv_op_rori, + rv_op_roriw, + rv_op_rorw, + rv_op_sext_b, + rv_op_sext_h, + rv_op_sh1add, + rv_op_sh1add_uw, + rv_op_sh2add, + rv_op_sh2add_uw, + rv_op_sh3add, + rv_op_sh3add_uw, + rv_op_slli_uw, + rv_op_xnor, + rv_op_zext_h, } rv_op; /* structures */ diff --git a/headless/Headless.vcxproj b/headless/Headless.vcxproj index 695f20dbe4..65cc85d5b4 100644 --- a/headless/Headless.vcxproj +++ b/headless/Headless.vcxproj @@ -191,7 +191,7 @@ Console true - mf.lib;mfplat.lib;mfreadwrite.lib;mfuuid.lib;shlwapi.lib;Winmm.lib;Ws2_32.lib;dsound.lib;avcodec.lib;avformat.lib;avutil.lib;swresample.lib;swscale.lib;comctl32.lib;d3d9.lib;d3dx9d.lib;dxguid.lib;opengl32.lib;glu32.lib;%(AdditionalDependencies) + winhttp.lib;mf.lib;mfplat.lib;mfreadwrite.lib;mfuuid.lib;shlwapi.lib;Winmm.lib;Ws2_32.lib;dsound.lib;avcodec.lib;avformat.lib;avutil.lib;swresample.lib;swscale.lib;comctl32.lib;d3d9.lib;d3dx9d.lib;dxguid.lib;opengl32.lib;glu32.lib;%(AdditionalDependencies) 0x00400000 false true @@ -225,7 +225,7 @@ Console true - mf.lib;mfplat.lib;mfreadwrite.lib;mfuuid.lib;shlwapi.lib;Winmm.lib;Ws2_32.lib;dsound.lib;avcodec.lib;avformat.lib;avutil.lib;swresample.lib;swscale.lib;comctl32.lib;d3d9.lib;d3dx9d.lib;dxguid.lib;opengl32.lib;glu32.lib;%(AdditionalDependencies) + winhttp.lib;mf.lib;mfplat.lib;mfreadwrite.lib;mfuuid.lib;shlwapi.lib;Winmm.lib;Ws2_32.lib;dsound.lib;avcodec.lib;avformat.lib;avutil.lib;swresample.lib;swscale.lib;comctl32.lib;d3d9.lib;d3dx9d.lib;dxguid.lib;opengl32.lib;glu32.lib;%(AdditionalDependencies) 0x00400000 false true @@ -258,7 +258,7 @@ Console true - ole32.lib;mf.lib;mfplat.lib;mfreadwrite.lib;mfuuid.lib;shlwapi.lib;Winmm.lib;Ws2_32.lib;dsound.lib;avcodec.lib;avformat.lib;avutil.lib;swresample.lib;swscale.lib;comctl32.lib;d3d9.lib;dxguid.lib;shell32.lib;advapi32.lib;gdi32.lib;%(AdditionalDependencies) + winhttp.lib;ole32.lib;mf.lib;mfplat.lib;mfreadwrite.lib;mfuuid.lib;shlwapi.lib;Winmm.lib;Ws2_32.lib;dsound.lib;avcodec.lib;avformat.lib;avutil.lib;swresample.lib;swscale.lib;comctl32.lib;d3d9.lib;dxguid.lib;shell32.lib;advapi32.lib;gdi32.lib;%(AdditionalDependencies) /ignore:4049 /ignore:4217 %(AdditionalOptions) ../ffmpeg/Windows/aarch64/lib @@ -289,7 +289,7 @@ Console true - ole32.lib;mf.lib;mfplat.lib;mfreadwrite.lib;mfuuid.lib;shlwapi.lib;Winmm.lib;Ws2_32.lib;dsound.lib;avcodec.lib;avformat.lib;avutil.lib;swresample.lib;swscale.lib;comctl32.lib;d3d9.lib;dxguid.lib;shell32.lib;advapi32.lib;gdi32.lib;%(AdditionalDependencies) + winhttp.lib;ole32.lib;mf.lib;mfplat.lib;mfreadwrite.lib;mfuuid.lib;shlwapi.lib;Winmm.lib;Ws2_32.lib;dsound.lib;avcodec.lib;avformat.lib;avutil.lib;swresample.lib;swscale.lib;comctl32.lib;d3d9.lib;dxguid.lib;shell32.lib;advapi32.lib;gdi32.lib;%(AdditionalDependencies) /ignore:4049 /ignore:4217 %(AdditionalOptions) ../ffmpeg/Windows/arm/lib @@ -323,7 +323,7 @@ true true true - mf.lib;mfplat.lib;mfreadwrite.lib;mfuuid.lib;shlwapi.lib;Winmm.lib;Ws2_32.lib;dsound.lib;avcodec.lib;avformat.lib;avutil.lib;swresample.lib;swscale.lib;comctl32.lib;d3d9.lib;d3dx9.lib;dxguid.lib;opengl32.lib;glu32.lib;%(AdditionalDependencies) + winhttp.lib;mf.lib;mfplat.lib;mfreadwrite.lib;mfuuid.lib;shlwapi.lib;Winmm.lib;Ws2_32.lib;dsound.lib;avcodec.lib;avformat.lib;avutil.lib;swresample.lib;swscale.lib;comctl32.lib;d3d9.lib;d3dx9.lib;dxguid.lib;opengl32.lib;glu32.lib;%(AdditionalDependencies) 0x00400000 false true @@ -362,7 +362,7 @@ true true true - mf.lib;mfplat.lib;mfreadwrite.lib;mfuuid.lib;shlwapi.lib;Winmm.lib;Ws2_32.lib;dsound.lib;avcodec.lib;avformat.lib;avutil.lib;swresample.lib;swscale.lib;comctl32.lib;d3d9.lib;d3dx9.lib;dxguid.lib;opengl32.lib;glu32.lib;%(AdditionalDependencies) + winhttp.lib;mf.lib;mfplat.lib;mfreadwrite.lib;mfuuid.lib;shlwapi.lib;Winmm.lib;Ws2_32.lib;dsound.lib;avcodec.lib;avformat.lib;avutil.lib;swresample.lib;swscale.lib;comctl32.lib;d3d9.lib;d3dx9.lib;dxguid.lib;opengl32.lib;glu32.lib;%(AdditionalDependencies) 0x00400000 false true @@ -400,7 +400,7 @@ true true true - ole32.lib;mf.lib;mfplat.lib;mfreadwrite.lib;mfuuid.lib;shlwapi.lib;Winmm.lib;Ws2_32.lib;dsound.lib;avcodec.lib;avformat.lib;avutil.lib;swresample.lib;swscale.lib;comctl32.lib;d3d9.lib;dxguid.lib;shell32.lib;advapi32.lib;gdi32.lib;%(AdditionalDependencies) + winhttp.lib;ole32.lib;mf.lib;mfplat.lib;mfreadwrite.lib;mfuuid.lib;shlwapi.lib;Winmm.lib;Ws2_32.lib;dsound.lib;avcodec.lib;avformat.lib;avutil.lib;swresample.lib;swscale.lib;comctl32.lib;d3d9.lib;dxguid.lib;shell32.lib;advapi32.lib;gdi32.lib;%(AdditionalDependencies) /ignore:4049 /ignore:4217 %(AdditionalOptions) ../ffmpeg/Windows/aarch64/lib;%(AdditionalLibraryDirectories) @@ -435,7 +435,7 @@ true true true - ole32.lib;mf.lib;mfplat.lib;mfreadwrite.lib;mfuuid.lib;shlwapi.lib;Winmm.lib;Ws2_32.lib;dsound.lib;avcodec.lib;avformat.lib;avutil.lib;swresample.lib;swscale.lib;comctl32.lib;d3d9.lib;dxguid.lib;shell32.lib;advapi32.lib;gdi32.lib;%(AdditionalDependencies) + winhttp.lib;ole32.lib;mf.lib;mfplat.lib;mfreadwrite.lib;mfuuid.lib;shlwapi.lib;Winmm.lib;Ws2_32.lib;dsound.lib;avcodec.lib;avformat.lib;avutil.lib;swresample.lib;swscale.lib;comctl32.lib;d3d9.lib;dxguid.lib;shell32.lib;advapi32.lib;gdi32.lib;%(AdditionalDependencies) /ignore:4049 /ignore:4217 %(AdditionalOptions) ../ffmpeg/Windows/arm/lib;%(AdditionalLibraryDirectories) diff --git a/ios/main.mm b/ios/main.mm index e90b60e8a1..4ca2f041dd 100644 --- a/ios/main.mm +++ b/ios/main.mm @@ -21,6 +21,7 @@ #include "Common/System/Request.h" #include "Common/StringUtils.h" #include "Common/Profiler/Profiler.h" +#include "Core/Config.h" #include "UI/DarwinFileSystemServices.h" static int (*csops)(pid_t pid, unsigned int ops, void * useraddr, size_t usersize); @@ -108,6 +109,8 @@ std::string System_GetProperty(SystemProperty prop) { return StringFromFormat("iOS %s", version.c_str()); case SYSPROP_LANGREGION: return [[[NSLocale currentLocale] objectForKey:NSLocaleIdentifier] UTF8String]; + case SYSPROP_BUILD_VERSION: + return PPSSPP_GIT_VERSION; default: return ""; } @@ -165,7 +168,10 @@ bool System_GetPropertyBool(SystemProperty prop) { #endif case SYSPROP_CAN_JIT: return get_debugged(); - +#ifndef HTTPS_NOT_AVAILABLE + case SYSPROP_SUPPORTS_HTTPS: + return true; +#endif default: return false; } diff --git a/libretro/CMakeLists.txt b/libretro/CMakeLists.txt index da76ff3e02..7ebc7a6e18 100644 --- a/libretro/CMakeLists.txt +++ b/libretro/CMakeLists.txt @@ -23,7 +23,7 @@ endif() if(NOT MSVC) if (APPLE OR IOS) - target_link_libraries(ppsspp_libretro "-Wl,-exported_symbols_list,${CMAKE_CURRENT_SOURCE_DIR}/libretro.osx.def") + target_link_libraries(ppsspp_libretro objc "-Wl,-exported_symbols_list,${CMAKE_CURRENT_SOURCE_DIR}/libretro.osx.def") else() target_link_libraries(ppsspp_libretro "-Wl,-Bsymbolic") endif() diff --git a/libretro/LibretroVulkanContext.cpp b/libretro/LibretroVulkanContext.cpp index c7072df17b..c07a000bcc 100644 --- a/libretro/LibretroVulkanContext.cpp +++ b/libretro/LibretroVulkanContext.cpp @@ -139,7 +139,7 @@ void LibretroVulkanContext::CreateDrawContext() { return; } - draw_ = Draw::T3DCreateVulkanContext(vk); + draw_ = Draw::T3DCreateVulkanContext(vk, true); ((VulkanRenderManager*)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER))->SetInflightFrames(g_Config.iInflightFrames); SetGPUBackend(GPUBackend::VULKAN); } diff --git a/libretro/Makefile.common b/libretro/Makefile.common index 29bc7de7e4..70c00dfee2 100644 --- a/libretro/Makefile.common +++ b/libretro/Makefile.common @@ -224,6 +224,8 @@ SOURCES_C += \ $(EXTDIR)/rcheevos/src/rhash/md5.c COREFLAGS += -DSTACK_LINE_READER_BUFFER_SIZE=1024 +COREFLAGS += -DHTTPS_NOT_AVAILABLE + ifeq ($(PLATFORM_EXT), android) COREFLAGS += -DHAVE_DLFCN_H else ifneq ($(PLATFORM_EXT), win32) @@ -351,6 +353,8 @@ SOURCES_CXX += \ $(COMMONDIR)/Net/HTTPClient.cpp \ $(COMMONDIR)/Net/HTTPHeaders.cpp \ $(COMMONDIR)/Net/HTTPServer.cpp \ + $(COMMONDIR)/Net/HTTPRequest.cpp \ + $(COMMONDIR)/Net/HTTPNaettRequest.cpp \ $(COMMONDIR)/Net/NetBuffer.cpp \ $(COMMONDIR)/Net/Resolve.cpp \ $(COMMONDIR)/Net/Sinks.cpp \ @@ -667,6 +671,7 @@ SOURCES_CXX += \ $(COREDIR)/MIPS/JitCommon/JitCommon.cpp \ $(COREDIR)/MIPS/JitCommon/JitState.cpp \ $(COREDIR)/MIPS/JitCommon/JitBlockCache.cpp \ + $(COREDIR)/MIPS/IR/IRAnalysis.cpp \ $(COREDIR)/MIPS/IR/IRCompALU.cpp \ $(COREDIR)/MIPS/IR/IRCompBranch.cpp \ $(COREDIR)/MIPS/IR/IRCompFPU.cpp \ diff --git a/source_assets/image/buttons.svg b/source_assets/image/buttons.svg index a335acacfb..b6bf625d11 100644 --- a/source_assets/image/buttons.svg +++ b/source_assets/image/buttons.svg @@ -1688,10 +1688,10 @@ id="filter5096-3" style="color-interpolation-filters:sRGB" inkscape:label="Drop Shadow" - x="-0.065" - y="-0.065" - width="1.13" - height="1.13"> + x="-0.17000000000000001" + y="-0.13999999999999999" + width="1.3600000000000001" + height="1.28"> + inkscape:label="Drop Shadow" + width="1.7200000000000004" + height="1.2" + x="-0.23000000000000009" + y="-0.10000000000000001"> + + + + + + + + + + + + + + + + + + + + + + x="-3.2951229" + y="-3.9247968" /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/source_assets/image/fast_forward.png b/source_assets/image/fast_forward.png new file mode 100644 index 0000000000..d7fb6ae6b4 Binary files /dev/null and b/source_assets/image/fast_forward.png differ diff --git a/source_assets/image/pausepng.png b/source_assets/image/pausepng.png new file mode 100644 index 0000000000..b618a7ffd9 Binary files /dev/null and b/source_assets/image/pausepng.png differ diff --git a/source_assets/image/play.png b/source_assets/image/play.png new file mode 100644 index 0000000000..d324c25b84 Binary files /dev/null and b/source_assets/image/play.png differ diff --git a/source_assets/image/record.png b/source_assets/image/record.png new file mode 100644 index 0000000000..05008ad253 Binary files /dev/null and b/source_assets/image/record.png differ diff --git a/source_assets/image/speaker.png b/source_assets/image/speaker.png new file mode 100644 index 0000000000..b043e5e631 Binary files /dev/null and b/source_assets/image/speaker.png differ diff --git a/source_assets/image/speaker_max.png b/source_assets/image/speaker_max.png new file mode 100644 index 0000000000..8625b2b2ee Binary files /dev/null and b/source_assets/image/speaker_max.png differ diff --git a/source_assets/image/speaker_off.png b/source_assets/image/speaker_off.png new file mode 100644 index 0000000000..b8b1e8e2bb Binary files /dev/null and b/source_assets/image/speaker_off.png differ diff --git a/source_assets/image/stop.png b/source_assets/image/stop.png new file mode 100644 index 0000000000..3860c5b38b Binary files /dev/null and b/source_assets/image/stop.png differ diff --git a/source_assets/image/winner_cup.png b/source_assets/image/winner_cup.png new file mode 100644 index 0000000000..27ba1ed945 Binary files /dev/null and b/source_assets/image/winner_cup.png differ diff --git a/ui_atlasscript.txt b/ui_atlasscript.txt index c2f633f3ad..62a8037c7a 100644 --- a/ui_atlasscript.txt +++ b/ui_atlasscript.txt @@ -72,3 +72,11 @@ image I_THREE_DOTS source_assets/image/three_dots.png copy image I_INFO source_assets/image/info.png copy image I_RETROACHIEVEMENTS_LOGO source_assets/image/retroachievements_logo.png copy image I_CHECKMARK source_assets/image/checkmark.png copy +image I_PLAY source_assets/image/play.png copy +image I_STOP source_assets/image/stop.png copy +image I_FASTFORWARD source_assets/image/fast_forward.png copy +image I_RECORD source_assets/image/record.png copy +image I_SPEAKER source_assets/image/speaker.png copy +image I_SPEAKER_MAX source_assets/image/speaker_max.png copy +image I_SPEAKER_OFF source_assets/image/speaker_off.png copy +image I_WINNER_CUP source_assets/image/winner_cup.png copy diff --git a/unittest/JitHarness.cpp b/unittest/JitHarness.cpp index 44f0cdf751..750b39ab57 100644 --- a/unittest/JitHarness.cpp +++ b/unittest/JitHarness.cpp @@ -178,26 +178,18 @@ bool TestJit() { jit_speed = ExecCPUTest(); // Disassemble - JitBlockCache *cache = MIPSComp::jit->GetBlockCache(); - JitBlock *block = cache->GetBlock(0); // Should only be one block. -#if PPSSPP_ARCH(ARM) - std::vector lines = DisassembleArm2(block->normalEntry, block->codeSize); -#elif PPSSPP_ARCH(ARM64) - std::vector lines = DisassembleArm64(block->normalEntry, block->codeSize); -#elif PPSSPP_ARCH(X86) || PPSSPP_ARCH(AMD64) - std::vector lines = DisassembleX86(block->normalEntry, block->codeSize); -#elif PPSSPP_ARCH(RISCV64) - std::vector lines = DisassembleRV64(block->normalEntry, block->codeSize); -#else - std::vector lines; -#endif - // Cut off at 25 due to the repetition above. Might need tweaking for large instructions. - const int cutoff = 25; - for (int i = 0; i < std::min((int)lines.size(), cutoff); i++) { - printf("%s\n", lines[i].c_str()); + JitBlockCacheDebugInterface *cache = MIPSComp::jit->GetBlockCacheDebugInterface(); + if (cache) { + JitBlockDebugInfo block = cache->GetBlockDebugInfo(0); // Should only be one block. + std::vector &lines = block.targetDisasm; + // Cut off at 25 due to the repetition above. Might need tweaking for large instructions. + const int cutoff = 25; + for (int i = 0; i < std::min((int)lines.size(), cutoff); i++) { + printf("%s\n", lines[i].c_str()); + } + if (lines.size() > cutoff) + printf("...\n"); } - if (lines.size() > cutoff) - printf("...\n"); printf("Jit was %fx faster than interp.\n\n", jit_speed / interp_speed); } diff --git a/unittest/UnitTests.vcxproj b/unittest/UnitTests.vcxproj index e96bfd7892..7417f4f38b 100644 --- a/unittest/UnitTests.vcxproj +++ b/unittest/UnitTests.vcxproj @@ -181,7 +181,7 @@ Console true - mf.lib;mfplat.lib;mfreadwrite.lib;mfuuid.lib;shlwapi.lib;Ws2_32.lib;winmm.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;dsound.lib;avcodec.lib;avformat.lib;avutil.lib;swresample.lib;swscale.lib;comctl32.lib;d3d9.lib;dxguid.lib;opengl32.lib;glu32.lib;%(AdditionalDependencies) + winhttp.lib;mf.lib;mfplat.lib;mfreadwrite.lib;mfuuid.lib;shlwapi.lib;Ws2_32.lib;winmm.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;dsound.lib;avcodec.lib;avformat.lib;avutil.lib;swresample.lib;swscale.lib;comctl32.lib;d3d9.lib;dxguid.lib;opengl32.lib;glu32.lib;%(AdditionalDependencies) /ignore:4049 /ignore:4217 %(AdditionalOptions) ../ffmpeg/Windows/x86/lib @@ -205,7 +205,7 @@ Console true - mf.lib;mfplat.lib;mfreadwrite.lib;mfuuid.lib;shlwapi.lib;Ws2_32.lib;winmm.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;dsound.lib;avcodec.lib;avformat.lib;avutil.lib;swresample.lib;swscale.lib;comctl32.lib;d3d9.lib;dxguid.lib;opengl32.lib;glu32.lib;%(AdditionalDependencies) + winhttp.lib;mf.lib;mfplat.lib;mfreadwrite.lib;mfuuid.lib;shlwapi.lib;Ws2_32.lib;winmm.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;dsound.lib;avcodec.lib;avformat.lib;avutil.lib;swresample.lib;swscale.lib;comctl32.lib;d3d9.lib;dxguid.lib;opengl32.lib;glu32.lib;%(AdditionalDependencies) /ignore:4049 /ignore:4217 %(AdditionalOptions) ../ffmpeg/Windows/x86_64/lib @@ -229,7 +229,7 @@ Console true - mf.lib;mfplat.lib;mfreadwrite.lib;mfuuid.lib;shlwapi.lib;Ws2_32.lib;winmm.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;dsound.lib;avcodec.lib;avformat.lib;avutil.lib;swresample.lib;swscale.lib;comctl32.lib;d3d9.lib;dxguid.lib;%(AdditionalDependencies) + winhttp.lib;mf.lib;mfplat.lib;mfreadwrite.lib;mfuuid.lib;shlwapi.lib;Ws2_32.lib;winmm.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;dsound.lib;avcodec.lib;avformat.lib;avutil.lib;swresample.lib;swscale.lib;comctl32.lib;d3d9.lib;dxguid.lib;%(AdditionalDependencies) /ignore:4049 /ignore:4217 %(AdditionalOptions) ../ffmpeg/Windows/aarch64/lib @@ -254,7 +254,7 @@ Console true - mf.lib;mfplat.lib;mfreadwrite.lib;mfuuid.lib;shlwapi.lib;Ws2_32.lib;winmm.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;dsound.lib;avcodec.lib;avformat.lib;avutil.lib;swresample.lib;swscale.lib;comctl32.lib;d3d9.lib;dxguid.lib;%(AdditionalDependencies) + winhttp.lib;mf.lib;mfplat.lib;mfreadwrite.lib;mfuuid.lib;shlwapi.lib;Ws2_32.lib;winmm.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;dsound.lib;avcodec.lib;avformat.lib;avutil.lib;swresample.lib;swscale.lib;comctl32.lib;d3d9.lib;dxguid.lib;%(AdditionalDependencies) /ignore:4049 /ignore:4217 %(AdditionalOptions) ../ffmpeg/Windows/arm/lib @@ -283,7 +283,7 @@ true true true - mf.lib;mfplat.lib;mfreadwrite.lib;mfuuid.lib;shlwapi.lib;Ws2_32.lib;winmm.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;dsound.lib;avcodec.lib;avformat.lib;avutil.lib;swresample.lib;swscale.lib;comctl32.lib;d3d9.lib;dxguid.lib;opengl32.lib;glu32.lib;%(AdditionalDependencies) + winhttp.lib;mf.lib;mfplat.lib;mfreadwrite.lib;mfuuid.lib;shlwapi.lib;Ws2_32.lib;winmm.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;dsound.lib;avcodec.lib;avformat.lib;avutil.lib;swresample.lib;swscale.lib;comctl32.lib;d3d9.lib;dxguid.lib;opengl32.lib;glu32.lib;%(AdditionalDependencies) /ignore:4049 /ignore:4217 %(AdditionalOptions) ../ffmpeg/Windows/x86/lib @@ -313,7 +313,7 @@ true true true - mf.lib;mfplat.lib;mfreadwrite.lib;mfuuid.lib;shlwapi.lib;Ws2_32.lib;winmm.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;dsound.lib;avcodec.lib;avformat.lib;avutil.lib;swresample.lib;swscale.lib;comctl32.lib;d3d9.lib;dxguid.lib;opengl32.lib;glu32.lib;%(AdditionalDependencies) + winhttp.lib;mf.lib;mfplat.lib;mfreadwrite.lib;mfuuid.lib;shlwapi.lib;Ws2_32.lib;winmm.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;dsound.lib;avcodec.lib;avformat.lib;avutil.lib;swresample.lib;swscale.lib;comctl32.lib;d3d9.lib;dxguid.lib;opengl32.lib;glu32.lib;%(AdditionalDependencies) /ignore:4049 /ignore:4217 %(AdditionalOptions) ../ffmpeg/Windows/x86_64/lib @@ -343,7 +343,7 @@ true true true - mf.lib;mfplat.lib;mfreadwrite.lib;mfuuid.lib;shlwapi.lib;Ws2_32.lib;winmm.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;dsound.lib;avcodec.lib;avformat.lib;avutil.lib;swresample.lib;swscale.lib;comctl32.lib;d3d9.lib;dxguid.lib;%(AdditionalDependencies) + winhttp.lib;mf.lib;mfplat.lib;mfreadwrite.lib;mfuuid.lib;shlwapi.lib;Ws2_32.lib;winmm.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;dsound.lib;avcodec.lib;avformat.lib;avutil.lib;swresample.lib;swscale.lib;comctl32.lib;d3d9.lib;dxguid.lib;%(AdditionalDependencies) /ignore:4049 /ignore:4217 %(AdditionalOptions) ../ffmpeg/Windows/aarch64/lib @@ -373,7 +373,7 @@ true true true - mf.lib;mfplat.lib;mfreadwrite.lib;mfuuid.lib;shlwapi.lib;Ws2_32.lib;winmm.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;dsound.lib;avcodec.lib;avformat.lib;avutil.lib;swresample.lib;swscale.lib;comctl32.lib;d3d9.lib;dxguid.lib;%(AdditionalDependencies) + winhttp.lib;mf.lib;mfplat.lib;mfreadwrite.lib;mfuuid.lib;shlwapi.lib;Ws2_32.lib;winmm.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;dsound.lib;avcodec.lib;avformat.lib;avutil.lib;swresample.lib;swscale.lib;comctl32.lib;d3d9.lib;dxguid.lib;%(AdditionalDependencies) /ignore:4049 /ignore:4217 %(AdditionalOptions) ../ffmpeg/Windows/arm/lib