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
@@ -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