diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b8fddd1a76..00187ee1c5 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -4,6 +4,8 @@ on: push: branches: - master + # For testing. + - actions paths-ignore: - '*.{txt,md}' - 'Tools/**' @@ -176,9 +178,19 @@ jobs: with: submodules: recursive + - name: Cache Qt + uses: actions/cache@v1 + if: matrix.extra == 'qt' + id: cache-qt + with: + path: ${{ runner.workspace }}/Qt + key: ${{ runner.os }}-QtCache + - name: Install Qt uses: jurplel/install-qt-action@v2 if: matrix.extra == 'qt' + with: + cached: ${{ steps.cache-qt.outputs.cache-hit }} - uses: nttld/setup-ndk@v1 if: matrix.extra == 'android' diff --git a/.gitignore b/.gitignore index 2c9994fde5..c4e913ccf5 100644 --- a/.gitignore +++ b/.gitignore @@ -116,6 +116,3 @@ debian/ppsspp/ # RenderDoc *.rdc - -# bad output from libretro. don't want to accidentally add it -nul diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index f1af20d665..981f938ebc 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -11,26 +11,49 @@ variables: variables: EXTRA_PATH: lib +.windows-defs: + variables: + MAKEFILE_PATH: libretro + include: - - template: Jobs/Code-Quality.gitlab-ci.yml - project: 'libretro-infrastructure/ci-templates' file: '/android-cmake.yml' - project: 'libretro-infrastructure/ci-templates' file: '/linux-cmake.yml' + - project: 'libretro-infrastructure/ci-templates' + file: '/windows-x64-msvc19-msys2.yml' + - project: 'libretro-infrastructure/ci-templates' + file: '/windows-i686-msvc19-msys2.yml' stages: - build-prepare - build-shared - - build-static - - test -#Desktop -libretro-build-linux-x86_64: +# Desktop +libretro-build-linux-x64: extends: - .libretro-linux-cmake-x86_64 - .core-defs - .linux-defs +libretro-build-linux-i686: + extends: + - .libretro-linux-cmake-x86 + - .core-defs + - .linux-defs + +libretro-build-windows-x64: + extends: + - .libretro-windows-x64-msvc19-msys2-make-default + - .core-defs + - .windows-defs + +libretro-build-windows-i686: + extends: + - .libretro-windows-i686-msvc19-msys2-make-default + - .core-defs + - .windows-defs + # Android libretro-build-android-armeabi-v7a: extends: diff --git a/CMakeLists.txt b/CMakeLists.txt index 4b0b8d7f2a..48c65cb2a6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,7 @@ # vim:noexpandtab: cmake_minimum_required(VERSION 3.6) project(PPSSPP) +enable_testing() #This is supposed to work but doesn't! if(NOT ANDROID) @@ -134,7 +135,8 @@ option(USE_MINIUPNPC "Build with miniUPnPc support" ON) option(USE_SYSTEM_SNAPPY "Dynamically link against system snappy" ${USE_SYSTEM_SNAPPY}) option(USE_SYSTEM_FFMPEG "Dynamically link against system FFMPEG" ${USE_SYSTEM_FFMPEG}) option(USE_SYSTEM_LIBZIP "Dynamically link against system libzip" ${USE_SYSTEM_LIBZIP}) -option(USE_ADDRESS_SANITIZER "Use Clang memory sanitizer" ${USE_ADDRESS_SANITIZER}) +option(USE_ASAN "Use address sanitizer" OFF) +option(USE_UBSAN "Use undefined behaviour sanitizer" OFF) if(UNIX AND NOT (APPLE OR ANDROID) AND VULKAN) if(USING_X11_VULKAN) @@ -188,6 +190,9 @@ if(NOT OPENGL_LIBRARIES AND USING_GLES2) endif() if(NOT OPENGL_LIBRARIES) + if(POLICY CMP0072) + cmake_policy(SET CMP0072 NEW) + endif() find_package(OpenGL REQUIRED) endif() @@ -272,11 +277,24 @@ if(NOT MSVC) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-switch -Wno-uninitialized") endif() - if(USE_ADDRESS_SANITIZER) + if(USE_ASAN) message("Address sanitizer enabled (DEBUG only)") - set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS} -fsanitize=address") + set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fsanitize=address") set(CMAKE_LINKER_FLAGS_DEBUG "${CMAKE_LINKER_FLAGS_DEBUG} -fsanitize=address") - add_definitions(-DUSE_ADDRESS_SANITIZER) + add_definitions(-DUSE_ASAN) + endif() + if(USE_UBSAN) + message("Undefined behaviour sanitizer enabled (DEBUG only)") + set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fsanitize=undefined") + set(CMAKE_LINKER_FLAGS_DEBUG "${CMAKE_LINKER_FLAGS_DEBUG} -fsanitize=undefined") + + # UBSAN is a collection of sanitizers, including vtpr, which reqiuires RTTI. + # ext/glslang disables RTTI by default using the `ENABLE_RTTI` option. + # If RTTI is disabled, we must also disable the vtpr sanitizer. + if(NOT ENABLE_RTTI) + set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fno-sanitize=vptr") + set(CMAKE_LINKER_FLAGS_DEBUG "${CMAKE_LINKER_FLAGS_DEBUG} -fno-sanitize=vptr") + endif() endif() set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -g -D_DEBUG") @@ -287,12 +305,6 @@ if(NOT MSVC) set(CMAKE_C_FLAGS_MINSIZEREL "${CMAKE_C_FLAGS_MINSIZEREL} -Os -D_NDEBUG") set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -O2 -D_NDEBUG") set(CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO} -O2 -g -D_NDEBUG") - #TODO: Remove this and include the file properly everywhere it makes sense - # First step is too use the macros everywhere - # Second step is to remove the compatibility defines - # Third step is to include the file - # Fourth step is to remove that line! - add_compile_options(-include ${CMAKE_CURRENT_SOURCE_DIR}/ppsspp_config.h) # Disable some warnings add_definitions(-Wno-multichar) @@ -1318,8 +1330,6 @@ set(GPU_D3D9 GPU/Directx9/TextureCacheDX9.h GPU/Directx9/TextureScalerDX9.cpp GPU/Directx9/TextureScalerDX9.h - GPU/Directx9/VertexShaderGeneratorHLSL.cpp - GPU/GLES/VertexShaderGeneratorGLES.h ) set(GPU_D3D11 @@ -1470,6 +1480,8 @@ add_library(${CoreLibName} ${CoreLinkType} Core/Debugger/Breakpoints.cpp Core/Debugger/Breakpoints.h Core/Debugger/DebugInterface.h + Core/Debugger/MemBlockInfo.cpp + Core/Debugger/MemBlockInfo.h Core/Debugger/SymbolMap.cpp Core/Debugger/SymbolMap.h Core/Debugger/DisassemblyManager.cpp @@ -1898,9 +1910,7 @@ if(USE_MINIUPNPC) endif() if (WIN32) set_source_files_properties(${MINIUPNPC_SOURCES} PROPERTIES COMPILE_DEFINITIONS "MINIUPNP_STATICLIB;MINIUPNP_EXPORTS") - find_library(WINSOCK2_LIBRARY NAMES ws2_32 WS2_32 Ws2_32) - find_library(IPHLPAPI_LIBRARY NAMES iphlpapi) - set(LDLIBS ${WINSOCK2_LIBRARY} ${IPHLPAPI_LIBRARY} ${LDLIBS}) + set(LDLIBS ws2_32 iphlpapi ${LDLIBS}) #elseif (CMAKE_SYSTEM_NAME STREQUAL "Solaris") # find_library (SOCKET_LIBRARY NAMES socket) # find_library (NSL_LIBRARY NAMES nsl) @@ -1956,7 +1966,6 @@ set(WindowsFiles Windows/Debugger/CtrlMemView.h Windows/Debugger/CtrlRegisterList.cpp Windows/Debugger/CtrlRegisterList.h - Windows/Debugger/Debugger.h Windows/Debugger/DebuggerShared.cpp Windows/Debugger/DebuggerShared.h Windows/Debugger/Debugger_Disasm.cpp @@ -1965,8 +1974,6 @@ set(WindowsFiles Windows/Debugger/Debugger_MemoryDlg.h Windows/Debugger/Debugger_Lists.cpp Windows/Debugger/Debugger_Lists.h - Windows/Debugger/Debugger_Misc.cpp - Windows/Debugger/Debugger_Misc.h Windows/Debugger/Debugger_SymbolMap.h Windows/Debugger/Debugger_VFPUDlg.cpp Windows/Debugger/Debugger_VFPUDlg.h @@ -2071,7 +2078,7 @@ set(NativeAssets if(HEADLESS) - add_executable(PPSSPPHeadless + set(HeadlessSource headless/Headless.cpp headless/StubHost.cpp headless/StubHost.h @@ -2080,6 +2087,25 @@ if(HEADLESS) headless/SDLHeadlessHost.cpp headless/SDLHeadlessHost.h ) + if(WIN32) + list(APPEND HeadlessSource + headless/WindowsHeadlessHost.cpp + headless/WindowsHeadlessHost.h + Windows/GPU/D3D9Context.cpp + Windows/GPU/D3D9Context.h + Windows/GPU/D3D11Context.cpp + Windows/GPU/D3D11Context.h + Windows/GPU/WindowsGLContext.cpp + Windows/GPU/WindowsVulkanContext.cpp + Windows/W32Util/ShellUtil.cpp + Windows/W32Util/ShellUtil.h + Windows/CaptureDevice.cpp + Windows/CaptureDevice.h + Windows/W32Util/Misc.cpp + Windows/W32Util/Misc.h + ) + endif() + add_executable(PPSSPPHeadless ${HeadlessSource}) target_link_libraries(PPSSPPHeadless ${COCOA_LIBRARY} ${QUARTZ_CORE_LIBRARY} ${LinkCommon}) setup_target_project(PPSSPPHeadless headless) endif() @@ -2087,6 +2113,7 @@ endif() if(UNITTEST) add_executable(unitTest unittest/UnitTest.cpp + unittest/TestShaderGenerators.cpp unittest/TestArmEmitter.cpp unittest/TestArm64Emitter.cpp unittest/TestX64Emitter.cpp @@ -2097,6 +2124,21 @@ if(UNITTEST) ) target_link_libraries(unitTest ${COCOA_LIBRARY} ${QUARTZ_CORE_LIBRARY} ${LinkCommon} Common) setup_target_project(unitTest unittest) + add_test(arm64_emitter unitTest Arm64Emitter) + add_test(arm_emitter unitTest ArmEmitter) + add_test(x64_emitter unitTest X64Emitter) + add_test(vertex_jit unitTest VertexJit) + add_test(asin unitTest Asin) + add_test(sincos unitTest SinCos) + add_test(vfpu_sincos unitTest VFPUSinCos) + add_test(math_util unitTest MathUtil) + add_test(parsers unitTest Parsers) + add_test(jit unitTest Jit) + add_test(matrix_transpose unitTest MatrixTranspose) + add_test(parse_lbn unitTest ParseLBN) + add_test(quick_texhash unitTest QuickTexHash) + add_test(clz unitTest CLZ) + add_test(shadergen unitTest ShaderGenerators) endif() if(LIBRETRO) @@ -2190,6 +2232,7 @@ if(IOS) RESOURCE "ios/Settings.bundle" RESOURCE "MoltenVK/iOS/Frameworks" XCODE_ATTRIBUTE_IPHONEOS_DEPLOYMENT_TARGET ${DEPLOYMENT_TARGET} + XCODE_ATTRIBUTE_TARGETED_DEVICE_FAMILY "iPhone/iPad" XCODE_ATTRIBUTE_CLANG_ENABLE_OBJC_ARC YES XCODE_ATTRIBUTE_ENABLE_BITCODE NO XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "-" diff --git a/Common/ABI.cpp b/Common/ABI.cpp index 10b864f006..60e8c927e1 100644 --- a/Common/ABI.cpp +++ b/Common/ABI.cpp @@ -15,7 +15,8 @@ // Official SVN repository and contact information can be found at // http://code.google.com/p/dolphin-emu/ -#if defined(_M_IX86) || defined(_M_X64) +#include "ppsspp_config.h" +#if PPSSPP_ARCH(X86) || PPSSPP_ARCH(AMD64) #include "x64Emitter.h" #include "ABI.h" @@ -27,9 +28,9 @@ using namespace Gen; // Sets up a __cdecl function. void XEmitter::ABI_EmitPrologue(int maxCallParams) { -#ifdef _M_IX86 +#if PPSSPP_ARCH(X86) // Don't really need to do anything -#elif defined(_M_X64) +#elif PPSSPP_ARCH(AMD64) #if _WIN32 int stacksize = ((maxCallParams + 1) & ~1) * 8 + 8; // Set up a stack frame so that we can call functions @@ -43,9 +44,9 @@ void XEmitter::ABI_EmitPrologue(int maxCallParams) void XEmitter::ABI_EmitEpilogue(int maxCallParams) { -#ifdef _M_IX86 +#if PPSSPP_ARCH(X86) RET(); -#elif defined(_M_X64) +#elif PPSSPP_ARCH(AMD64) #ifdef _WIN32 int stacksize = ((maxCallParams+1)&~1)*8 + 8; ADD(64, R(RSP), Imm8(stacksize)); @@ -58,7 +59,7 @@ void XEmitter::ABI_EmitEpilogue(int maxCallParams) #endif } -#ifdef _M_IX86 // All32 +#if PPSSPP_ARCH(X86) // All32 // Shared code between Win32 and Unix32 void XEmitter::ABI_CallFunction(const void *func) { @@ -681,4 +682,4 @@ void XEmitter::ABI_RestoreStack(unsigned int /*frameSize*/) { #endif // 32bit -#endif // defined(_M_IX86) || defined(_M_X64) +#endif // PPSSPP_ARCH(X86) || PPSSPP_ARCH(AMD64) diff --git a/Common/ABI.h b/Common/ABI.h index 90cb36b6b9..0782fea6de 100644 --- a/Common/ABI.h +++ b/Common/ABI.h @@ -17,6 +17,7 @@ #pragma once +#include "ppsspp_config.h" #include "Common.h" // x86/x64 ABI:s, and helpers to help follow them when JIT-ing code. @@ -44,7 +45,7 @@ // Callee-save: RBX RBP R12 R13 R14 R15 // Parameters: RDI RSI RDX RCX R8 R9 -#ifdef _M_IX86 // 32 bit calling convention, shared by all +#if PPSSPP_ARCH(X86) // 32 bit calling convention, shared by all // 32-bit don't pass parameters in regs, but these are convenient to have anyway when we have to // choose regs to put stuff in. @@ -55,7 +56,7 @@ // 32-bit bog standard cdecl, shared between linux and windows // MacOSX 32-bit is same as System V with a few exceptions that we probably don't care much about. -#elif _M_X64 // 64 bit calling convention +#elif PPSSPP_ARCH(AMD64) // 64 bit calling convention #ifdef _WIN32 // 64-bit Windows - the really exotic calling convention diff --git a/Common/Arm64Emitter.cpp b/Common/Arm64Emitter.cpp index 5201126584..ec6e0ef61c 100644 --- a/Common/Arm64Emitter.cpp +++ b/Common/Arm64Emitter.cpp @@ -3833,8 +3833,6 @@ bool ARM64XEmitter::TryEORI2R(ARM64Reg Rd, ARM64Reg Rn, u64 imm) { } float FPImm8ToFloat(uint8_t bits) { - int E = 8; - int F = 32 - 8 - 1; int sign = bits >> 7; uint32_t f = 0; f |= (sign << 31); diff --git a/Common/ArmEmitter.cpp b/Common/ArmEmitter.cpp index b3878fd443..73d28a1eb1 100644 --- a/Common/ArmEmitter.cpp +++ b/Common/ArmEmitter.cpp @@ -23,7 +23,7 @@ #include #include -#ifdef IOS +#if PPSSPP_PLATFORM(IOS) #include #include #endif @@ -627,7 +627,7 @@ void ARMXEmitter::FlushIcache() void ARMXEmitter::FlushIcacheSection(u8 *start, u8 *end) { -#if defined(IOS) +#if PPSSPP_PLATFORM(IOS) // Header file says this is equivalent to: sys_icache_invalidate(start, end - start); sys_cache_control(kCacheFunctionPrepareForExecution, start, end - start); #elif PPSSPP_PLATFORM(WINDOWS) diff --git a/Common/BitSet.h b/Common/BitSet.h index caf7951509..28c7597048 100644 --- a/Common/BitSet.h +++ b/Common/BitSet.h @@ -2,6 +2,7 @@ #pragma once +#include "ppsspp_config.h" #include #include // for byte swapping #include @@ -27,7 +28,7 @@ inline int LeastSignificantSetBit(u32 val) _BitScanForward(&index, val); return (int)index; } -#ifdef _M_X64 +#if PPSSPP_ARCH(AMD64) inline int LeastSignificantSetBit(u64 val) { unsigned long index; diff --git a/Common/CPUDetect.cpp b/Common/CPUDetect.cpp index 70c640eee7..96c9b140e8 100644 --- a/Common/CPUDetect.cpp +++ b/Common/CPUDetect.cpp @@ -16,9 +16,9 @@ // http://code.google.com/p/dolphin-emu/ // Reference : https://stackoverflow.com/questions/6121792/how-to-check-if-a-cpu-supports-the-sse3-instruction-set -#if defined(_M_IX86) || defined(_M_X64) - #include "ppsspp_config.h" +#if PPSSPP_ARCH(X86) || PPSSPP_ARCH(AMD64) + #ifdef __ANDROID__ #include #include @@ -26,6 +26,7 @@ #include #endif +#include #include #include @@ -34,10 +35,9 @@ #include "Common/File/FileUtil.h" #include "Common/StringUtils.h" -#if defined(_WIN32) && !defined(__MINGW32__) +#if defined(_WIN32) +#include "Common/CommonWindows.h" -#define WIN32_LEAN_AND_MEAN -#include #define _interlockedbittestandset workaround_ms_header_bug_platform_sdk6_set #define _interlockedbittestandreset workaround_ms_header_bug_platform_sdk6_reset #define _interlockedbittestandset64 workaround_ms_header_bug_platform_sdk6_set64 @@ -55,24 +55,30 @@ void do_cpuid(u32 regs[4], u32 cpuid_leaf) { __cpuid((int *)regs, cpuid_leaf); } +#ifdef __MINGW32__ +static uint64_t do_xgetbv(unsigned int index) { + unsigned int eax, edx; + // This is xgetbv directly, so we can avoid compilers warning we need runtime checks. + asm(".byte 0x0f, 0x01, 0xd0" : "=a"(eax), "=d"(edx) : "c"(index)); + return ((uint64_t)edx << 32) | eax; +} +#else +#define do_xgetbv _xgetbv +#endif + #else // _WIN32 #ifdef _M_SSE #include -#define _XCR_XFEATURE_ENABLED_MASK 0 -static unsigned long long _xgetbv(unsigned int index) -{ +static uint64_t do_xgetbv(unsigned int index) { unsigned int eax, edx; __asm__ __volatile__("xgetbv" : "=a"(eax), "=d"(edx) : "c"(index)); - return ((unsigned long long)edx << 32) | eax; + return ((uint64_t)edx << 32) | eax; } - -#else -#define _XCR_XFEATURE_ENABLED_MASK 0 #endif // _M_SSE -#if !defined(MIPS) +#if !PPSSPP_ARCH(MIPS) void do_cpuidex(u32 regs[4], u32 cpuid_leaf, u32 ecxval) { #if defined(__i386__) && defined(__PIC__) @@ -94,10 +100,13 @@ void do_cpuid(u32 regs[4], u32 cpuid_leaf) do_cpuidex(regs, cpuid_leaf, 0); } -#endif // !defined(MIPS) +#endif // !PPSSPP_ARCH(MIPS) #endif // !win32 +#ifndef _XCR_XFEATURE_ENABLED_MASK +#define _XCR_XFEATURE_ENABLED_MASK 0 +#endif CPUInfo cpu_info; @@ -105,6 +114,7 @@ CPUInfo::CPUInfo() { Detect(); } +#if PPSSPP_PLATFORM(LINUX) static std::vector ParseCPUList(const std::string &filename) { std::string data; std::vector results; @@ -126,13 +136,14 @@ static std::vector ParseCPUList(const std::string &filename) { return results; } +#endif // Detects the various cpu features void CPUInfo::Detect() { memset(this, 0, sizeof(*this)); -#ifdef _M_IX86 +#if PPSSPP_ARCH(X86) Mode64bit = false; -#elif defined (_M_X64) +#elif PPSSPP_ARCH(AMD64) Mode64bit = true; OS64bit = true; #endif @@ -140,7 +151,7 @@ void CPUInfo::Detect() { #if PPSSPP_PLATFORM(UWP) OS64bit = Mode64bit; // TODO: Not always accurate! -#elif defined(_WIN32) && defined(_M_IX86) +#elif defined(_WIN32) && PPSSPP_ARCH(X86) BOOL f64 = false; IsWow64Process(GetCurrentProcess(), &f64); OS64bit = (f64 == TRUE) ? true : false; @@ -215,10 +226,8 @@ void CPUInfo::Detect() { // - Is the XSAVE bit set in CPUID? ( >>26) // - Is the OSXSAVE bit set in CPUID? ( >>27) // - XGETBV result has the XCR bit set. - if (((cpu_id[2] >> 28) & 1) && ((cpu_id[2] >> 27) & 1) && ((cpu_id[2] >> 26) & 1)) - { - if ((_xgetbv(_XCR_XFEATURE_ENABLED_MASK) & 0x6) == 0x6) - { + if (((cpu_id[2] >> 28) & 1) && ((cpu_id[2] >> 27) & 1) && ((cpu_id[2] >> 26) & 1)) { + if ((do_xgetbv(_XCR_XFEATURE_ENABLED_MASK) & 0x6) == 0x6) { bAVX = true; if ((cpu_id[2] >> 12) & 1) bFMA3 = true; @@ -432,4 +441,4 @@ std::string CPUInfo::Summarize() return sum; } -#endif // defined(_M_IX86) || defined(_M_X64) +#endif // PPSSPP_ARCH(X86) || PPSSPP_ARCH(AMD64) diff --git a/Common/ColorConv.cpp b/Common/ColorConv.cpp index 0146967e99..0e48cca0bc 100644 --- a/Common/ColorConv.cpp +++ b/Common/ColorConv.cpp @@ -15,6 +15,8 @@ // Official git repository and contact information can be found at // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. +#include "ppsspp_config.h" +#include "Common/Data/Convert/SmallDataConvert.h" #include "ColorConv.h" // NEON is in a separate file so that it can be compiled with a runtime check. #include "ColorConvNEON.h" @@ -487,8 +489,8 @@ void ConvertRGBA5551ToBGRA8888(u32 *dst, const u16 *src, u32 numPixels) { u32 r = Convert5To8(c & 0x001f); u32 g = Convert5To8((c >> 5) & 0x001f); u32 b = Convert5To8((c >> 10) & 0x001f); - // We force an arithmetic shift to get the sign bits/ - u32 a = ((s32)(s16)c) & 0xff000000; + // We force an arithmetic shift to get the sign bits. + u32 a = SignExtend16ToU32(c) & 0xff000000; dst[x] = a | (r << 16) | (g << 8) | b; } diff --git a/Common/Common.h b/Common/Common.h index 184db926d7..c5cc45ebaa 100644 --- a/Common/Common.h +++ b/Common/Common.h @@ -25,7 +25,6 @@ #ifdef _MSC_VER #pragma warning(disable:4100) #pragma warning(disable:4244) -#pragma warning(disable:4996) #endif #include "CommonTypes.h" diff --git a/Common/Common.vcxproj b/Common/Common.vcxproj index b58a19d289..d66d2ebac2 100644 --- a/Common/Common.vcxproj +++ b/Common/Common.vcxproj @@ -133,7 +133,7 @@ Level3 - _CRTDBG_MAP_ALLOC;USING_WIN_UI;_CRT_SECURE_NO_WARNINGS;WIN32;_ARCH_32=1;_M_IX86=1;_DEBUG;_LIB;_UNICODE;UNICODE;%(PreprocessorDefinitions) + _CRTDBG_MAP_ALLOC;USING_WIN_UI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;WIN32;_ARCH_32=1;_M_IX86=1;_DEBUG;_LIB;_UNICODE;UNICODE;%(PreprocessorDefinitions) Common/DbgNew.h MultiThreadedDebug ../ext/glew;../ext/snappy;../ext/glslang;../ext/libpng17;../ext;../ext/zlib;.. @@ -160,7 +160,7 @@ Level3 Common/DbgNew.h MultiThreadedDebug - _CRTDBG_MAP_ALLOC;USING_WIN_UI;_CRT_SECURE_NO_WARNINGS;WIN32;_ARCH_64=1;_M_X64=1;_DEBUG;_LIB;_UNICODE;UNICODE;%(PreprocessorDefinitions) + _CRTDBG_MAP_ALLOC;USING_WIN_UI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;WIN32;_ARCH_64=1;_M_X64=1;_DEBUG;_LIB;_UNICODE;UNICODE;%(PreprocessorDefinitions) ../ext/glew;../ext/snappy;../ext/glslang;../ext/libpng17;../ext;../ext/zlib;.. NotSet Precise @@ -186,7 +186,7 @@ Level3 Common/DbgNew.h MultiThreadedDebug - _CRTDBG_MAP_ALLOC;USING_WIN_UI;_CRT_SECURE_NO_WARNINGS;WIN32;_ARCH_64=1;_DEBUG;_LIB;_UNICODE;UNICODE;%(PreprocessorDefinitions) + _CRTDBG_MAP_ALLOC;USING_WIN_UI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;WIN32;_ARCH_64=1;_DEBUG;_LIB;_UNICODE;UNICODE;%(PreprocessorDefinitions) ../ext/glew;../ext/snappy;../ext/glslang;../ext/libpng17;../ext;../ext/zlib;.. NotSet Precise @@ -213,7 +213,7 @@ MultiThreadedDebug - _CRTDBG_MAP_ALLOC;USING_WIN_UI;_CRT_SECURE_NO_WARNINGS;WIN32;_ARCH_32=1;_DEBUG;_LIB;_UNICODE;UNICODE;%(PreprocessorDefinitions) + _CRTDBG_MAP_ALLOC;USING_WIN_UI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;WIN32;_ARCH_32=1;_DEBUG;_LIB;_UNICODE;UNICODE;%(PreprocessorDefinitions) ../ext/glew;../ext/snappy;../ext/glslang;../ext/libpng17;../ext;../ext/zlib;.. NotSet Precise @@ -240,7 +240,7 @@ MaxSpeed true true - USING_WIN_UI;_CRT_SECURE_NO_WARNINGS;WIN32;_ARCH_32=1;_M_IX86=1;NDEBUG;_LIB;_UNICODE;UNICODE;%(PreprocessorDefinitions) + USING_WIN_UI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;WIN32;_ARCH_32=1;_M_IX86=1;NDEBUG;_LIB;_UNICODE;UNICODE;%(PreprocessorDefinitions) false @@ -272,7 +272,7 @@ MaxSpeed true true - USING_WIN_UI;_CRT_SECURE_NO_WARNINGS;WIN32;_ARCH_64=1;_M_X64=1;NDEBUG;_LIB;_UNICODE;UNICODE;%(PreprocessorDefinitions) + USING_WIN_UI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;WIN32;_ARCH_64=1;_M_X64=1;NDEBUG;_LIB;_UNICODE;UNICODE;%(PreprocessorDefinitions) ../ext/glew;../ext/snappy;../ext/glslang;../ext/libpng17;../ext;../ext/zlib;.. @@ -306,7 +306,7 @@ MaxSpeed true true - USING_WIN_UI;_CRT_SECURE_NO_WARNINGS;WIN32;_ARCH_64=1;NDEBUG;_LIB;_UNICODE;UNICODE;%(PreprocessorDefinitions) + USING_WIN_UI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;WIN32;_ARCH_64=1;NDEBUG;_LIB;_UNICODE;UNICODE;%(PreprocessorDefinitions) ../ext/glew;../ext/snappy;../ext/glslang;../ext/libpng17;../ext;../ext/zlib;.. @@ -340,7 +340,7 @@ MaxSpeed true true - USING_WIN_UI;_CRT_SECURE_NO_WARNINGS;WIN32;_ARCH_32=1;NDEBUG;_LIB;_UNICODE;UNICODE;%(PreprocessorDefinitions) + USING_WIN_UI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;WIN32;_ARCH_32=1;NDEBUG;_LIB;_UNICODE;UNICODE;%(PreprocessorDefinitions) ../ext/glew;../ext/snappy;../ext/glslang;../ext/libpng17;../ext;../ext/zlib;.. diff --git a/Common/ConsoleListener.cpp b/Common/ConsoleListener.cpp index e5625c4f0f..141d92e26d 100644 --- a/Common/ConsoleListener.cpp +++ b/Common/ConsoleListener.cpp @@ -517,8 +517,6 @@ void ConsoleListener::PixelSpace(int Left, int Top, int Width, int Height, bool // Check size if (Width < 8 || Height < 12) return; - bool DBef = true; - bool DAft = true; std::string SLog = ""; // Get console info diff --git a/Common/Data/Convert/SmallDataConvert.h b/Common/Data/Convert/SmallDataConvert.h index 502cb9a0a6..ac31d5116f 100644 --- a/Common/Data/Convert/SmallDataConvert.h +++ b/Common/Data/Convert/SmallDataConvert.h @@ -180,3 +180,22 @@ inline void ExpandFloat24x3ToFloat4(float dest[4], const uint32_t src[3]) { inline uint32_t BytesToUint32(uint8_t a, uint8_t b, uint8_t c, uint8_t d) { return (a) | (b << 8) | (c << 16) | (d << 24); } + +constexpr int32_t SignExtend8ToS32(uint32_t value) { + // This extends this sign at the 8th bit to the other 24 bits. + return (int8_t)(value & 0xFF); +} + +constexpr uint32_t SignExtend8ToU32(uint32_t value) { + // Just treat the bits as unsigned. + return (uint32_t)SignExtend8ToS32(value); +} + +constexpr int32_t SignExtend16ToS32(uint32_t value) { + // Same as SignExtend8toS32, but from the 16th bit. + return (int16_t)(value & 0xFFFF); +} + +constexpr uint32_t SignExtend16ToU32(uint32_t value) { + return (uint32_t)SignExtend16ToS32(value); +} diff --git a/Common/Data/Random/Rng.h b/Common/Data/Random/Rng.h index 50ec4ea60e..8be052c0ba 100644 --- a/Common/Data/Random/Rng.h +++ b/Common/Data/Random/Rng.h @@ -1,6 +1,7 @@ #pragma once #include +#include "Common/Swap.h" // George Marsaglia-style random number generator. class GMRng { @@ -57,8 +58,8 @@ private: MT_SIZE = 624, }; - uint32_t index_; - uint32_t mt_[MT_SIZE]; + u32_le index_; + u32_le mt_[MT_SIZE]; void gen() { for(uint32_t i = 0; i < MT_SIZE; i++){ diff --git a/Common/FakeEmitter.h b/Common/FakeEmitter.h index 6b827655c8..25954ee350 100644 --- a/Common/FakeEmitter.h +++ b/Common/FakeEmitter.h @@ -24,6 +24,7 @@ #include #include "Common.h" +#include "Common/CodeBlock.h" // VCVT flags #define TO_FLOAT 0 @@ -167,7 +168,7 @@ public: Operand2(FakeReg base, ShiftType type, FakeReg shift) // RSR { Type = TYPE_RSR; - _assert_msg_(JIT, type != ST_RRX, "Invalid Operand2: RRX does not take a register shift amount"); + _assert_msg_(type != ST_RRX, "Invalid Operand2: RRX does not take a register shift amount"); IndexOrShift = shift; Shift = type; Value = base; @@ -179,29 +180,29 @@ public: switch (type) { case ST_LSL: - _assert_msg_(JIT, shift < 32, "Invalid Operand2: LSL %u", shift); + _assert_msg_(shift < 32, "Invalid Operand2: LSL %u", shift); break; case ST_LSR: - _assert_msg_(JIT, shift <= 32, "Invalid Operand2: LSR %u", shift); + _assert_msg_(shift <= 32, "Invalid Operand2: LSR %u", shift); if (!shift) type = ST_LSL; if (shift == 32) shift = 0; break; case ST_ASR: - _assert_msg_(JIT, shift < 32, "Invalid Operand2: ASR %u", shift); + _assert_msg_(shift < 32, "Invalid Operand2: ASR %u", shift); if (!shift) type = ST_LSL; if (shift == 32) shift = 0; break; case ST_ROR: - _assert_msg_(JIT, shift < 32, "Invalid Operand2: ROR %u", shift); + _assert_msg_(shift < 32, "Invalid Operand2: ROR %u", shift); if (!shift) type = ST_LSL; break; case ST_RRX: - _assert_msg_(JIT, shift == 0, "Invalid Operand2: RRX does not take an immediate shift amount"); + _assert_msg_(shift == 0, "Invalid Operand2: RRX does not take an immediate shift amount"); type = ST_ROR; break; } @@ -223,45 +224,45 @@ public: case TYPE_RSR: return RSR(); default: - _assert_msg_(JIT, false, "GetData with Invalid Type"); + _assert_msg_(false, "GetData with Invalid Type"); return 0; } } u32 IMMSR() // IMM shifted register { - _assert_msg_(JIT, Type == TYPE_IMMSREG, "IMMSR must be imm shifted register"); + _assert_msg_(Type == TYPE_IMMSREG, "IMMSR must be imm shifted register"); return ((IndexOrShift & 0x1f) << 7 | (Shift << 5) | Value); } u32 RSR() // Register shifted register { - _assert_msg_(JIT, Type == TYPE_RSR, "RSR must be RSR Of Course"); + _assert_msg_(Type == TYPE_RSR, "RSR must be RSR Of Course"); return (IndexOrShift << 8) | (Shift << 5) | 0x10 | Value; } u32 Rm() { - _assert_msg_(JIT, Type == TYPE_REG, "Rm must be with Reg"); + _assert_msg_(Type == TYPE_REG, "Rm must be with Reg"); return Value; } u32 Imm5() { - _assert_msg_(JIT, (Type == TYPE_IMM), "Imm5 not IMM value"); + _assert_msg_(Type == TYPE_IMM, "Imm5 not IMM value"); return ((Value & 0x0000001F) << 7); } u32 Imm8() { - _assert_msg_(JIT, (Type == TYPE_IMM), "Imm8Rot not IMM value"); + _assert_msg_(Type == TYPE_IMM, "Imm8Rot not IMM value"); return Value & 0xFF; } u32 Imm8Rot() // IMM8 with Rotation { - _assert_msg_(JIT, (Type == TYPE_IMM), "Imm8Rot not IMM value"); - _assert_msg_(JIT, (Rotation & 0xE1) != 0, "Invalid Operand2: immediate rotation %u", Rotation); + _assert_msg_(Type == TYPE_IMM, "Imm8Rot not IMM value"); + _assert_msg_((Rotation & 0xE1) != 0, "Invalid Operand2: immediate rotation %u", Rotation); return (1 << 25) | (Rotation << 7) | (Value & 0x000000FF); } u32 Imm12() { - _assert_msg_(JIT, (Type == TYPE_IMM), "Imm12 not IMM"); + _assert_msg_(Type == TYPE_IMM, "Imm12 not IMM"); return (Value & 0x00000FFF); } @@ -272,12 +273,12 @@ public: // expand a 8bit IMM to a 32bit value and gives you some rotation as // well. // Each rotation rotates to the right by 2 bits - _assert_msg_(JIT, (Type == TYPE_IMM), "Imm12Mod not IMM"); + _assert_msg_(Type == TYPE_IMM, "Imm12Mod not IMM"); return ((Rotation & 0xF) << 8) | (Value & 0xFF); } u32 Imm16() { - _assert_msg_(JIT, (Type == TYPE_IMM), "Imm16 not IMM"); + _assert_msg_(Type == TYPE_IMM, "Imm16 not IMM"); return ( (Value & 0xF000) << 4) | (Value & 0x0FFF); } u32 Imm16Low() @@ -286,12 +287,12 @@ public: } u32 Imm16High() // Returns high 16bits { - _assert_msg_(JIT, (Type == TYPE_IMM), "Imm16 not IMM"); + _assert_msg_(Type == TYPE_IMM, "Imm16 not IMM"); return ( ((Value >> 16) & 0xF000) << 4) | ((Value >> 16) & 0x0FFF); } u32 Imm24() { - _assert_msg_(JIT, (Type == TYPE_IMM), "Imm16 not IMM"); + _assert_msg_(Type == TYPE_IMM, "Imm16 not IMM"); return (Value & 0x0FFFFFFF); } }; @@ -380,6 +381,9 @@ public: } virtual ~FakeXEmitter() {} + void SetCodePointer(u8 *ptr, u8 *writePtr) {} + const u8 *GetCodePointer() const { return nullptr; } + void SetCodePtr(u8 *ptr) {} void ReserveCodeSpace(u32 bytes) {} const u8 *AlignCode16() { return nullptr; } @@ -411,52 +415,9 @@ public: // Everything that needs to generate machine code should inherit from this. // You get memory management for free, plus, you can use all the MOV etc functions without // having to prefix them with gen-> or something similar. -class FakeXCodeBlock : public FakeXEmitter -{ -protected: - u8 *region; - size_t region_size; - +class FakeXCodeBlock : public CodeBlock { public: - FakeXCodeBlock() : region(NULL), region_size(0) {} - virtual ~FakeXCodeBlock() { if (region) FreeCodeSpace(); } - - // Call this before you generate any code. - void AllocCodeSpace(int size) { } - - // Always clear code space with breakpoints, so that if someone accidentally executes - // uninitialized, it just breaks into the debugger. - void ClearCodeSpace() { } - - // Call this when shutting down. Don't rely on the destructor, even though it'll do the job. - void FreeCodeSpace() { } - - bool IsInSpace(const u8 *ptr) const - { - return ptr >= region && ptr < region + region_size; - } - - // Cannot currently be undone. Will write protect the entire code region. - // Start over if you need to change the code (call FreeCodeSpace(), AllocCodeSpace()). - void WriteProtect() { } - void UnWriteProtect() { } - - void ResetCodePtr() - { - SetCodePtr(region); - } - - size_t GetSpaceLeft() const - { - return region_size - (GetCodePtr() - region); - } - - u8 *GetBasePtr() { - return region; - } - - size_t GetOffset(const u8 *ptr) const { - return ptr - region; + void PoisonMemory(int offset) override { } }; diff --git a/Common/File/FileUtil.cpp b/Common/File/FileUtil.cpp index 05af060d95..0cb921f0e0 100644 --- a/Common/File/FileUtil.cpp +++ b/Common/File/FileUtil.cpp @@ -24,7 +24,14 @@ #include "ppsspp_config.h" +#ifdef __MINGW32__ +#include +#ifndef _POSIX_THREAD_SAFE_FUNCTIONS +#define _POSIX_THREAD_SAFE_FUNCTIONS 200112L +#endif +#endif #include +#include #include #include "Common/Log.h" @@ -56,9 +63,9 @@ #include #include #include -#if !defined(IOS) +#if !PPSSPP_PLATFORM(IOS) #include -#endif // !defined(IOS) +#endif // !PPSSPP_PLATFORM(IOS) #endif // __APPLE__ #include "Common/Data/Encoding/Utf8.h" @@ -238,7 +245,7 @@ bool IsDirectory(const std::string &filename) std::wstring copy = ConvertUTF8ToWString(fn); WIN32_FILE_ATTRIBUTE_DATA data{}; if (!GetFileAttributesEx(copy.c_str(), GetFileExInfoStandard, &data) || data.dwFileAttributes == INVALID_FILE_ATTRIBUTES) { - WARN_LOG(COMMON, "GetFileAttributes failed on %s: %08x", fn.c_str(), GetLastError()); + WARN_LOG(COMMON, "GetFileAttributes failed on %s: %08x", fn.c_str(), (uint32_t)GetLastError()); return false; } DWORD result = data.dwFileAttributes; @@ -304,7 +311,7 @@ bool CreateDir(const std::string &path) WARN_LOG(COMMON, "CreateDir: CreateDirectory failed on %s: already exists", path.c_str()); return true; } - ERROR_LOG(COMMON, "CreateDir: CreateDirectory failed on %s: %i", path.c_str(), error); + ERROR_LOG(COMMON, "CreateDir: CreateDirectory failed on %s: %08x", path.c_str(), (uint32_t)error); return false; #else if (mkdir(fn.c_str(), 0755) == 0) @@ -837,13 +844,13 @@ const std::string &GetExeDirectory() ExePath = program_path; #endif -#elif (defined(__APPLE__) && !defined(IOS)) || defined(__linux__) || defined(KERN_PROC_PATHNAME) +#elif (defined(__APPLE__) && !PPSSPP_PLATFORM(IOS)) || defined(__linux__) || defined(KERN_PROC_PATHNAME) char program_path[4096]; uint32_t program_path_size = sizeof(program_path) - 1; #if defined(__linux__) if (readlink("/proc/self/exe", program_path, program_path_size) > 0) -#elif defined(__APPLE__) && !defined(IOS) +#elif defined(__APPLE__) && !PPSSPP_PLATFORM(IOS) if (_NSGetExecutablePath(program_path, &program_path_size) == 0) #elif defined(KERN_PROC_PATHNAME) int mib[4] = { diff --git a/Common/File/PathBrowser.cpp b/Common/File/PathBrowser.cpp index 7bd70e2063..26dce5e338 100644 --- a/Common/File/PathBrowser.cpp +++ b/Common/File/PathBrowser.cpp @@ -146,22 +146,22 @@ void PathBrowser::SetPath(const std::string &path) { } void PathBrowser::HandlePath() { - std::lock_guard guard(pendingLock_); - if (!path_.empty() && path_[0] == '!') { + if (pendingActive_) + ResetPending(); ready_ = true; - pendingCancel_ = true; - pendingPath_.clear(); return; } if (!startsWith(path_, "http://") && !startsWith(path_, "https://")) { + if (pendingActive_) + ResetPending(); ready_ = true; - pendingCancel_ = true; - pendingPath_.clear(); return; } + std::lock_guard guard(pendingLock_); ready_ = false; + pendingActive_ = true; pendingCancel_ = false; pendingFiles_.clear(); pendingPath_ = path_; @@ -201,6 +201,12 @@ void PathBrowser::HandlePath() { }); } +void PathBrowser::ResetPending() { + std::lock_guard guard(pendingLock_); + pendingCancel_ = true; + pendingPath_.clear(); +} + bool PathBrowser::IsListingReady() { return ready_; } diff --git a/Common/File/PathBrowser.h b/Common/File/PathBrowser.h index 83e5e2f900..0873b2f58b 100644 --- a/Common/File/PathBrowser.h +++ b/Common/File/PathBrowser.h @@ -37,6 +37,7 @@ public: private: void HandlePath(); + void ResetPending(); std::string path_; std::string pendingPath_; @@ -44,6 +45,7 @@ private: std::condition_variable pendingCond_; std::mutex pendingLock_; std::thread pendingThread_; + bool pendingActive_ = false; bool pendingCancel_ = false; bool pendingStop_ = false; bool ready_ = false; diff --git a/Common/GPU/D3D11/D3D11Loader.cpp b/Common/GPU/D3D11/D3D11Loader.cpp index 2d81727ddb..0dbea80994 100644 --- a/Common/GPU/D3D11/D3D11Loader.cpp +++ b/Common/GPU/D3D11/D3D11Loader.cpp @@ -1,3 +1,4 @@ +#include "ppsspp_config.h" #include "Common/GPU/D3D11/D3D11Loader.h" #if PPSSPP_PLATFORM(UWP) diff --git a/Common/GPU/D3D11/thin3d_d3d11.cpp b/Common/GPU/D3D11/thin3d_d3d11.cpp index 1b3f5546e9..eb808fb66d 100644 --- a/Common/GPU/D3D11/thin3d_d3d11.cpp +++ b/Common/GPU/D3D11/thin3d_d3d11.cpp @@ -14,10 +14,10 @@ #include "Common/Log.h" #include -#include +#include #include #include -#include +#include #ifdef __MINGW32__ #undef __uuidof @@ -68,7 +68,7 @@ public: void CopyFramebufferImage(Framebuffer *src, int level, int x, int y, int z, Framebuffer *dst, int dstLevel, int dstX, int dstY, int dstZ, int width, int height, int depth, int channelBits, const char *tag) override; bool BlitFramebuffer(Framebuffer *src, int srcX1, int srcY1, int srcX2, int srcY2, Framebuffer *dst, int dstX1, int dstY1, int dstX2, int dstY2, int channelBits, FBBlitFilter filter, const char *tag) override; - bool CopyFramebufferToMemorySync(Framebuffer *src, int channelBits, int x, int y, int w, int h, Draw::DataFormat format, void *pixels, int pixelStride, const char *tag); + bool CopyFramebufferToMemorySync(Framebuffer *src, int channelBits, int x, int y, int w, int h, Draw::DataFormat format, void *pixels, int pixelStride, const char *tag) override; // These functions should be self explanatory. void BindFramebufferAsRenderTarget(Framebuffer *fbo, const RenderPassInfo &rp, const char *tag) override; @@ -110,7 +110,7 @@ public: void Draw(int vertexCount, int offset) override; void DrawIndexed(int vertexCount, int offset) override; void DrawUP(const void *vdata, int vertexCount) override; - void Clear(int mask, uint32_t colorval, float depthVal, int stencilVal); + void Clear(int mask, uint32_t colorval, float depthVal, int stencilVal) override; // void BeginFrame() override; //Libretro bs @@ -185,7 +185,7 @@ private: ID3D11Texture2D *bbDepthStencilTex_ = nullptr; ID3D11DepthStencilView *bbDepthStencilView_ = nullptr; - Framebuffer *curRenderTarget_ = nullptr; + AutoRef curRenderTarget_; ID3D11RenderTargetView *curRenderTargetView_ = nullptr; ID3D11DepthStencilView *curDepthStencilView_ = nullptr; // Needed to rotate stencil/viewport rectangles properly @@ -194,12 +194,12 @@ private: int curRTWidth_ = 0; int curRTHeight_ = 0; - D3D11Pipeline *curPipeline_ = nullptr; + AutoRef curPipeline_; DeviceCaps caps_{}; - D3D11BlendState *curBlend_ = nullptr; - D3D11DepthStencilState *curDepth_ = nullptr; - D3D11RasterState *curRaster_ = nullptr; + AutoRef curBlend_; + AutoRef curDepth_; + AutoRef curRaster_; ID3D11InputLayout *curInputLayout_ = nullptr; ID3D11VertexShader *curVS_ = nullptr; ID3D11PixelShader *curPS_ = nullptr; @@ -229,12 +229,12 @@ private: }; D3D11DrawContext::D3D11DrawContext(ID3D11Device *device, ID3D11DeviceContext *deviceContext, ID3D11Device1 *device1, ID3D11DeviceContext1 *deviceContext1, D3D_FEATURE_LEVEL featureLevel, HWND hWnd, std::vector deviceList) - : device_(device), + : hWnd_(hWnd), + device_(device), context_(deviceContext1), device1_(device1), context1_(deviceContext1), featureLevel_(featureLevel), - hWnd_(hWnd), deviceList_(deviceList) { // We no longer support Windows Phone. @@ -696,14 +696,6 @@ public: class D3D11Pipeline : public Pipeline { public: ~D3D11Pipeline() { - if (input) - input->Release(); - if (blend) - blend->Release(); - if (depth) - depth->Release(); - if (raster) - raster->Release(); if (il) il->Release(); if (dynamicUniforms) @@ -716,11 +708,11 @@ public: return true; } - D3D11InputLayout *input = nullptr; + AutoRef input; ID3D11InputLayout *il = nullptr; - D3D11BlendState *blend = nullptr; - D3D11DepthStencilState *depth = nullptr; - D3D11RasterState *raster = nullptr; + AutoRef blend; + AutoRef depth; + AutoRef raster; ID3D11VertexShader *vs = nullptr; ID3D11PixelShader *ps = nullptr; ID3D11GeometryShader *gs = nullptr; @@ -975,12 +967,6 @@ Pipeline *D3D11DrawContext::CreateGraphicsPipeline(const PipelineDesc &desc) { dPipeline->depth = (D3D11DepthStencilState *)desc.depthStencil; dPipeline->input = (D3D11InputLayout *)desc.inputLayout; dPipeline->raster = (D3D11RasterState *)desc.raster; - dPipeline->blend->AddRef(); - dPipeline->depth->AddRef(); - if (dPipeline->input) { - dPipeline->input->AddRef(); - } - dPipeline->raster->AddRef(); dPipeline->topology = primToD3D11[(int)desc.prim]; if (desc.uniformDesc) { dPipeline->dynamicUniformsSize = desc.uniformDesc->uniformBufferSize; @@ -991,6 +977,10 @@ Pipeline *D3D11DrawContext::CreateGraphicsPipeline(const PipelineDesc &desc) { bufdesc.Usage = D3D11_USAGE_DYNAMIC; bufdesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER; HRESULT hr = device_->CreateBuffer(&bufdesc, nullptr, &dPipeline->dynamicUniforms); + if (FAILED(hr)) { + dPipeline->Release(); + return nullptr; + } } std::vector shaders; @@ -1022,8 +1012,7 @@ Pipeline *D3D11DrawContext::CreateGraphicsPipeline(const PipelineDesc &desc) { } // Can finally create the input layout - if (dPipeline->input) { - auto &inputDesc = dPipeline->input->desc; + if (dPipeline->input != nullptr) { const std::vector &elements = dPipeline->input->elements; HRESULT hr = device_->CreateInputLayout(elements.data(), (UINT)elements.size(), vshader->byteCode_.data(), vshader->byteCode_.size(), &dPipeline->il); if (!SUCCEEDED(hr)) { @@ -1101,7 +1090,7 @@ void D3D11DrawContext::ApplyCurrentState() { curTopology_ = curPipeline_->topology; } - if (curPipeline_->input) { + if (curPipeline_->input != nullptr) { int numVBs = (int)curPipeline_->input->strides.size(); context_->IASetVertexBuffers(0, numVBs, nextVertexBuffers_, (UINT *)curPipeline_->input->strides.data(), (UINT *)nextVertexBufferOffsets_); } @@ -1375,13 +1364,13 @@ void D3D11DrawContext::Clear(int mask, uint32_t colorval, float depthVal, int st /*void D3D11DrawContext::BeginFrame() { //Libretro bs context_->OMSetRenderTargets(1, &curRenderTargetView_, curDepthStencilView_); - if (curBlend_) { + if (curBlend_ != nullptr) { context_->OMSetBlendState(curBlend_->bs, blendFactor_, 0xFFFFFFFF); } - if (curDepth_) { + if (curDepth_ != nullptr) { context_->OMSetDepthStencilState(curDepth_->dss, stencilRef_); } - if (curRaster_) { + if (curRaster_ != nullptr) { context_->RSSetState(curRaster_->rs); } context_->IASetInputLayout(curInputLayout_); @@ -1391,7 +1380,7 @@ void D3D11DrawContext::Clear(int mask, uint32_t colorval, float depthVal, int st if (curTopology_ != D3D11_PRIMITIVE_TOPOLOGY_UNDEFINED) { context_->IASetPrimitiveTopology(curTopology_); } - if (curPipeline_) { + if (curPipeline_ != nullptr) { context_->IASetVertexBuffers(0, 1, nextVertexBuffers_, (UINT *)curPipeline_->input->strides.data(), (UINT *)nextVertexBufferOffsets_); context_->IASetIndexBuffer(nextIndexBuffer_, DXGI_FORMAT_R16_UINT, nextIndexBufferOffset_); if (curPipeline_->dynamicUniforms) { @@ -1488,7 +1477,6 @@ bool D3D11DrawContext::CopyFramebufferToMemorySync(Framebuffer *src, int channel packDesc.MipLevels = 1; packDesc.Usage = D3D11_USAGE_STAGING; packDesc.SampleDesc.Count = 1; - D3D11_BOX srcBox{ (UINT)bx, (UINT)by, 0, (UINT)(bx + bw), (UINT)(by + bh), 1 }; switch (channelBits) { case FB_COLOR_BIT: packDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; // TODO: fb->colorFormat; diff --git a/Common/GPU/D3D9/thin3d_d3d9.cpp b/Common/GPU/D3D9/thin3d_d3d9.cpp index 83a1ca2bdf..79a0bb0dca 100644 --- a/Common/GPU/D3D9/thin3d_d3d9.cpp +++ b/Common/GPU/D3D9/thin3d_d3d9.cpp @@ -271,12 +271,8 @@ private: class D3D9Pipeline : public Pipeline { public: - D3D9Pipeline(LPDIRECT3DDEVICE9 device) : device_(device) {} + D3D9Pipeline() {} ~D3D9Pipeline() { - if (depthStencil) depthStencil->Release(); - if (blend) blend->Release(); - if (raster) raster->Release(); - if (inputLayout) inputLayout->Release(); } bool RequiresBuffer() override { return false; @@ -287,15 +283,13 @@ public: D3DPRIMITIVETYPE prim; int primDivisor; - D3D9InputLayout *inputLayout = nullptr; - D3D9DepthStencilState *depthStencil = nullptr; - D3D9BlendState *blend = nullptr; - D3D9RasterState *raster = nullptr; + AutoRef inputLayout; + AutoRef depthStencil; + AutoRef blend; + AutoRef raster; UniformBufferDesc dynamicUniforms; void Apply(LPDIRECT3DDEVICE9 device); -private: - LPDIRECT3DDEVICE9 device_; }; class D3D9Texture : public Texture { @@ -564,7 +558,7 @@ public: void Draw(int vertexCount, int offset) override; void DrawIndexed(int vertexCount, int offset) override; void DrawUP(const void *vdata, int vertexCount) override; - void Clear(int mask, uint32_t colorval, float depthVal, int stencilVal); + void Clear(int mask, uint32_t colorval, float depthVal, int stencilVal) override; uint64_t GetNativeObject(NativeObject obj) override { switch (obj) { @@ -612,12 +606,12 @@ private: DeviceCaps caps_{}; // Bound state - D3D9Pipeline *curPipeline_ = nullptr; - D3D9Buffer *curVBuffers_[4]{}; + AutoRef curPipeline_; + AutoRef curVBuffers_[4]; int curVBufferOffsets_[4]{}; - D3D9Buffer *curIBuffer_ = nullptr; + AutoRef curIBuffer_; int curIBufferOffset_ = 0; - Framebuffer *curRenderTarget_ = nullptr; + AutoRef curRenderTarget_; // Framebuffer state LPDIRECT3DSURFACE9 deviceRTsurf = 0; @@ -633,7 +627,7 @@ void D3D9Context::InvalidateCachedState() { #define FOURCC_INTZ ((D3DFORMAT)(MAKEFOURCC('I', 'N', 'T', 'Z'))) D3D9Context::D3D9Context(IDirect3D9 *d3d, IDirect3D9Ex *d3dEx, int adapterId, IDirect3DDevice9 *device, IDirect3DDevice9Ex *deviceEx) - : d3d_(d3d), d3dEx_(d3dEx), adapterId_(adapterId), device_(device), deviceEx_(deviceEx), caps_{} { + : d3d_(d3d), d3dEx_(d3dEx), device_(device), deviceEx_(deviceEx), adapterId_(adapterId), caps_{} { if (FAILED(d3d->GetAdapterIdentifier(adapterId, 0, &identifier_))) { ERROR_LOG(G3D, "Failed to get adapter identifier: %d", adapterId); } @@ -695,7 +689,7 @@ Pipeline *D3D9Context::CreateGraphicsPipeline(const PipelineDesc &desc) { ERROR_LOG(G3D, "Pipeline requires at least one shader"); return NULL; } - D3D9Pipeline *pipeline = new D3D9Pipeline(device_); + D3D9Pipeline *pipeline = new D3D9Pipeline(); for (auto iter : desc.shaders) { if (!iter) { ERROR_LOG(G3D, "NULL shader passed to CreateGraphicsPipeline"); @@ -715,10 +709,6 @@ Pipeline *D3D9Context::CreateGraphicsPipeline(const PipelineDesc &desc) { pipeline->blend = (D3D9BlendState *)desc.blend; pipeline->raster = (D3D9RasterState *)desc.raster; pipeline->inputLayout = (D3D9InputLayout *)desc.inputLayout; - pipeline->depthStencil->AddRef(); - pipeline->blend->AddRef(); - pipeline->raster->AddRef(); - pipeline->inputLayout->AddRef(); if (desc.uniformDesc) pipeline->dynamicUniforms = *desc.uniformDesc; return pipeline; @@ -971,9 +961,6 @@ void D3D9Context::Draw(int vertexCount, int offset) { } void D3D9Context::DrawIndexed(int vertexCount, int offset) { - D3D9Buffer *vbuf = static_cast(curVBuffers_[0]); - D3D9Buffer *ibuf = static_cast(curIBuffer_); - curPipeline_->Apply(device_); curPipeline_->inputLayout->Apply(device_); device_->SetStreamSource(0, curVBuffers_[0]->vbuffer_, curVBufferOffsets_[0], curPipeline_->inputLayout->GetStride(0)); @@ -1032,7 +1019,6 @@ void D3D9Context::SetStencilRef(uint8_t ref) { bool D3D9ShaderModule::Compile(LPDIRECT3DDEVICE9 device, const uint8_t *data, size_t size) { LPD3D_SHADER_MACRO defines = nullptr; LPD3DINCLUDE includes = nullptr; - DWORD flags = 0; LPD3DBLOB codeBuffer = nullptr; LPD3DBLOB errorBuffer = nullptr; const char *source = (const char *)data; diff --git a/Common/GPU/OpenGL/GLCommon.h b/Common/GPU/OpenGL/GLCommon.h index b9fbb3e413..26f9220d7f 100644 --- a/Common/GPU/OpenGL/GLCommon.h +++ b/Common/GPU/OpenGL/GLCommon.h @@ -2,15 +2,12 @@ #include "ppsspp_config.h" -#ifdef IOS +#if PPSSPP_PLATFORM(IOS) #include #include #elif defined(USING_GLES2) #include #include -// At least Nokia platforms need the three below -#include -typedef char GLchar; #define GL_BGRA_EXT 0x80E1 #else // OpenGL #include "GL/glew.h" @@ -61,7 +58,7 @@ typedef void (EGLAPIENTRYP PFNGLBLITFRAMEBUFFERNVPROC) ( #endif extern PFNGLBLITFRAMEBUFFERNVPROC glBlitFramebufferNV; -#ifdef IOS +#if PPSSPP_PLATFORM(IOS) extern PFNGLDISCARDFRAMEBUFFEREXTPROC glDiscardFramebufferEXT; extern PFNGLGENVERTEXARRAYSOESPROC glGenVertexArraysOES; extern PFNGLBINDVERTEXARRAYOESPROC glBindVertexArrayOES; diff --git a/Common/GPU/OpenGL/GLFeatures.cpp b/Common/GPU/OpenGL/GLFeatures.cpp index 9759915345..8628697766 100644 --- a/Common/GPU/OpenGL/GLFeatures.cpp +++ b/Common/GPU/OpenGL/GLFeatures.cpp @@ -31,7 +31,7 @@ PFNGLBINDVERTEXARRAYOESPROC glBindVertexArrayOES; PFNGLDELETEVERTEXARRAYSOESPROC glDeleteVertexArraysOES; PFNGLISVERTEXARRAYOESPROC glIsVertexArrayOES; #endif -#ifndef IOS +#if !PPSSPP_PLATFORM(IOS) #include "EGL/egl.h" #endif #endif @@ -181,7 +181,7 @@ void CheckGLExtensions() { // Just for reference: Galaxy Y has renderer == "VideoCore IV HW" } else if (vendor == "Vivante Corporation") { gl_extensions.gpuVendor = GPU_VENDOR_VIVANTE; - } else if (vendor == "Apple Inc.") { + } else if (vendor == "Apple Inc." || vendor == "Apple") { gl_extensions.gpuVendor = GPU_VENDOR_APPLE; } else { WARN_LOG(G3D, "Unknown GL vendor: '%s'", vendor.c_str()); diff --git a/Common/GPU/OpenGL/GLQueueRunner.cpp b/Common/GPU/OpenGL/GLQueueRunner.cpp index 44e0a2da27..487a335dd0 100644 --- a/Common/GPU/OpenGL/GLQueueRunner.cpp +++ b/Common/GPU/OpenGL/GLQueueRunner.cpp @@ -1,3 +1,4 @@ +#include "ppsspp_config.h" #include #include "Common/GPU/OpenGL/GLCommon.h" @@ -18,7 +19,7 @@ #define TEXCACHE_NAME_CACHE_SIZE 16 -#ifdef IOS +#if PPSSPP_PLATFORM(IOS) extern void bindDefaultFBO(); #endif @@ -201,7 +202,7 @@ void GLQueueRunner::RunInitSteps(const std::vector &steps, bool ski } else if (gl_extensions.VersionGEThan(3, 3, 0)) { glBindFragDataLocation(program->program, 0, "fragColor0"); } -#elif !defined(IOS) +#elif !PPSSPP_PLATFORM(IOS) if (gl_extensions.GLES3 && step.create_program.support_dual_source) { glBindFragDataLocationIndexedEXT(program->program, 0, 0, "fragColor0"); glBindFragDataLocationIndexedEXT(program->program, 0, 1, "fragColor1"); @@ -1368,13 +1369,15 @@ void GLQueueRunner::PerformCopy(const GLRStep &step) { _dbg_assert_(dstTex); #if defined(USING_GLES2) -#ifndef IOS +#if !PPSSPP_PLATFORM(IOS) + _assert_msg_(gl_extensions.OES_copy_image || gl_extensions.NV_copy_image || gl_extensions.EXT_copy_image, "Image copy extension expected"); glCopyImageSubDataOES( srcTex, target, srcLevel, srcRect.x, srcRect.y, srcZ, dstTex, target, dstLevel, dstPos.x, dstPos.y, dstZ, srcRect.w, srcRect.h, depth); #endif #else + _assert_msg_(gl_extensions.ARB_copy_image || gl_extensions.NV_copy_image, "Image copy extension expected"); if (gl_extensions.ARB_copy_image) { glCopyImageSubData( srcTex, target, srcLevel, srcRect.x, srcRect.y, srcZ, @@ -1680,7 +1683,7 @@ void GLQueueRunner::fbo_unbind() { glBindFramebuffer(GL_FRAMEBUFFER, g_defaultFBO); #endif -#ifdef IOS +#if PPSSPP_PLATFORM(IOS) bindDefaultFBO(); #endif diff --git a/Common/GPU/OpenGL/GLRenderManager.cpp b/Common/GPU/OpenGL/GLRenderManager.cpp index 97f5bcb0a6..6229aa676a 100644 --- a/Common/GPU/OpenGL/GLRenderManager.cpp +++ b/Common/GPU/OpenGL/GLRenderManager.cpp @@ -1,3 +1,4 @@ +#include "ppsspp_config.h" #include "GLRenderManager.h" #include "Common/GPU/OpenGL/GLFeatures.h" #include "Common/GPU/thin3d.h" @@ -672,7 +673,7 @@ void GLRenderManager::WaitUntilQueueIdle() { } } -GLPushBuffer::GLPushBuffer(GLRenderManager *render, GLuint target, size_t size) : render_(render), target_(target), size_(size) { +GLPushBuffer::GLPushBuffer(GLRenderManager *render, GLuint target, size_t size) : render_(render), size_(size), target_(target) { bool res = AddBuffer(); _assert_(res); } @@ -889,7 +890,7 @@ void *GLRBuffer::Map(GLBufferStrategy strategy) { glBindBuffer(target_, buffer_); if (gl_extensions.ARB_buffer_storage || gl_extensions.EXT_buffer_storage) { -#ifndef IOS +#if !PPSSPP_PLATFORM(IOS) if (!hasStorage_) { GLbitfield storageFlags = access & ~(GL_MAP_INVALIDATE_BUFFER_BIT | GL_MAP_FLUSH_EXPLICIT_BIT); #ifdef USING_GLES2 diff --git a/Common/GPU/OpenGL/GLRenderManager.h b/Common/GPU/OpenGL/GLRenderManager.h index fc774aa1c9..69cd0e9d37 100644 --- a/Common/GPU/OpenGL/GLRenderManager.h +++ b/Common/GPU/OpenGL/GLRenderManager.h @@ -47,8 +47,8 @@ public: class GLRFramebuffer { public: GLRFramebuffer(int _width, int _height, bool z_stencil) - : width(_width), height(_height), z_stencil_(z_stencil), - color_texture(_width, _height, 1), z_stencil_texture(_width, _height, 1) { + : color_texture(_width, _height, 1), z_stencil_texture(_width, _height, 1), + width(_width), height(_height), z_stencil_(z_stencil) { } ~GLRFramebuffer(); diff --git a/Common/GPU/OpenGL/gl3stub.c b/Common/GPU/OpenGL/gl3stub.c index 28bbcd774a..1caf54b685 100644 --- a/Common/GPU/OpenGL/gl3stub.c +++ b/Common/GPU/OpenGL/gl3stub.c @@ -382,7 +382,7 @@ GLboolean gl3stubInit() { return GL_TRUE; } -#endif // IOS +#endif // PPSPP_PLATFORM(IOS) #endif // GLES2 diff --git a/Common/GPU/OpenGL/gl3stub.h b/Common/GPU/OpenGL/gl3stub.h index 26b571b982..ba99aec6b2 100644 --- a/Common/GPU/OpenGL/gl3stub.h +++ b/Common/GPU/OpenGL/gl3stub.h @@ -514,7 +514,7 @@ extern GL_APICALL void (* GL_APIENTRY glBufferStorageEXT) (GLenum targ /* OES_copy_image, etc. */ extern GL_APICALL void (* GL_APIENTRY glCopyImageSubDataOES) (GLuint srcName, GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srcY, GLint srcZ, GLuint dstName, GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ, GLsizei width, GLsizei height, GLsizei depth); -#endif // IOS +#endif // PPSSPP_PLATFORM(IOS) #ifdef __cplusplus } diff --git a/Common/GPU/OpenGL/thin3d_gl.cpp b/Common/GPU/OpenGL/thin3d_gl.cpp index fcce3aabbd..29e8283d53 100644 --- a/Common/GPU/OpenGL/thin3d_gl.cpp +++ b/Common/GPU/OpenGL/thin3d_gl.cpp @@ -61,7 +61,7 @@ static const unsigned short blendFactorToGL[] = { GL_ONE_MINUS_SRC1_COLOR, GL_SRC1_ALPHA, GL_ONE_MINUS_SRC1_ALPHA, -#elif !defined(IOS) +#elif !PPSSPP_PLATFORM(IOS) GL_SRC1_COLOR_EXT, GL_ONE_MINUS_SRC1_COLOR_EXT, GL_SRC1_ALPHA_EXT, @@ -281,10 +281,6 @@ public: iter->Release(); } if (program_) render_->DeleteProgram(program_); - if (depthStencil) depthStencil->Release(); - if (blend) blend->Release(); - if (raster) raster->Release(); - if (inputLayout) inputLayout->Release(); } bool LinkShaders(); @@ -295,14 +291,14 @@ public: GLuint prim = 0; std::vector shaders; - OpenGLInputLayout *inputLayout = nullptr; - OpenGLDepthStencilState *depthStencil = nullptr; - OpenGLBlendState *blend = nullptr; - OpenGLRasterState *raster = nullptr; + AutoRef inputLayout; + AutoRef depthStencil; + AutoRef blend; + AutoRef raster; // TODO: Optimize by getting the locations first and putting in a custom struct UniformBufferDesc dynamicUniforms; - GLint samplerLocs_[8]; + GLint samplerLocs_[8]{}; std::vector dynamicUniformLocs_; GLRProgram *program_ = nullptr; @@ -406,7 +402,7 @@ public: void BindPipeline(Pipeline *pipeline) override; void BindVertexBuffers(int start, int count, Buffer **buffers, int *offsets) override { for (int i = 0; i < count; i++) { - curVBuffers_[i + start] = (OpenGLBuffer *)buffers[i]; + curVBuffers_[i + start] = (OpenGLBuffer *)buffers[i]; curVBufferOffsets_[i + start] = offsets ? offsets[i] : 0; } } @@ -482,17 +478,17 @@ private: DeviceCaps caps_{}; // Bound state - OpenGLSamplerState *boundSamplers_[MAX_TEXTURE_SLOTS]{}; + AutoRef boundSamplers_[MAX_TEXTURE_SLOTS]; // Point to GLRTexture directly because they can point to the textures // in framebuffers too (which also can be bound). const GLRTexture *boundTextures_[MAX_TEXTURE_SLOTS]{}; - OpenGLPipeline *curPipeline_ = nullptr; - OpenGLBuffer *curVBuffers_[4]{}; + AutoRef curPipeline_; + AutoRef curVBuffers_[4]{}; int curVBufferOffsets_[4]{}; - OpenGLBuffer *curIBuffer_ = nullptr; + AutoRef curIBuffer_; int curIBufferOffset_ = 0; - Framebuffer *curRenderTarget_ = nullptr; + AutoRef curRenderTarget_; uint8_t stencilRef_ = 0; @@ -1038,8 +1034,8 @@ Pipeline *OpenGLContext::CreateGraphicsPipeline(const PipelineDesc &desc) { ERROR_LOG(G3D, "Pipeline requires at least one shader"); return nullptr; } - if ((int)desc.prim >= (int)Primitive::PRIMITIVE_TYPE_COUNT) { - ERROR_LOG(G3D, "Invalid primitive type"); + if ((uint32_t)desc.prim >= (uint32_t)Primitive::PRIMITIVE_TYPE_COUNT) { + ERROR_LOG(G3D, "Invalid primitive type"); return nullptr; } if (!desc.depthStencil || !desc.blend || !desc.raster) { @@ -1069,12 +1065,6 @@ Pipeline *OpenGLContext::CreateGraphicsPipeline(const PipelineDesc &desc) { pipeline->blend = (OpenGLBlendState *)desc.blend; pipeline->raster = (OpenGLRasterState *)desc.raster; pipeline->inputLayout = (OpenGLInputLayout *)desc.inputLayout; - pipeline->depthStencil->AddRef(); - pipeline->blend->AddRef(); - pipeline->raster->AddRef(); - if (pipeline->inputLayout) { - pipeline->inputLayout->AddRef(); - } return pipeline; } else { ERROR_LOG(G3D, "Failed to create pipeline - shaders failed to link"); @@ -1210,7 +1200,7 @@ void OpenGLContext::UpdateDynamicUniformBuffer(const void *ub, size_t size) { } void OpenGLContext::Draw(int vertexCount, int offset) { - _dbg_assert_msg_(curVBuffers_[0], "Can't call Draw without a vertex buffer"); + _dbg_assert_msg_(curVBuffers_[0] != nullptr, "Can't call Draw without a vertex buffer"); ApplySamplers(); if (curPipeline_->inputLayout) { renderManager_.BindVertexBuffer(curPipeline_->inputLayout->inputLayout_, curVBuffers_[0]->buffer_, curVBufferOffsets_[0]); @@ -1219,8 +1209,8 @@ void OpenGLContext::Draw(int vertexCount, int offset) { } void OpenGLContext::DrawIndexed(int vertexCount, int offset) { - _dbg_assert_msg_(curVBuffers_[0], "Can't call DrawIndexed without a vertex buffer"); - _dbg_assert_msg_(curIBuffer_, "Can't call DrawIndexed without an index buffer"); + _dbg_assert_msg_(curVBuffers_[0] != nullptr, "Can't call DrawIndexed without a vertex buffer"); + _dbg_assert_msg_(curIBuffer_ != nullptr, "Can't call DrawIndexed without an index buffer"); ApplySamplers(); if (curPipeline_->inputLayout) { renderManager_.BindVertexBuffer(curPipeline_->inputLayout->inputLayout_, curVBuffers_[0]->buffer_, curVBufferOffsets_[0]); @@ -1230,7 +1220,7 @@ void OpenGLContext::DrawIndexed(int vertexCount, int offset) { } void OpenGLContext::DrawUP(const void *vdata, int vertexCount) { - _assert_(curPipeline_->inputLayout); + _assert_(curPipeline_->inputLayout != nullptr); int stride = curPipeline_->inputLayout->stride; size_t dataSize = stride * vertexCount; diff --git a/Common/GPU/Vulkan/VulkanContext.cpp b/Common/GPU/Vulkan/VulkanContext.cpp index bf77c2720f..f0a28b484f 100644 --- a/Common/GPU/Vulkan/VulkanContext.cpp +++ b/Common/GPU/Vulkan/VulkanContext.cpp @@ -21,17 +21,8 @@ #undef new #endif -#ifdef _MSC_VER -#pragma warning(push) -#pragma warning(disable:4996) -#endif - #include "ext/glslang/SPIRV/GlslangToSpv.h" -#ifdef _MSC_VER -#pragma warning(pop) -#endif - #ifdef USE_CRT_DBG #define new DBG_NEW #endif @@ -1001,7 +992,6 @@ bool VulkanContext::InitSwapchain() { std::string currentTransform = surface_transforms_to_string(surfCapabilities_.currentTransform); g_display_rotation = DisplayRotation::ROTATE_0; g_display_rot_matrix.setIdentity(); - bool swapChainExtentSwap = false; if (surfCapabilities_.currentTransform & (VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR | VK_SURFACE_TRANSFORM_INHERIT_BIT_KHR)) { preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR; } else if (surfCapabilities_.currentTransform & (VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR | VK_SURFACE_TRANSFORM_ROTATE_180_BIT_KHR | VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR)) { @@ -1429,7 +1419,6 @@ bool IsHashMaliDriverVersion(const VkPhysicalDeviceProperties &props) { // ARM used to put a hash in place of the driver version. // Now they only use major versions. We'll just make a bad heuristic. uint32_t major = VK_VERSION_MAJOR(props.driverVersion); - uint32_t minor = VK_VERSION_MINOR(props.driverVersion); uint32_t branch = VK_VERSION_PATCH(props.driverVersion); if (branch > 0) return true; diff --git a/Common/GPU/Vulkan/VulkanDebug.cpp b/Common/GPU/Vulkan/VulkanDebug.cpp index f165d73609..d4052e970a 100644 --- a/Common/GPU/Vulkan/VulkanDebug.cpp +++ b/Common/GPU/Vulkan/VulkanDebug.cpp @@ -37,8 +37,12 @@ VKAPI_ATTR VkBool32 VKAPI_CALL VulkanDebugUtilsCallback( // UNASSIGNED-CoreValidation-Shader-OutputNotConsumed - benign perf warning return false; } + if (messageCode == 1303270965) { + // Benign perf warning, image blit using GENERAL layout. + // UNASSIGNED + return false; + } - const char *pLayerPrefix = ""; if (messageSeverity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT) { message << "ERROR("; } else if (messageSeverity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT) { diff --git a/Common/GPU/Vulkan/VulkanImage.cpp b/Common/GPU/Vulkan/VulkanImage.cpp index 5301a47803..564729a99a 100644 --- a/Common/GPU/Vulkan/VulkanImage.cpp +++ b/Common/GPU/Vulkan/VulkanImage.cpp @@ -31,6 +31,11 @@ static bool IsDepthStencilFormat(VkFormat format) { } bool VulkanTexture::CreateDirect(VkCommandBuffer cmd, VulkanDeviceAllocator *allocator, int w, int h, int numMips, VkFormat format, VkImageLayout initialLayout, VkImageUsageFlags usage, const VkComponentMapping *mapping) { + if (w == 0 || h == 0 || numMips == 0) { + ERROR_LOG(G3D, "Can't create a zero-size VulkanTexture"); + return false; + } + Wipe(); width_ = w; @@ -196,7 +201,8 @@ void VulkanTexture::ClearMip(VkCommandBuffer cmd, int mip, uint32_t value) { vkCmdClearColorImage(cmd, image_, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &clearVal, 1, &range); } -void VulkanTexture::GenerateMip(VkCommandBuffer cmd, int mip) { +// Low-quality mipmap generation by bilinear blit, but works okay. +void VulkanTexture::GenerateMip(VkCommandBuffer cmd, int mip, VkImageLayout imageLayout) { _assert_msg_(mip != 0, "Cannot generate the first level"); _assert_msg_(mip < numMips_, "Cannot generate mipmaps past the maximum created (%d vs %d)", mip, numMips_); VkImageBlit blit{}; @@ -214,16 +220,20 @@ void VulkanTexture::GenerateMip(VkCommandBuffer cmd, int mip) { blit.dstOffsets[1].y = height_ >> mip; blit.dstOffsets[1].z = 1; + // TODO: We could do better with the image transitions - would be enough with one per level + // for the memory barrier, then one final one for the whole stack when done. This function + // currently doesn't have a global enough view, though. + // We should also coalesce barriers across multiple texture uploads in a frame and all kinds of other stuff, but... + TransitionImageLayout2(cmd, image_, mip - 1, 1, VK_IMAGE_ASPECT_COLOR_BIT, - VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + imageLayout, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT); - // Low-quality mipmap generation, but works okay. - vkCmdBlitImage(cmd, image_, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, image_, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &blit, VK_FILTER_LINEAR); + vkCmdBlitImage(cmd, image_, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, image_, imageLayout, 1, &blit, VK_FILTER_LINEAR); TransitionImageLayout2(cmd, image_, mip - 1, 1, VK_IMAGE_ASPECT_COLOR_BIT, - VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, imageLayout, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_ACCESS_TRANSFER_READ_BIT, VK_ACCESS_TRANSFER_WRITE_BIT); } diff --git a/Common/GPU/Vulkan/VulkanImage.h b/Common/GPU/Vulkan/VulkanImage.h index 0745e0e352..be0367aee0 100644 --- a/Common/GPU/Vulkan/VulkanImage.h +++ b/Common/GPU/Vulkan/VulkanImage.h @@ -21,7 +21,7 @@ public: bool CreateDirect(VkCommandBuffer cmd, VulkanDeviceAllocator *allocator, int w, int h, int numMips, VkFormat format, VkImageLayout initialLayout, VkImageUsageFlags usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, const VkComponentMapping *mapping = nullptr); void ClearMip(VkCommandBuffer cmd, int mip, uint32_t value); void UploadMip(VkCommandBuffer cmd, int mip, int mipWidth, int mipHeight, VkBuffer buffer, uint32_t offset, size_t rowLength); // rowLength is in pixels - void GenerateMip(VkCommandBuffer cmd, int mip); + void GenerateMip(VkCommandBuffer cmd, int mip, VkImageLayout imageLayout); void EndCreate(VkCommandBuffer cmd, bool vertexTexture = false, VkImageLayout layout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); // When loading mips from compute shaders, you need to pass VK_IMAGE_LAYOUT_GENERAL to the above function. diff --git a/Common/GPU/Vulkan/VulkanLoader.cpp b/Common/GPU/Vulkan/VulkanLoader.cpp index ad44606272..eede42ce48 100644 --- a/Common/GPU/Vulkan/VulkanLoader.cpp +++ b/Common/GPU/Vulkan/VulkanLoader.cpp @@ -15,11 +15,12 @@ // Official git repository and contact information can be found at // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. -#include "Common/GPU/Vulkan/VulkanLoader.h" +#include "ppsspp_config.h" #include #include #include +#include "Common/GPU/Vulkan/VulkanLoader.h" #include "Common/Log.h" #include "Common/System/System.h" @@ -235,10 +236,11 @@ static const char *device_name_blacklist[] = { "SDL:Horizon", }; +#ifndef _WIN32 static const char *so_names[] = { -#ifdef IOS +#if PPSSPP_PLATFORM(IOS) "@executable_path/Frameworks/libMoltenVK.dylib", -#elif defined(PPSSPP_PLATFORM_MAC) +#elif PPSSPP_PLATFORM(MAC) "@executable_path/../Frameworks/libMoltenVK.dylib", #else "libvulkan.so", @@ -247,6 +249,7 @@ static const char *so_names[] = { #endif #endif }; +#endif void VulkanSetAvailable(bool available) { g_vulkanAvailabilityChecked = true; diff --git a/Common/GPU/Vulkan/VulkanMemory.cpp b/Common/GPU/Vulkan/VulkanMemory.cpp index ce002733b7..77f41732cc 100644 --- a/Common/GPU/Vulkan/VulkanMemory.cpp +++ b/Common/GPU/Vulkan/VulkanMemory.cpp @@ -246,7 +246,6 @@ size_t VulkanDeviceAllocator::Allocate(const VkMemoryRequirements &reqs, VkDevic bool VulkanDeviceAllocator::AllocateFromSlab(Slab &slab, size_t &start, size_t blocks, const char *tag) { _assert_(!destroyed_); - bool matched = true; if (start + blocks > slab.usage.size()) { start = slab.usage.size(); diff --git a/Common/GPU/Vulkan/VulkanRenderManager.cpp b/Common/GPU/Vulkan/VulkanRenderManager.cpp index 3b68fcac48..fce371640f 100644 --- a/Common/GPU/Vulkan/VulkanRenderManager.cpp +++ b/Common/GPU/Vulkan/VulkanRenderManager.cpp @@ -207,9 +207,10 @@ VulkanRenderManager::VulkanRenderManager(VulkanContext *vulkan) : vulkan_(vulkan queueRunner_.CreateDeviceObjects(); - // Temporary AMD hack for issue #10097 - if (vulkan_->GetPhysicalDeviceProperties().properties.vendorID == VULKAN_VENDOR_AMD) { - useThread_ = false; + // AMD hack for issue #10097 (older drivers only.) + const auto &props = vulkan_->GetPhysicalDeviceProperties().properties; + if (props.vendorID == VULKAN_VENDOR_AMD && props.apiVersion < VK_API_VERSION_1_1) { + useThread_ = false; } } diff --git a/Common/GPU/Vulkan/thin3d_vulkan.cpp b/Common/GPU/Vulkan/thin3d_vulkan.cpp index ba3bc05897..054096f377 100644 --- a/Common/GPU/Vulkan/thin3d_vulkan.cpp +++ b/Common/GPU/Vulkan/thin3d_vulkan.cpp @@ -215,7 +215,7 @@ bool VKShaderModule::Compile(VulkanContext *vulkan, ShaderLanguage language, con std::vector spirv; std::string errorMessage; if (!GLSLtoSPV(vkstage_, source_.c_str(), GLSLVariant::VULKAN, spirv, &errorMessage)) { - INFO_LOG(G3D, "Shader compile to module failed: %s", errorMessage.c_str()); + WARN_LOG(G3D, "Shader compile to module failed: %s", errorMessage.c_str()); return false; } @@ -231,6 +231,7 @@ bool VKShaderModule::Compile(VulkanContext *vulkan, ShaderLanguage language, con if (vulkan->CreateShaderModule(spirv, &module_)) { ok_ = true; } else { + WARN_LOG(G3D, "vkCreateShaderModule failed"); ok_ = false; } return ok_; @@ -245,7 +246,7 @@ public: class VKPipeline : public Pipeline { public: - VKPipeline(VulkanContext *vulkan, size_t size, PipelineFlags _flags) : vulkan_(vulkan), flags(_flags) { + VKPipeline(VulkanContext *vulkan, size_t size, PipelineFlags _flags) : flags(_flags), vulkan_(vulkan) { uboSize_ = (int)size; ubo_ = new uint8_t[uboSize_]; } @@ -510,16 +511,16 @@ private: VulkanTexture *nullTexture_ = nullptr; - VKPipeline *curPipeline_ = nullptr; - VKBuffer *curVBuffers_[4]{}; + AutoRef curPipeline_; + AutoRef curVBuffers_[4]; int curVBufferOffsets_[4]{}; - VKBuffer *curIBuffer_ = nullptr; + AutoRef curIBuffer_; int curIBufferOffset_ = 0; VkDescriptorSetLayout descriptorSetLayout_ = VK_NULL_HANDLE; VkPipelineLayout pipelineLayout_ = VK_NULL_HANDLE; VkPipelineCache pipelineCache_ = VK_NULL_HANDLE; - Framebuffer *curFramebuffer_ = nullptr; + AutoRef curFramebuffer_; VkDevice device_; VkQueue queue_; @@ -528,8 +529,8 @@ private: enum { MAX_FRAME_COMMAND_BUFFERS = 256, }; - VKTexture *boundTextures_[MAX_BOUND_TEXTURES]{}; - VKSamplerState *boundSamplers_[MAX_BOUND_TEXTURES]{}; + AutoRef boundTextures_[MAX_BOUND_TEXTURES]; + AutoRef boundSamplers_[MAX_BOUND_TEXTURES]; VkImageView boundImageView_[MAX_BOUND_TEXTURES]{}; struct FrameData { @@ -754,7 +755,7 @@ bool VKTexture::Create(VkCommandBuffer cmd, VulkanPushBuffer *push, const Textur } // Generate the rest of the mips automatically. for (; i < mipLevels_; i++) { - vkTex_->GenerateMip(cmd, i); + vkTex_->GenerateMip(cmd, i, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); } } vkTex_->EndCreate(cmd, false); @@ -762,7 +763,7 @@ bool VKTexture::Create(VkCommandBuffer cmd, VulkanPushBuffer *push, const Textur } VKContext::VKContext(VulkanContext *vulkan, bool splitSubmit) - : vulkan_(vulkan), caps_{}, renderManager_(vulkan) { + : vulkan_(vulkan), renderManager_(vulkan) { shaderLanguageDesc_.Init(GLSL_VULKAN); caps_.anisoSupported = vulkan->GetDeviceFeatures().enabled.samplerAnisotropy != 0; @@ -1281,7 +1282,7 @@ ShaderModule *VKContext::CreateShaderModule(ShaderStage stage, ShaderLanguage la if (shader->Compile(vulkan_, language, data, size)) { return shader; } else { - ERROR_LOG(G3D, "Failed to compile shader: %s", (const char *)data); + ERROR_LOG(G3D, "Failed to compile shader:\n%s", (const char *)data); shader->Release(); return nullptr; } diff --git a/Common/GPU/thin3d.h b/Common/GPU/thin3d.h index 40442c8f48..9ee285c804 100644 --- a/Common/GPU/thin3d.h +++ b/Common/GPU/thin3d.h @@ -335,6 +335,46 @@ private: int refcount_; }; +template +struct AutoRef { + AutoRef() { + } + explicit AutoRef(T *p) { + ptr = p; + if (ptr) + ptr->AddRef(); + } + AutoRef(const AutoRef &p) { + *this = p.ptr; + } + ~AutoRef() { + if (ptr) + ptr->Release(); + } + + T *operator =(T *p) { + if (ptr) + ptr->Release(); + ptr = p; + if (ptr) + ptr->AddRef(); + return ptr; + } + AutoRef &operator =(const AutoRef &p) { + *this = p.ptr; + return *this; + } + + T *operator->() const { + return ptr; + } + operator T *() { + return ptr; + } + + T *ptr = nullptr; +}; + class BlendState : public RefCountedObject { public: }; diff --git a/Common/GPU/thin3d_create.h b/Common/GPU/thin3d_create.h index a2030792af..702e7f45c2 100644 --- a/Common/GPU/thin3d_create.h +++ b/Common/GPU/thin3d_create.h @@ -7,12 +7,8 @@ // Separated this stuff into its own file so we don't get Windows.h included if all we want is the thin3d declarations. #ifdef _WIN32 -#ifndef NOMINMAX -#define NOMINMAX -#endif -#define WIN32_LEAN_AND_MEAN -#include -#include +#include "Common/CommonWindows.h" +#include struct IDirect3DDevice9; struct IDirect3D9; struct IDirect3DDevice9Ex; diff --git a/Common/Math/fast/fast_math.c b/Common/Math/fast/fast_math.c index 9cc9e0728b..707069dd54 100644 --- a/Common/Math/fast/fast_math.c +++ b/Common/Math/fast/fast_math.c @@ -1,3 +1,4 @@ +#include "ppsspp_config.h" #include "fast_math.h" #include "fast_matrix.h" diff --git a/Common/Math/geom2d.h b/Common/Math/geom2d.h index 1d375fc1d6..1d0afa1c2b 100644 --- a/Common/Math/geom2d.h +++ b/Common/Math/geom2d.h @@ -66,6 +66,9 @@ struct Bounds { Bounds Expand(float amount) const { return Bounds(x - amount, y - amount, w + amount * 2, h + amount * 2); } + Bounds Expand(float xAmount, float yAmount) const { + return Bounds(x - xAmount, y - yAmount, w + xAmount * 2, h + yAmount * 2); + } Bounds Offset(float xAmount, float yAmount) const { return Bounds(x + xAmount, y + yAmount, w, h); } diff --git a/Common/MemArenaDarwin.cpp b/Common/MemArenaDarwin.cpp index d35fb7f84d..acac2957a1 100644 --- a/Common/MemArenaDarwin.cpp +++ b/Common/MemArenaDarwin.cpp @@ -84,7 +84,7 @@ void MemArena::ReleaseView(void* view, size_t size) { } bool MemArena::NeedsProbing() { -#if defined(IOS) && PPSSPP_ARCH(64BIT) +#if PPSSPP_PLATFORM(IOS) && PPSSPP_ARCH(64BIT) return true; #else return false; @@ -92,7 +92,7 @@ bool MemArena::NeedsProbing() { } u8* MemArena::Find4GBBase() { -#if defined(IOS) && PPSSPP_ARCH(64BIT) +#if PPSSPP_PLATFORM(IOS) && PPSSPP_ARCH(64BIT) // The caller will need to do probing, like on 32-bit Windows. return nullptr; #else diff --git a/Common/MemArenaPosix.cpp b/Common/MemArenaPosix.cpp index 9a6166b379..7df84ab35c 100644 --- a/Common/MemArenaPosix.cpp +++ b/Common/MemArenaPosix.cpp @@ -107,7 +107,7 @@ void MemArena::ReleaseView(void* view, size_t size) { u8* MemArena::Find4GBBase() { // Now, create views in high memory where there's plenty of space. -#if PPSSPP_ARCH(64BIT) && !defined(USE_ADDRESS_SANITIZER) +#if PPSSPP_ARCH(64BIT) && !defined(USE_ASAN) // We should probably just go look in /proc/self/maps for some free space. // But let's try the anonymous mmap trick, just like on 32-bit, but bigger and // aligned to 4GB for the movk trick. We can ensure that we get an aligned 4GB diff --git a/Common/MemoryUtil.cpp b/Common/MemoryUtil.cpp index d99bebe956..536858f6c6 100644 --- a/Common/MemoryUtil.cpp +++ b/Common/MemoryUtil.cpp @@ -87,7 +87,7 @@ static uint32_t ConvertProtFlagsUnix(uint32_t flags) { #endif -#if defined(_WIN32) && defined(_M_X64) +#if defined(_WIN32) && PPSSPP_ARCH(AMD64) static uintptr_t last_executable_addr; static void *SearchForFreeMem(size_t size) { if (!last_executable_addr) @@ -128,7 +128,7 @@ void *AllocateExecutableMemory(size_t size) { prot = PAGE_READWRITE; if (sys_info.dwPageSize == 0) GetSystemInfo(&sys_info); -#if defined(_M_X64) +#if PPSSPP_ARCH(AMD64) if ((uintptr_t)&hint_location > 0xFFFFFFFFULL) { size_t aligned_size = ppsspp_round_page(size); #if 1 // Turn off to hunt for RIP bugs on x86-64. @@ -159,7 +159,7 @@ void *AllocateExecutableMemory(size_t size) { } #else static char *map_hint = 0; -#if defined(_M_X64) +#if PPSSPP_ARCH(AMD64) // Try to request one that is close to our memory location if we're in high memory. // We use a dummy global variable to give us a good location to start from. if (!map_hint) { @@ -190,7 +190,7 @@ void *AllocateExecutableMemory(size_t size) { ERROR_LOG(MEMMAP, "Failed to allocate executable memory (%d) errno=%d", (int)size, errno); } -#if defined(_M_X64) && !defined(_WIN32) +#if PPSSPP_ARCH(AMD64) && !defined(_WIN32) else if ((uintptr_t)map_hint <= 0xFFFFFFFF) { // Round up if we're below 32-bit mark, probably allocating sequentially. map_hint += ppsspp_round_page(size); @@ -279,7 +279,7 @@ void FreeAlignedMemory(void* ptr) { bool PlatformIsWXExclusive() { // Needed on platforms that disable W^X pages for security. Even without block linking, still should be much faster than IR JIT. // This might also come in useful for UWP (Universal Windows Platform) if I'm understanding things correctly. -#if defined(IOS) || PPSSPP_PLATFORM(UWP) || defined(__OpenBSD__) +#if PPSSPP_PLATFORM(IOS) || PPSSPP_PLATFORM(UWP) || defined(__OpenBSD__) return true; #elif PPSSPP_PLATFORM(MAC) && PPSSPP_ARCH(ARM64) return true; diff --git a/Common/MipsCPUDetect.cpp b/Common/MipsCPUDetect.cpp index eb35259d3a..9f2c1a1835 100644 --- a/Common/MipsCPUDetect.cpp +++ b/Common/MipsCPUDetect.cpp @@ -15,7 +15,8 @@ // Official SVN repository and contact information can be found at // http://code.google.com/p/dolphin-emu/ -#ifdef __mips__ +#include "ppsspp_config.h" +#if PPSSPP_ARCH(MIPS) || PPSSPP_ARCH(MIPS64) #include "Common/Common.h" #include "Common/CPUDetect.h" @@ -202,4 +203,4 @@ std::string CPUInfo::Summarize() return sum; } -#endif // __mips__ +#endif // PPSSPP_ARCH(MIPS) || PPSSPP_ARCH(MIPS64) diff --git a/Common/MipsEmitter.cpp b/Common/MipsEmitter.cpp index 5d6288be3e..54c22bbebf 100644 --- a/Common/MipsEmitter.cpp +++ b/Common/MipsEmitter.cpp @@ -15,6 +15,7 @@ // Official git repository and contact information can be found at // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. +#include "ppsspp_config.h" #include #include #include @@ -27,9 +28,13 @@ #include "Common/CPUDetect.h" namespace MIPSGen { -void MIPSEmitter::SetCodePtr(u8 *ptr) { - code_ = ptr; - lastCacheFlushEnd_ = ptr; +void MIPSEmitter::SetCodePointer(const u8 *ptr, u8 *writePtr) { + code_ = writePtr; + lastCacheFlushEnd_ = writePtr; +} + +const u8 *MIPSEmitter::GetCodePointer() const { + return code_; } void MIPSEmitter::ReserveCodeSpace(u32 bytes) { @@ -63,7 +68,7 @@ void MIPSEmitter::FlushIcache() { } void MIPSEmitter::FlushIcacheSection(u8 *start, u8 *end) { -#if defined(MIPS) +#if PPSSPP_ARCH(MIPS) #ifdef __clang__ __clear_cache(start, end); #else @@ -466,27 +471,14 @@ void MIPSEmitter::MOVI2R(MIPSReg reg, u32 imm) { } } -void MIPSCodeBlock::AllocCodeSpace(int size) { - region_size = size; - region = (u8 *)AllocateExecutableMemory(region_size); - SetCodePtr(region); -} - -// Always clear code space with breakpoints, so that if someone accidentally executes -// uninitialized, it just breaks into the debugger. -void MIPSCodeBlock::ClearCodeSpace() { - // Set BREAK instructions on all of it. - u32 *region32 = (u32 *)region; - for (u32 i = 0; i < region_size / 4; ++i) { - *region32++ = 0x0000000d; - } - ResetCodePtr(); -} - -void MIPSCodeBlock::FreeCodeSpace() { - FreeMemoryPages(region, region_size); - region = NULL; - region_size = 0; +void MIPSCodeBlock::PoisonMemory(int offset) { + u32 *ptr = (u32 *)(region + offset); + u32 *maxptr = (u32 *)(region + region_size - offset); + // If our memory isn't a multiple of u32 then this won't write the last remaining bytes with anything + // Less than optimal, but there would be nothing we could do but throw a runtime warning anyway. + // AArch64: 0x0000000d = break 0 + while (ptr < maxptr) + *ptr++ = 0x0000000d; } } diff --git a/Common/MipsEmitter.h b/Common/MipsEmitter.h index 19913ec2e2..6f57324297 100644 --- a/Common/MipsEmitter.h +++ b/Common/MipsEmitter.h @@ -21,7 +21,8 @@ #include #include -#include "Common.h" +#include "Common/CodeBlock.h" +#include "Common/Common.h" namespace MIPSGen { @@ -72,12 +73,14 @@ public: MIPSEmitter() : code_(0), lastCacheFlushEnd_(0) { } MIPSEmitter(u8 *code_ptr) : code_(code_ptr), lastCacheFlushEnd_(code_ptr) { - SetCodePtr(code_ptr); + SetCodePointer(code_ptr, code_ptr); } virtual ~MIPSEmitter() { } - void SetCodePtr(u8 *ptr); + void SetCodePointer(const u8 *ptr, u8 *writePtr); + const u8* GetCodePointer() const; + void ReserveCodeSpace(u32 bytes); const u8 *AlignCode16(); const u8 *AlignCodePage(); @@ -270,45 +273,9 @@ private: // Everything that needs to generate machine code should inherit from this. // You get memory management for free, plus, you can use all the LUI etc functions without // having to prefix them with gen-> or something similar. -class MIPSCodeBlock : public MIPSEmitter { +class MIPSCodeBlock : public CodeBlock { public: - MIPSCodeBlock() : region(nullptr), region_size(0) { - } - virtual ~MIPSCodeBlock() { - if (region) { - FreeCodeSpace(); - } - } - - // Call this before you generate any code. - void AllocCodeSpace(int size); - - // Always clear code space with breakpoints, so that if someone accidentally executes - // uninitialized, it just breaks into the debugger. - void ClearCodeSpace(); - - // Call this when shutting down. Don't rely on the destructor, even though it'll do the job. - void FreeCodeSpace(); - - bool IsInSpace(const u8 *ptr) const { - return ptr >= region && ptr < region + region_size; - } - - void ResetCodePtr() { - SetCodePtr(region); - } - - size_t GetSpaceLeft() const { - return region_size - (GetCodePtr() - region); - } - - u8 *GetBasePtr() { - return region; - } - - size_t GetOffset(const u8 *ptr) const { - return ptr - region; - } + void PoisonMemory(int offset) override; protected: u8 *region; diff --git a/Common/Render/DrawBuffer.cpp b/Common/Render/DrawBuffer.cpp index 94be1bc385..b29513d725 100644 --- a/Common/Render/DrawBuffer.cpp +++ b/Common/Render/DrawBuffer.cpp @@ -224,6 +224,20 @@ void DrawBuffer::DrawImage(ImageID atlas_image, float x, float y, float scale, C DrawImageStretch(atlas_image, x, y, x + w, y + h, color); } +void DrawBuffer::DrawImageCenterTexel(ImageID atlas_image, float x1, float y1, float x2, float y2, Color color) { + const AtlasImage *image = atlas->getImage(atlas_image); + if (!image) + return; + float centerU = (image->u1 + image->u2) * 0.5f; + float centerV = (image->v1 + image->v2) * 0.5f; + V(x1, y1, color, centerU, centerV); + V(x2, y1, color, centerU, centerV); + V(x2, y2, color, centerU, centerV); + V(x1, y1, color, centerU, centerV); + V(x2, y2, color, centerU, centerV); + V(x1, y2, color, centerU, centerV); +} + void DrawBuffer::DrawImageStretch(ImageID atlas_image, float x1, float y1, float x2, float y2, Color color) { const AtlasImage *image = atlas->getImage(atlas_image); if (!image) diff --git a/Common/Render/DrawBuffer.h b/Common/Render/DrawBuffer.h index 6c3f056f5d..f88308cf97 100644 --- a/Common/Render/DrawBuffer.h +++ b/Common/Render/DrawBuffer.h @@ -107,6 +107,9 @@ public: const Atlas *GetAtlas() const { return atlas; } bool MeasureImage(ImageID atlas_image, float *w, float *h); void DrawImage(ImageID atlas_image, float x, float y, float scale, Color color = COLOR(0xFFFFFF), int align = ALIGN_TOPLEFT); + + // Good for stretching out a white image without edge artifacts that I'm getting on iOS. + void DrawImageCenterTexel(ImageID atlas_image, float x1, float y1, float x2, float y2, Color color = COLOR(0xFFFFFF)); void DrawImageStretch(ImageID atlas_image, float x1, float y1, float x2, float y2, Color color = COLOR(0xFFFFFF)); void DrawImageStretchVGradient(ImageID atlas_image, float x1, float y1, float x2, float y2, Color color1, Color color2); void DrawImageStretch(ImageID atlas_image, const Bounds &bounds, Color color = COLOR(0xFFFFFF)) { diff --git a/Common/Render/Text/draw_text_android.cpp b/Common/Render/Text/draw_text_android.cpp index 7e6a66d85f..16241818ad 100644 --- a/Common/Render/Text/draw_text_android.cpp +++ b/Common/Render/Text/draw_text_android.cpp @@ -1,3 +1,4 @@ +#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_uwp.cpp b/Common/Render/Text/draw_text_uwp.cpp index 7ec375ae54..6f5088619f 100644 --- a/Common/Render/Text/draw_text_uwp.cpp +++ b/Common/Render/Text/draw_text_uwp.cpp @@ -1,3 +1,4 @@ +#include "ppsspp_config.h" #include "Common/System/Display.h" #include "Common/GPU/thin3d.h" #include "Common/Data/Hash/Hash.h" diff --git a/Common/Render/Text/draw_text_win.cpp b/Common/Render/Text/draw_text_win.cpp index e7518857ef..1a79f6a192 100644 --- a/Common/Render/Text/draw_text_win.cpp +++ b/Common/Render/Text/draw_text_win.cpp @@ -1,3 +1,4 @@ +#include "ppsspp_config.h" #include "Common/System/Display.h" #include "Common/GPU/thin3d.h" #include "Common/Data/Hash/Hash.h" diff --git a/Common/Serialize/Serializer.cpp b/Common/Serialize/Serializer.cpp index 4738b829f3..4a6c9d94a9 100644 --- a/Common/Serialize/Serializer.cpp +++ b/Common/Serialize/Serializer.cpp @@ -111,14 +111,22 @@ void Do(PointerWrap &p, std::string &x) { } void Do(PointerWrap &p, std::wstring &x) { - int stringLen = sizeof(wchar_t)*((int)x.length() + 1); + int stringLen = sizeof(wchar_t) * ((int)x.length() + 1); Do(p, stringLen); + auto read = [&]() { + std::wstring r; + // In case unaligned, use memcpy. + r.resize((stringLen / sizeof(wchar_t)) - 1); + memcpy(&r[0], *p.ptr, stringLen - sizeof(wchar_t)); + return r; + }; + switch (p.mode) { - case PointerWrap::MODE_READ: x = (wchar_t*)*p.ptr; break; + case PointerWrap::MODE_READ: x = read(); break; case PointerWrap::MODE_WRITE: memcpy(*p.ptr, x.c_str(), stringLen); break; case PointerWrap::MODE_MEASURE: break; - case PointerWrap::MODE_VERIFY: _dbg_assert_msg_(x == (wchar_t*)*p.ptr, "Savestate verification failure: \"%ls\" != \"%ls\" (at %p).\n", x.c_str(), (wchar_t*)*p.ptr, p.ptr); break; + case PointerWrap::MODE_VERIFY: _dbg_assert_msg_(x == read(), "Savestate verification failure: \"%ls\" != \"%ls\" (at %p).\n", x.c_str(), read().c_str(), p.ptr); break; } (*p.ptr) += stringLen; } @@ -127,11 +135,19 @@ void Do(PointerWrap &p, std::u16string &x) { int stringLen = sizeof(char16_t) * ((int)x.length() + 1); Do(p, stringLen); + auto read = [&]() { + std::u16string r; + // In case unaligned, use memcpy. + r.resize((stringLen / sizeof(char16_t)) - 1); + memcpy(&r[0], *p.ptr, stringLen - sizeof(char16_t)); + return r; + }; + switch (p.mode) { - case PointerWrap::MODE_READ: x = (char16_t*)*p.ptr; break; + case PointerWrap::MODE_READ: x = read(); break; case PointerWrap::MODE_WRITE: memcpy(*p.ptr, x.c_str(), stringLen); break; case PointerWrap::MODE_MEASURE: break; - case PointerWrap::MODE_VERIFY: _dbg_assert_msg_(x == (char16_t*)*p.ptr, "Savestate verification failure: (at %p).\n", x.c_str()); break; + case PointerWrap::MODE_VERIFY: _dbg_assert_msg_(x == read(), "Savestate verification failure: (at %p).\n", p.ptr); break; } (*p.ptr) += stringLen; } @@ -174,7 +190,7 @@ void PointerWrap::DoMarker(const char *prevName, u32 arbitraryNumber) { u32 cookie = arbitraryNumber; Do(*this, cookie); if (mode == PointerWrap::MODE_READ && cookie != arbitraryNumber) { - _assert_msg_(false, "Error: After \"%s\", found %d (0x%X) instead of save marker %d (0x%X). Aborting savestate load...", prevName, cookie, cookie, arbitraryNumber, arbitraryNumber); + ERROR_LOG(SAVESTATE, "Error: After \"%s\", found %d (0x%X) instead of save marker %d (0x%X). Aborting savestate load...", prevName, cookie, cookie, arbitraryNumber, arbitraryNumber); SetError(ERROR_FAILURE); } } diff --git a/Common/StringUtils.cpp b/Common/StringUtils.cpp index e812bad1b6..ee4478a5b4 100644 --- a/Common/StringUtils.cpp +++ b/Common/StringUtils.cpp @@ -154,6 +154,23 @@ std::string LineNumberString(const std::string &str) { return output.str(); } +std::string IndentString(const std::string &str, const std::string &sep, bool skipFirst) { + std::stringstream input(str); + std::stringstream output; + std::string line; + + bool doIndent = !skipFirst; + while (std::getline(input, line)) { + if (doIndent) { + output << sep; + } + doIndent = true; + output << line << "\n"; + } + + return output.str(); +} + void SkipSpace(const char **ptr) { while (**ptr && isspace(**ptr)) { (*ptr)++; diff --git a/Common/StringUtils.h b/Common/StringUtils.h index 0cc81c6cd3..7a3e879dca 100644 --- a/Common/StringUtils.h +++ b/Common/StringUtils.h @@ -23,7 +23,6 @@ #include #ifdef _MSC_VER -#pragma warning (disable:4996) #define strncasecmp _strnicmp #define strcasecmp _stricmp #else @@ -32,6 +31,7 @@ // Useful for shaders with error messages.. std::string LineNumberString(const std::string &str); +std::string IndentString(const std::string &str, const std::string &sep, bool skipFirst = false); // Other simple string utilities. diff --git a/Common/System/System.h b/Common/System/System.h index a713616a10..09560191e0 100644 --- a/Common/System/System.h +++ b/Common/System/System.h @@ -98,6 +98,11 @@ enum SystemProperty { SYSPROP_SUPPORTS_PERMISSIONS, SYSPROP_SUPPORTS_SUSTAINED_PERF_MODE, + + // Android-specific. + SYSPROP_ANDROID_SCOPED_STORAGE, + + SYSPROP_CAN_JIT, }; std::string System_GetProperty(SystemProperty prop); diff --git a/Common/Thunk.cpp b/Common/Thunk.cpp index 06e517cc6c..6248d087a0 100644 --- a/Common/Thunk.cpp +++ b/Common/Thunk.cpp @@ -15,6 +15,7 @@ // Official SVN repository and contact information can be found at // http://code.google.com/p/dolphin-emu/ +#include "ppsspp_config.h" #include "MemoryUtil.h" #include "ABI.h" #include "Thunk.h" @@ -23,7 +24,7 @@ namespace { -#ifndef _M_X64 +#if !PPSSPP_ARCH(AMD64) alignas(32) static u8 saved_fp_state[16 * 4 * 4]; alignas(32) static u8 saved_gpr_state[16 * 8]; static u16 saved_mxcsr; @@ -35,7 +36,7 @@ using namespace Gen; void ThunkManager::Init() { -#ifdef _M_X64 +#if PPSSPP_ARCH(AMD64) // Account for the return address and "home space" on Windows (which needs to be at the bottom.) const int stackOffset = ThunkStackOffset(); int stackPosition; @@ -44,7 +45,7 @@ void ThunkManager::Init() AllocCodeSpace(THUNK_ARENA_SIZE); BeginWrite(); save_regs = GetCodePtr(); -#ifdef _M_X64 +#if PPSSPP_ARCH(AMD64) for (int i = 2; i < ABI_GetNumXMMRegs(); i++) MOVAPS(MDisp(RSP, stackOffset + (i - 2) * 16), (X64Reg)(XMM0 + i)); stackPosition = (ABI_GetNumXMMRegs() - 2) * 2; @@ -70,7 +71,7 @@ void ThunkManager::Init() RET(); load_regs = GetCodePtr(); -#ifdef _M_X64 +#if PPSSPP_ARCH(AMD64) for (int i = 2; i < ABI_GetNumXMMRegs(); i++) MOVAPS((X64Reg)(XMM0 + i), MDisp(RSP, stackOffset + (i - 2) * 16)); stackPosition = (ABI_GetNumXMMRegs() - 2) * 2; @@ -112,7 +113,7 @@ void ThunkManager::Shutdown() int ThunkManager::ThunkBytesNeeded() { int space = (ABI_GetNumXMMRegs() - 2) * 16; -#ifdef _M_X64 +#if PPSSPP_ARCH(AMD64) // MXCSR space += 8; space += 7 * 8; @@ -131,7 +132,7 @@ int ThunkManager::ThunkBytesNeeded() int ThunkManager::ThunkStackOffset() { -#ifdef _M_X64 +#if PPSSPP_ARCH(AMD64) #ifdef _WIN32 return 0x28; #else @@ -154,7 +155,7 @@ const void *ThunkManager::ProtectFunction(const void *function, int num_params) const u8 *call_point = GetCodePtr(); Enter(this, true); -#ifdef _M_X64 +#if PPSSPP_ARCH(AMD64) ABI_CallFunction(function); #else // Since parameters are in the previous stack frame, not in registers, this takes some @@ -180,7 +181,7 @@ const void *ThunkManager::ProtectFunction(const void *function, int num_params) void ThunkManager::Enter(ThunkEmitter *emit, bool withinCall) { -#ifdef _M_X64 +#if PPSSPP_ARCH(AMD64) // Make sure to align stack. emit->SUB(64, R(ESP), Imm32(ThunkStackOffset() + ThunkBytesNeeded() + (withinCall ? 0 : 8))); emit->ABI_CallFunction(save_regs); @@ -191,7 +192,7 @@ void ThunkManager::Enter(ThunkEmitter *emit, bool withinCall) void ThunkManager::Leave(ThunkEmitter *emit, bool withinCall) { -#ifdef _M_X64 +#if PPSSPP_ARCH(AMD64) emit->ABI_CallFunction(load_regs); emit->ADD(64, R(ESP), Imm32(ThunkStackOffset() + ThunkBytesNeeded() + (withinCall ? 0 : 8))); #else diff --git a/Common/TimeUtil.cpp b/Common/TimeUtil.cpp index 8f0ce379ed..08f48e742a 100644 --- a/Common/TimeUtil.cpp +++ b/Common/TimeUtil.cpp @@ -1,6 +1,5 @@ #include #include -#include #include "ppsspp_config.h" @@ -18,6 +17,7 @@ #include #include #endif +#include static double curtime = 0; diff --git a/Common/UI/Context.cpp b/Common/UI/Context.cpp index a492c31185..d1f9631c69 100644 --- a/Common/UI/Context.cpp +++ b/Common/UI/Context.cpp @@ -224,7 +224,7 @@ void UIContext::FillRect(const UI::Drawable &drawable, const Bounds &bounds) { switch (drawable.type) { case UI::DRAW_SOLID_COLOR: - uidrawbuffer_->DrawImageStretch(theme->whiteImage, bounds.x, bounds.y, bounds.x2(), bounds.y2(), drawable.color); + uidrawbuffer_->DrawImageCenterTexel(theme->whiteImage, bounds.x, bounds.y, bounds.x2(), bounds.y2(), drawable.color); break; case UI::DRAW_4GRID: uidrawbuffer_->DrawImage4Grid(drawable.image, bounds.x, bounds.y, bounds.x2(), bounds.y2(), drawable.color); diff --git a/Common/UI/Context.h b/Common/UI/Context.h index 944d31e4d8..29635b74fd 100644 --- a/Common/UI/Context.h +++ b/Common/UI/Context.h @@ -99,7 +99,7 @@ public: Bounds TransformBounds(const Bounds &bounds); private: - Draw::DrawContext *draw_; + Draw::DrawContext *draw_ = nullptr; Bounds bounds_; float fontScaleX_ = 1.0f; @@ -107,7 +107,7 @@ private: UI::FontStyle *fontStyle_ = nullptr; TextDrawer *textDrawer_ = nullptr; - Draw::SamplerState *sampler_; + Draw::SamplerState *sampler_ = nullptr; Draw::Pipeline *ui_pipeline_ = nullptr; Draw::Pipeline *ui_pipeline_notex_ = nullptr; std::unique_ptr uitexture_; diff --git a/Common/UI/Root.cpp b/Common/UI/Root.cpp index 73cca79888..fadbaeeb6d 100644 --- a/Common/UI/Root.cpp +++ b/Common/UI/Root.cpp @@ -1,3 +1,4 @@ +#include #include #include @@ -29,6 +30,7 @@ struct DispatchQueueItem { EventParams params; }; +std::atomic hasDispatchQueue; std::deque g_dispatchQueue; void EventTriggered(Event *e, EventParams params) { @@ -37,11 +39,13 @@ void EventTriggered(Event *e, EventParams params) { item.params = params; std::unique_lock guard(eventMutex_); + // Set before adding so we lock and check the added value. + hasDispatchQueue = true; g_dispatchQueue.push_front(item); } void DispatchEvents() { - while (true) { + while (hasDispatchQueue) { DispatchQueueItem item; { std::unique_lock guard(eventMutex_); @@ -49,6 +53,7 @@ void DispatchEvents() { break; item = g_dispatchQueue.back(); g_dispatchQueue.pop_back(); + hasDispatchQueue = !g_dispatchQueue.empty(); } if (item.e) { item.e->Dispatch(item.params); @@ -57,6 +62,8 @@ void DispatchEvents() { } void RemoveQueuedEventsByView(View *view) { + if (!hasDispatchQueue) + return; std::unique_lock guard(eventMutex_); for (auto it = g_dispatchQueue.begin(); it != g_dispatchQueue.end(); ) { if (it->params.v == view) { @@ -68,6 +75,8 @@ void RemoveQueuedEventsByView(View *view) { } void RemoveQueuedEventsByEvent(Event *event) { + if (!hasDispatchQueue) + return; std::unique_lock guard(eventMutex_); for (auto it = g_dispatchQueue.begin(); it != g_dispatchQueue.end(); ) { if (it->e == event) { diff --git a/Common/UI/Tween.cpp b/Common/UI/Tween.cpp index d06601cb71..bbe819e915 100644 --- a/Common/UI/Tween.cpp +++ b/Common/UI/Tween.cpp @@ -6,7 +6,7 @@ namespace UI { void Tween::Apply(View *view) { - if (!valid_) + if (!valid_ || finishApplied_) return; if (DurationOffset() >= duration_) diff --git a/Common/UI/UIScreen.cpp b/Common/UI/UIScreen.cpp index 8e8d447dc9..156eea5c35 100644 --- a/Common/UI/UIScreen.cpp +++ b/Common/UI/UIScreen.cpp @@ -27,9 +27,9 @@ UIScreen::~UIScreen() { } void UIScreen::DoRecreateViews() { - std::lock_guard guard(screenManager()->inputLock_); - if (recreateViews_) { + std::lock_guard guard(screenManager()->inputLock_); + UI::PersistMap persisted; bool persisting = root_ != nullptr; if (persisting) { @@ -144,7 +144,7 @@ bool UIScreen::touch(const TouchInput &touch) { std::vector views; root_->Query(touch.x, touch.y, views); for (auto view : views) { - INFO_LOG(SYSTEM, "%s", view->Describe().c_str()); + INFO_LOG(SYSTEM, "%s", view->DescribeLog().c_str()); } } @@ -655,7 +655,7 @@ void SliderPopupScreen::CreatePopupContents(UI::ViewGroup *parent) { char temp[64]; sprintf(temp, "%d", sliderValue_); - edit_ = new TextEdit(temp, "", new LinearLayoutParams(10.0f)); + edit_ = new TextEdit(temp, Title(), "", new LinearLayoutParams(10.0f)); edit_->SetMaxLen(16); edit_->SetTextColor(dc.theme->popupStyle.fgColor); edit_->SetTextAlign(FLAG_DYNAMIC_ASCII); @@ -689,7 +689,7 @@ void SliderFloatPopupScreen::CreatePopupContents(UI::ViewGroup *parent) { char temp[64]; sprintf(temp, "%0.3f", sliderValue_); - edit_ = new TextEdit(temp, "", new LinearLayoutParams(10.0f)); + edit_ = new TextEdit(temp, Title(), "", new LinearLayoutParams(10.0f)); edit_->SetMaxLen(16); edit_->SetTextColor(dc.theme->popupStyle.fgColor); edit_->SetTextAlign(FLAG_DYNAMIC_ASCII); @@ -818,7 +818,7 @@ void TextEditPopupScreen::CreatePopupContents(UI::ViewGroup *parent) { textEditValue_ = *value_; LinearLayout *lin = parent->Add(new LinearLayout(ORIENT_HORIZONTAL, new LinearLayoutParams((UI::Size)300, WRAP_CONTENT))); - edit_ = new TextEdit(textEditValue_, placeholder_, new LinearLayoutParams(1.0f)); + edit_ = new TextEdit(textEditValue_, Title(), placeholder_, new LinearLayoutParams(1.0f)); edit_->SetMaxLen(maxLen_); edit_->SetTextColor(dc.theme->popupStyle.fgColor); lin->Add(edit_); diff --git a/Common/UI/UIScreen.h b/Common/UI/UIScreen.h index aa660b0084..3e1fa8fdc9 100644 --- a/Common/UI/UIScreen.h +++ b/Common/UI/UIScreen.h @@ -88,6 +88,7 @@ protected: virtual bool ShowButtons() const { return true; } virtual bool CanComplete(DialogResult result) { return true; } virtual void OnCompleted(DialogResult result) {} + const std::string &Title() { return title_; } virtual void update() override; diff --git a/Common/UI/View.cpp b/Common/UI/View.cpp index 2babf3e4ba..dbb206185d 100644 --- a/Common/UI/View.cpp +++ b/Common/UI/View.cpp @@ -6,6 +6,7 @@ #include "Common/Render/DrawBuffer.h" #include "Common/Render/TextureAtlas.h" #include "Common/Data/Encoding/Utf8.h" +#include "Common/Data/Text/I18n.h" #include "Common/UI/UI.h" #include "Common/UI/View.h" #include "Common/UI/Context.h" @@ -135,7 +136,7 @@ void View::Query(float x, float y, std::vector &list) { } } -std::string View::Describe() const { +std::string View::DescribeLog() const { return StringFromFormat("%0.1f,%0.1f %0.1fx%0.1f", bounds_.x, bounds_.y, bounds_.w, bounds_.h); } @@ -201,7 +202,7 @@ void Clickable::DrawBG(UIContext &dc, const Style &style) { if (style.background.type == DRAW_SOLID_COLOR) { if (time_now_d() - bgColorLast_ >= 0.25f) { bgColor_->Reset(style.background.color); - } else { + } else if (bgColor_->CurrentValue() != style.background.color) { bgColor_->Divert(style.background.color, down_ ? 0.05f : 0.1f); } bgColorLast_ = time_now_d(); @@ -513,6 +514,11 @@ void Choice::Draw(UIContext &dc) { } } +std::string Choice::DescribeText() const { + auto u = GetI18NCategory("UI Elements"); + return ReplaceAll(u->T("%1 choice"), "%1", text_); +} + InfoItem::InfoItem(const std::string &text, const std::string &rightText, LayoutParams *layoutParams) : Item(layoutParams), text_(text), rightText_(rightText) { // We set the colors later once we have a UIContext. @@ -540,11 +546,21 @@ void InfoItem::Draw(UIContext &dc) { dc.FillRect(style.background, bounds_); int paddingX = 12; + Bounds padBounds = bounds_.Expand(-paddingX, 0); + + float leftWidth, leftHeight; + dc.MeasureTextRect(dc.theme->uiFont, 1.0f, 1.0f, text_.c_str(), (int)text_.size(), padBounds, &leftWidth, &leftHeight, ALIGN_VCENTER); dc.SetFontStyle(dc.theme->uiFont); - dc.DrawText(text_.c_str(), bounds_.x + paddingX, bounds_.centerY(), style.fgColor, ALIGN_VCENTER); - dc.DrawText(rightText_.c_str(), bounds_.x2() - paddingX, bounds_.centerY(), style.fgColor, ALIGN_VCENTER | ALIGN_RIGHT); -// dc.Draw()->DrawImageStretch(dc.theme->whiteImage, bounds_.x, bounds_.y, bounds_.x2(), bounds_.y + 2, dc.theme->itemDownStyle.bgColor); + dc.DrawTextRect(text_.c_str(), padBounds, style.fgColor, ALIGN_VCENTER); + + Bounds rightBounds(padBounds.x + leftWidth, padBounds.y, padBounds.w - leftWidth, padBounds.h); + dc.DrawTextRect(rightText_.c_str(), rightBounds, style.fgColor, ALIGN_VCENTER | ALIGN_RIGHT | FLAG_WRAP_TEXT); +} + +std::string InfoItem::DescribeText() const { + auto u = GetI18NCategory("UI Elements"); + return ReplaceAll(ReplaceAll(u->T("%1: %2"), "%1", text_), "%2", rightText_); } ItemHeader::ItemHeader(const std::string &text, LayoutParams *layoutParams) @@ -556,7 +572,7 @@ ItemHeader::ItemHeader(const std::string &text, LayoutParams *layoutParams) void ItemHeader::Draw(UIContext &dc) { dc.SetFontStyle(dc.theme->uiFontSmall); dc.DrawText(text_.c_str(), bounds_.x + 4, bounds_.centerY(), dc.theme->headerStyle.fgColor, ALIGN_LEFT | ALIGN_VCENTER); - dc.Draw()->DrawImageStretch(dc.theme->whiteImage, bounds_.x, bounds_.y2()-2, bounds_.x2(), bounds_.y2(), dc.theme->headerStyle.fgColor); + dc.Draw()->DrawImageCenterTexel(dc.theme->whiteImage, bounds_.x, bounds_.y2()-2, bounds_.x2(), bounds_.y2(), dc.theme->headerStyle.fgColor); } void ItemHeader::GetContentDimensionsBySpec(const UIContext &dc, MeasureSpec horiz, MeasureSpec vert, float &w, float &h) const { @@ -572,6 +588,11 @@ void ItemHeader::GetContentDimensionsBySpec(const UIContext &dc, MeasureSpec hor dc.MeasureTextRect(dc.theme->uiFontSmall, 1.0f, 1.0f, text_.c_str(), (int)text_.length(), bounds, &w, &h, ALIGN_LEFT | ALIGN_VCENTER); } +std::string ItemHeader::DescribeText() const { + auto u = GetI18NCategory("UI Elements"); + return ReplaceAll(u->T("%1 heading"), "%1", text_); +} + void PopupHeader::Draw(UIContext &dc) { const float paddingHorizontal = 12; const float availableWidth = bounds_.w - paddingHorizontal * 2; @@ -593,13 +614,18 @@ void PopupHeader::Draw(UIContext &dc) { } dc.DrawText(text_.c_str(), bounds_.x + tx, bounds_.centerY(), dc.theme->popupTitle.fgColor, ALIGN_LEFT | ALIGN_VCENTER); - dc.Draw()->DrawImageStretch(dc.theme->whiteImage, bounds_.x, bounds_.y2()-2, bounds_.x2(), bounds_.y2(), dc.theme->popupTitle.fgColor); + dc.Draw()->DrawImageCenterTexel(dc.theme->whiteImage, bounds_.x, bounds_.y2()-2, bounds_.x2(), bounds_.y2(), dc.theme->popupTitle.fgColor); if (availableWidth < tw) { dc.PopScissor(); } } +std::string PopupHeader::DescribeText() const { + auto u = GetI18NCategory("UI Elements"); + return ReplaceAll(u->T("%1 heading"), "%1", text_); +} + void CheckBox::Toggle() { if (toggle_) { *toggle_ = !(*toggle_); @@ -642,6 +668,15 @@ void CheckBox::Draw(UIContext &dc) { dc.SetFontScale(1.0f, 1.0f); } +std::string CheckBox::DescribeText() const { + auto u = GetI18NCategory("UI Elements"); + std::string text = ReplaceAll(u->T("%1 checkbox"), "%1", text_); + if (!smallText_.empty()) { + text += "\n" + smallText_; + } + return text; +} + float CheckBox::CalculateTextScale(const UIContext &dc, float availWidth) const { float actualWidth, actualHeight; Bounds availBounds(0, 0, availWidth, bounds_.h); @@ -705,6 +740,11 @@ void Button::GetContentDimensions(const UIContext &dc, float &w, float &h) const h *= scale_; } +std::string Button::DescribeText() const { + auto u = GetI18NCategory("UI Elements"); + return ReplaceAll(u->T("%1 button"), "%1", GetText()); +} + void Button::Click() { Clickable::Click(); UI::PlayUISound(UI::UISound::CONFIRM); @@ -729,7 +769,7 @@ void Button::Draw(UIContext &dc) { } dc.SetFontStyle(dc.theme->uiFont); dc.SetFontScale(scale_, scale_); - if (imageID_.isValid() && text_.empty()) { + if (imageID_.isValid() && (ignoreText_ || text_.empty())) { dc.Draw()->DrawImage(imageID_, bounds_.centerX(), bounds_.centerY(), scale_, 0xFFFFFFFF, ALIGN_CENTER); } else if (!text_.empty()) { dc.DrawText(text_.c_str(), bounds_.centerX(), bounds_.centerY(), style.fgColor, ALIGN_CENTER); @@ -812,8 +852,8 @@ void TextView::Draw(UIContext &dc) { } } -TextEdit::TextEdit(const std::string &text, const std::string &placeholderText, LayoutParams *layoutParams) - : View(layoutParams), text_(text), undo_(text), placeholderText_(placeholderText), +TextEdit::TextEdit(const std::string &text, const std::string &title, const std::string &placeholderText, LayoutParams *layoutParams) + : View(layoutParams), text_(text), title_(title), undo_(text), placeholderText_(placeholderText), textColor_(0xFFFFFFFF), maxLen_(255) { caret_ = (int)text_.size(); } @@ -861,6 +901,11 @@ void TextEdit::GetContentDimensions(const UIContext &dc, float &w, float &h) con h += 2; } +std::string TextEdit::DescribeText() const { + auto u = GetI18NCategory("UI Elements"); + return ReplaceAll(u->T("%1 text field"), "%1", GetText()); +} + // Handles both windows and unix line endings. static std::string FirstLine(const std::string &text) { size_t pos = text.find("\r\n"); @@ -1032,11 +1077,17 @@ void ProgressBar::GetContentDimensions(const UIContext &dc, float &w, float &h) void ProgressBar::Draw(UIContext &dc) { char temp[32]; sprintf(temp, "%i%%", (int)(progress_ * 100.0f)); - dc.Draw()->DrawImageStretch(dc.theme->whiteImage, bounds_.x, bounds_.y, bounds_.x + bounds_.w * progress_, bounds_.y2(), 0xc0c0c0c0); + dc.Draw()->DrawImageCenterTexel(dc.theme->whiteImage, bounds_.x, bounds_.y, bounds_.x + bounds_.w * progress_, bounds_.y2(), 0xc0c0c0c0); dc.SetFontStyle(dc.theme->uiFont); dc.DrawTextRect(temp, bounds_, 0xFFFFFFFF, ALIGN_CENTER); } +std::string ProgressBar::DescribeText() const { + auto u = GetI18NCategory("UI Elements"); + float percent = progress_ * 100.0f; + return ReplaceAll(u->T("Progress: %1%"), "%1", StringFromInt((int)percent)); +} + void Spinner::GetContentDimensions(const UIContext &dc, float &w, float &h) const { w = 48; h = 48; @@ -1181,6 +1232,12 @@ void Slider::Draw(UIContext &dc) { dc.DrawText(temp, bounds_.x2() - 22, bounds_.centerY(), dc.theme->popupStyle.fgColor, ALIGN_CENTER | FLAG_DYNAMIC_ASCII); } +std::string Slider::DescribeText() const { + if (showPercent_) + return StringFromFormat("%i%% / %i%%", *value_, maxValue_); + return StringFromFormat("%i / %i", *value_, maxValue_); +} + void Slider::Update() { View::Update(); if (repeat_ >= 0) { @@ -1291,6 +1348,10 @@ void SliderFloat::Draw(UIContext &dc) { dc.DrawText(temp, bounds_.x2() - 22, bounds_.centerY(), dc.theme->popupStyle.fgColor, ALIGN_CENTER); } +std::string SliderFloat::DescribeText() const { + return StringFromFormat("%0.2f / %0.2f", *value_, maxValue_); +} + void SliderFloat::Update() { View::Update(); if (repeat_ >= 0) { diff --git a/Common/UI/View.h b/Common/UI/View.h index 4f779fa1f4..1d7c2b40d2 100644 --- a/Common/UI/View.h +++ b/Common/UI/View.h @@ -365,7 +365,9 @@ public: // If this view covers these coordinates, it should add itself and its children to the list. virtual void Query(float x, float y, std::vector &list); - virtual std::string Describe() const; + virtual std::string DescribeLog() const; + // Accessible/searchable description. + virtual std::string DescribeText() const { return ""; } virtual void FocusChanged(int focusFlags) {} virtual void PersistData(PersistStatus status, std::string anonId, PersistMap &storage); @@ -514,8 +516,6 @@ class Button : public Clickable { public: Button(const std::string &text, LayoutParams *layoutParams = 0) : Clickable(layoutParams), text_(text), imageID_(ImageID::invalid()) {} - Button(ImageID imageID, LayoutParams *layoutParams = 0) - : Clickable(layoutParams), imageID_(imageID) {} Button(const std::string &text, ImageID imageID, LayoutParams *layoutParams = 0) : Clickable(layoutParams), text_(text), imageID_(imageID) {} @@ -523,6 +523,7 @@ public: void Draw(UIContext &dc) override; void GetContentDimensions(const UIContext &dc, float &w, float &h) const override; const std::string &GetText() const { return text_; } + std::string DescribeText() const override; void SetPadding(int w, int h) { paddingW_ = w; paddingH_ = h; @@ -530,6 +531,9 @@ public: void SetImageID(ImageID imageID) { imageID_ = imageID; } + void SetIgnoreText(bool ignore) { + ignoreText_ = ignore; + } // Needed an extra small button... void SetScale(float f) { @@ -543,6 +547,7 @@ private: int paddingW_ = 16; int paddingH_ = 8; float scale_ = 1.0f; + bool ignoreText_ = false; }; class Slider : public Clickable { @@ -555,6 +560,7 @@ public: step_ = step <= 0 ? 1 : step; } void Draw(UIContext &dc) override; + std::string DescribeText() const override; bool Key(const KeyInput &input) override; void Touch(const TouchInput &input) override; void Update() override; @@ -585,6 +591,7 @@ public: SliderFloat(float *value, float minValue, float maxValue, LayoutParams *layoutParams = 0) : Clickable(layoutParams), value_(value), minValue_(minValue), maxValue_(maxValue), paddingLeft_(5), paddingRight_(70), repeat_(-1) {} void Draw(UIContext &dc) override; + std::string DescribeText() const override; bool Key(const KeyInput &input) override; void Touch(const TouchInput &input) override; void Update() override; @@ -661,6 +668,7 @@ public: virtual void HighlightChanged(bool highlighted); void GetContentDimensionsBySpec(const UIContext &dc, MeasureSpec horiz, MeasureSpec vert, float &w, float &h) const override; void Draw(UIContext &dc) override; + std::string DescribeText() const override; virtual void SetCentered(bool c) { centered_ = c; } @@ -711,6 +719,7 @@ public: InfoItem(const std::string &text, const std::string &rightText, LayoutParams *layoutParams = nullptr); void Draw(UIContext &dc) override; + std::string DescribeText() const override; // These are focusable so that long lists of them can be keyboard scrolled. bool CanBeFocused() const override { return true; } @@ -737,6 +746,7 @@ class ItemHeader : public Item { public: ItemHeader(const std::string &text, LayoutParams *layoutParams = 0); void Draw(UIContext &dc) override; + std::string DescribeText() const override; void GetContentDimensionsBySpec(const UIContext &dc, MeasureSpec horiz, MeasureSpec vert, float &w, float &h) const override; private: @@ -751,6 +761,8 @@ public: layoutParams_->height = 64; } void Draw(UIContext &dc) override; + std::string DescribeText() const override; + private: std::string text_; }; @@ -763,6 +775,7 @@ public: } void Draw(UIContext &dc) override; + std::string DescribeText() const override; void GetContentDimensions(const UIContext &dc, float &w, float &h) const override; EventReturn OnClicked(EventParams &e); @@ -803,6 +816,8 @@ public: w = size_; h = size_; } void Draw(UIContext &dc) override {} + std::string DescribeText() const override { return ""; } + private: float size_; }; @@ -820,6 +835,7 @@ public: void SetText(const std::string &text) { text_ = text; } const std::string &GetText() const { return text_; } + std::string DescribeText() const override { return GetText(); } void SetSmall(bool small) { small_ = small; } void SetTextColor(uint32_t color) { textColor_ = color; hasTextColor_ = true; } void SetShadow(bool shadow) { shadow_ = shadow; } @@ -841,7 +857,7 @@ private: class TextEdit : public View { public: - TextEdit(const std::string &text, const std::string &placeholderText, LayoutParams *layoutParams = 0); + TextEdit(const std::string &text, const std::string &title, const std::string &placeholderText, LayoutParams *layoutParams = nullptr); void SetText(const std::string &text) { text_ = text; scrollPos_ = 0; caret_ = (int)text_.size(); } void SetTextColor(uint32_t color) { textColor_ = color; hasTextColor_ = true; } const std::string &GetText() const { return text_; } @@ -850,6 +866,7 @@ public: void GetContentDimensions(const UIContext &dc, float &w, float &h) const override; void Draw(UIContext &dc) override; + std::string DescribeText() const override; bool Key(const KeyInput &key) override; void Touch(const TouchInput &touch) override; @@ -860,6 +877,7 @@ private: void InsertAtCaret(const char *text); std::string text_; + std::string title_; std::string undo_; std::string placeholderText_; uint32_t textColor_; @@ -880,13 +898,15 @@ enum ImageSizeMode { class ImageView : public InertView { public: - ImageView(ImageID atlasImage, ImageSizeMode sizeMode, LayoutParams *layoutParams = 0) - : InertView(layoutParams), atlasImage_(atlasImage), sizeMode_(sizeMode) {} + ImageView(ImageID atlasImage, const std::string &text, ImageSizeMode sizeMode, LayoutParams *layoutParams = 0) + : InertView(layoutParams), text_(text), atlasImage_(atlasImage), sizeMode_(sizeMode) {} void GetContentDimensions(const UIContext &dc, float &w, float &h) const override; void Draw(UIContext &dc) override; + std::string DescribeText() const override { return text_; } private: + std::string text_; ImageID atlasImage_; ImageSizeMode sizeMode_; }; @@ -898,6 +918,7 @@ public: void GetContentDimensions(const UIContext &dc, float &w, float &h) const override; void Draw(UIContext &dc) override; + std::string DescribeText() const override; void SetProgress(float progress) { if (progress > 1.0f) { @@ -922,6 +943,7 @@ public: void GetContentDimensions(const UIContext &dc, float &w, float &h) const override; void Draw(UIContext &dc) override; + std::string DescribeText() const override { return ""; } void SetColor(uint32_t color) { color_ = color; } private: diff --git a/Common/UI/ViewGroup.cpp b/Common/UI/ViewGroup.cpp index 60685b8b80..7f133a25b6 100644 --- a/Common/UI/ViewGroup.cpp +++ b/Common/UI/ViewGroup.cpp @@ -1,8 +1,12 @@ #include +#include #include -#include +#include #include +#include +#include +#include "Common/Data/Text/I18n.h" #include "Common/Input/KeyCodes.h" #include "Common/Math/curves.h" #include "Common/UI/Context.h" @@ -154,6 +158,64 @@ void ViewGroup::Draw(UIContext &dc) { } } +std::string ViewGroup::DescribeText() const { + std::stringstream ss; + bool needNewline = false; + for (View *view : views_) { + if (view->GetVisibility() != V_VISIBLE) + continue; + std::string s = view->DescribeText(); + if (s.empty()) + continue; + + if (needNewline) { + ss << "\n"; + } + ss << s; + needNewline = s[s.length() - 1] != '\n'; + } + return ss.str(); +} + +std::string ViewGroup::DescribeListUnordered(const char *heading) const { + std::stringstream ss; + ss << heading << "\n"; + + bool needNewline = false; + for (View *view : views_) { + if (view->GetVisibility() != V_VISIBLE) + continue; + std::string s = view->DescribeText(); + if (s.empty()) + continue; + + ss << " - " << IndentString(s, " ", true); + } + return ss.str(); +} + +std::string ViewGroup::DescribeListOrdered(const char *heading) const { + std::stringstream ss; + ss << heading << "\n"; + + // This is how much space we need for the highest number. + int sz = (int)floorf(log10f((float)views_.size())) + 1; + std::string indent = " " + std::string(sz, ' '); + + bool needNewline = false; + int n = 1; + for (View *view : views_) { + if (view->GetVisibility() != V_VISIBLE) + continue; + std::string s = view->DescribeText(); + if (s.empty()) + continue; + + ss << std::setw(sz) << n++ << ". " << IndentString(s, indent, true); + } + return ss.str(); +} + void ViewGroup::Update() { View::Update(); for (View *view : views_) { @@ -582,6 +644,11 @@ void LinearLayout::Layout() { } } +std::string LinearLayoutList::DescribeText() const { + auto u = GetI18NCategory("UI Elements"); + return DescribeListOrdered(u->T("List:")); +} + void FrameLayout::Measure(const UIContext &dc, MeasureSpec horiz, MeasureSpec vert) { if (views_.empty()) { MeasureBySpec(layoutParams_->width, 0.0f, horiz, &measuredWidth_); @@ -1134,6 +1201,11 @@ void GridLayout::Layout() { } } +std::string GridLayoutList::DescribeText() const { + auto u = GetI18NCategory("UI Elements"); + return DescribeListOrdered(u->T("List:")); +} + TabHolder::TabHolder(Orientation orientation, float stripSize, LayoutParams *layoutParams) : LinearLayout(Opposite(orientation), layoutParams), stripSize_(stripSize) { SetSpacing(0.0f); @@ -1216,7 +1288,7 @@ void TabHolder::SetCurrentTab(int tab, bool skipTween) { currentTab_ = tab; } - tabStrip_->SetSelection(tab); + tabStrip_->SetSelection(tab, false); } EventReturn TabHolder::OnTabClick(EventParams &e) { @@ -1297,7 +1369,7 @@ EventReturn ChoiceStrip::OnChoiceClick(EventParams &e) { return OnChoice.Dispatch(e2); } -void ChoiceStrip::SetSelection(int sel) { +void ChoiceStrip::SetSelection(int sel, bool triggerClick) { int prevSelected = selected_; StickyChoice *prevChoice = Choice(selected_); if (prevChoice) @@ -1312,7 +1384,7 @@ void ChoiceStrip::SetSelection(int sel) { e.v = views_[selected_]; e.a = selected_; // Set to 0 to indicate a selection change (not a click.) - e.b = 0; + e.b = triggerClick ? 1 : 0; OnChoice.Trigger(e); } } @@ -1326,16 +1398,16 @@ void ChoiceStrip::HighlightChoice(unsigned int choice){ bool ChoiceStrip::Key(const KeyInput &input) { bool ret = false; - if (input.flags & KEY_DOWN) { + if (topTabs_ && (input.flags & KEY_DOWN)) { if (IsTabLeftKey(input)) { if (selected_ > 0) { - SetSelection(selected_ - 1); + SetSelection(selected_ - 1, true); UI::PlayUISound(UI::UISound::TOGGLE_OFF); // Maybe make specific sounds for this at some point? } ret = true; } else if (IsTabRightKey(input)) { if (selected_ < (int)views_.size() - 1) { - SetSelection(selected_ + 1); + SetSelection(selected_ + 1, true); UI::PlayUISound(UI::UISound::TOGGLE_ON); } ret = true; @@ -1348,12 +1420,17 @@ void ChoiceStrip::Draw(UIContext &dc) { ViewGroup::Draw(dc); if (topTabs_) { if (orientation_ == ORIENT_HORIZONTAL) - dc.Draw()->DrawImageStretch(dc.theme->whiteImage, bounds_.x, bounds_.y2() - 4, bounds_.x2(), bounds_.y2(), dc.theme->itemDownStyle.background.color ); + dc.Draw()->DrawImageCenterTexel(dc.theme->whiteImage, bounds_.x, bounds_.y2() - 4, bounds_.x2(), bounds_.y2(), dc.theme->itemDownStyle.background.color ); else if (orientation_ == ORIENT_VERTICAL) - dc.Draw()->DrawImageStretch(dc.theme->whiteImage, bounds_.x2() - 4, bounds_.y, bounds_.x2(), bounds_.y2(), dc.theme->itemDownStyle.background.color ); + dc.Draw()->DrawImageCenterTexel(dc.theme->whiteImage, bounds_.x2() - 4, bounds_.y, bounds_.x2(), bounds_.y2(), dc.theme->itemDownStyle.background.color ); } } +std::string ChoiceStrip::DescribeText() const { + auto u = GetI18NCategory("UI Elements"); + return DescribeListUnordered(u->T("Choices:")); +} + StickyChoice *ChoiceStrip::Choice(int index) { if ((size_t)index < views_.size()) return static_cast(views_[index]); @@ -1386,6 +1463,11 @@ void ListView::Measure(const UIContext &dc, MeasureSpec horiz, MeasureSpec vert) } } +std::string ListView::DescribeText() const { + auto u = GetI18NCategory("UI Elements"); + return DescribeListOrdered(u->T("List:")); +} + EventReturn ListView::OnItemCallback(int num, EventParams &e) { EventParams ev{}; ev.v = nullptr; diff --git a/Common/UI/ViewGroup.h b/Common/UI/ViewGroup.h index 7e093d775b..5bbac5e060 100644 --- a/Common/UI/ViewGroup.h +++ b/Common/UI/ViewGroup.h @@ -80,9 +80,13 @@ public: void Unlock() { modifyLock_.unlock(); } void SetClip(bool clip) { clip_ = clip; } - std::string Describe() const override { return "ViewGroup: " + View::Describe(); } + std::string DescribeLog() const override { return "ViewGroup: " + View::DescribeLog(); } + std::string DescribeText() const override; protected: + std::string DescribeListUnordered(const char *heading) const; + std::string DescribeListOrdered(const char *heading) const; + std::mutex modifyLock_; // Hold this when changing the subviews. std::vector views_; View *defaultFocusView_ = nullptr; @@ -132,7 +136,7 @@ public: void Overflow(bool allow) { overflow_ = allow; } - std::string Describe() const override { return "AnchorLayout: " + View::Describe(); } + std::string DescribeLog() const override { return "AnchorLayout: " + View::DescribeLog(); } private: void MeasureViews(const UIContext &dc, MeasureSpec horiz, MeasureSpec vert); @@ -182,7 +186,7 @@ public: void SetSpacing(float spacing) { spacing_ = spacing; } - std::string Describe() const override { return (orientation_ == ORIENT_HORIZONTAL ? "LinearLayoutHoriz: " : "LinearLayoutVert: ") + View::Describe(); } + std::string DescribeLog() const override { return (orientation_ == ORIENT_HORIZONTAL ? "LinearLayoutHoriz: " : "LinearLayoutVert: ") + View::DescribeLog(); } protected: Orientation orientation_; @@ -191,6 +195,15 @@ private: float spacing_; }; +class LinearLayoutList : public LinearLayout { +public: + LinearLayoutList(Orientation orientation, LayoutParams *layoutParams = nullptr) + : LinearLayout(orientation, layoutParams) { + } + + std::string DescribeText() const override; +}; + // GridLayout is a little different from the Android layout. This one has fixed size // rows and columns. Items are not allowed to deviate from the set sizes. // Initially, only horizontal layout is supported. @@ -212,13 +225,22 @@ public: void Measure(const UIContext &dc, MeasureSpec horiz, MeasureSpec vert) override; void Layout() override; - std::string Describe() const override { return "GridLayout: " + View::Describe(); } + std::string DescribeLog() const override { return "GridLayout: " + View::DescribeLog(); } private: GridLayoutSettings settings_; int numColumns_; }; +class GridLayoutList : public GridLayout { +public: + GridLayoutList(GridLayoutSettings settings, LayoutParams *layoutParams = nullptr) + : GridLayout(settings, layoutParams) { + } + + std::string DescribeText() const override; +}; + // A scrollview usually contains just a single child - a linear layout or similar. class ScrollView : public ViewGroup { public: @@ -231,7 +253,7 @@ public: bool Key(const KeyInput &input) override; void Touch(const TouchInput &input) override; void Draw(UIContext &dc) override; - std::string Describe() const override { return "ScrollView: " + View::Describe(); } + std::string DescribeLog() const override { return "ScrollView: " + View::DescribeLog(); } void ScrollTo(float newScrollPos); void ScrollToBottom(); @@ -278,7 +300,7 @@ public: void AddChoice(ImageID buttonImage); int GetSelection() const { return selected_; } - void SetSelection(int sel); + void SetSelection(int sel, bool triggerClick); void HighlightChoice(unsigned int choice); @@ -287,7 +309,8 @@ public: void SetTopTabs(bool tabs) { topTabs_ = tabs; } void Draw(UIContext &dc) override; - std::string Describe() const override { return "ChoiceStrip: " + View::Describe(); } + std::string DescribeLog() const override { return "ChoiceStrip: " + View::DescribeLog(); } + std::string DescribeText() const override; Event OnChoice; @@ -313,7 +336,7 @@ public: void SetCurrentTab(int tab, bool skipTween = false); int GetCurrentTab() const { return currentTab_; } - std::string Describe() const override { return "TabHolder: " + View::Describe(); } + std::string DescribeLog() const override { return "TabHolder: " + View::DescribeLog(); } void PersistData(PersistStatus status, std::string anonId, PersistMap &storage) override; @@ -383,7 +406,8 @@ public: virtual void Measure(const UIContext &dc, MeasureSpec horiz, MeasureSpec vert) override; virtual void SetMaxHeight(float mh) { maxHeight_ = mh; } Event OnChoice; - std::string Describe() const override { return "ListView: " + View::Describe(); } + std::string DescribeLog() const override { return "ListView: " + View::DescribeLog(); } + std::string DescribeText() const override; private: void CreateAllItems(); diff --git a/Common/x64Emitter.cpp b/Common/x64Emitter.cpp index 82c790c36f..7a3850dbb8 100644 --- a/Common/x64Emitter.cpp +++ b/Common/x64Emitter.cpp @@ -15,6 +15,7 @@ // Official SVN repository and contact information can be found at // http://code.google.com/p/dolphin-emu/ +#include "ppsspp_config.h" #include #include "x64Emitter.h" @@ -160,7 +161,7 @@ void XEmitter::WriteSIB(int scale, int index, int base) void OpArg::WriteRex(XEmitter *emit, int opBits, int bits, int customOp) const { if (customOp == -1) customOp = operandReg; -#ifdef _M_X64 +#if PPSSPP_ARCH(AMD64) u8 op = 0x40; // REX.W (whether operation is a 64-bit operation) if (opBits == 64) op |= 8; @@ -232,7 +233,7 @@ void OpArg::WriteRest(XEmitter *emit, int extraBytes, X64Reg _operandReg, _offsetOrBaseReg = 5; emit->WriteModRM(0, _operandReg, _offsetOrBaseReg); //TODO : add some checks -#ifdef _M_X64 +#if PPSSPP_ARCH(AMD64) u64 ripAddr = (u64)emit->GetCodePointer() + 4 + extraBytes; s64 distance = (s64)offset - (s64)ripAddr; _assert_msg_( @@ -501,7 +502,8 @@ void XEmitter::SetJumpTarget(const FixupBranch &branch) { s64 distance = (s64)(code - branch.ptr); _assert_msg_(distance >= -0x80000000LL && distance < 0x80000000LL, "Jump target too far away, needs indirect register"); - ((s32*)branch.ptr)[-1] = (s32)distance; + const s32 distance32 = static_cast(distance); + std::memcpy(branch.ptr - sizeof(s32), &distance32, sizeof(s32)); } } @@ -1450,7 +1452,7 @@ void XEmitter::MOVD_xmm(const OpArg &arg, X64Reg src) {WriteSSEOp(0x66, 0x7E, sr void XEmitter::MOVQ_xmm(X64Reg dest, OpArg arg) { -#ifdef _M_X64 +#if PPSSPP_ARCH(AMD64) // Alternate encoding // This does not display correctly in MSVC's debugger, it thinks it's a MOVD arg.operandReg = dest; diff --git a/Common/x64Emitter.h b/Common/x64Emitter.h index fe32fa0d6d..5347754bf7 100644 --- a/Common/x64Emitter.h +++ b/Common/x64Emitter.h @@ -20,6 +20,7 @@ #include "ppsspp_config.h" #include +#include #include "Common/Common.h" #include "Common/Log.h" @@ -361,9 +362,9 @@ private: protected: inline void Write8(u8 value) {*code++ = value;} - inline void Write16(u16 value) {*(u16*)code = (value); code += 2;} - inline void Write32(u32 value) {*(u32*)code = (value); code += 4;} - inline void Write64(u64 value) {*(u64*)code = (value); code += 8;} + inline void Write16(u16 value) {std::memcpy(code, &value, sizeof(u16)); code += 2;} + inline void Write32(u32 value) {std::memcpy(code, &value, sizeof(u32)); code += 4;} + inline void Write64(u64 value) {std::memcpy(code, &value, sizeof(u64)); code += 8;} public: XEmitter() { code = nullptr; flags_locked = false; } @@ -1056,7 +1057,7 @@ public: void ABI_EmitPrologue(int maxCallParams); void ABI_EmitEpilogue(int maxCallParams); - #ifdef _M_IX86 + #if PPSSPP_ARCH(X86) inline int ABI_GetNumXMMRegs() { return 8; } #else inline int ABI_GetNumXMMRegs() { return 16; } @@ -1074,7 +1075,7 @@ public: bool RipAccessible(const void *ptr) const { // For debugging // return false; -#ifdef _M_IX86 +#if PPSSPP_ARCH(X86) return true; #else ptrdiff_t diff = GetCodePtr() - (const uint8_t *)ptr; diff --git a/Core/AVIDump.cpp b/Core/AVIDump.cpp index 19cbcf1227..1e3f1803d5 100644 --- a/Core/AVIDump.cpp +++ b/Core/AVIDump.cpp @@ -44,11 +44,12 @@ extern "C" { #define av_frame_free avcodec_free_frame #endif -static AVFormatContext* s_format_context = nullptr; -static AVStream* s_stream = nullptr; -static AVFrame* s_src_frame = nullptr; -static AVFrame* s_scaled_frame = nullptr; -static SwsContext* s_sws_context = nullptr; +static AVFormatContext *s_format_context = nullptr; +static AVCodecContext *s_codec_context = nullptr; +static AVStream *s_stream = nullptr; +static AVFrame *s_src_frame = nullptr; +static AVFrame *s_scaled_frame = nullptr; +static SwsContext *s_sws_context = nullptr; #endif @@ -65,7 +66,9 @@ static void InitAVCodec() { static bool first_run = true; if (first_run) { #ifdef USE_FFMPEG +#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(58, 12, 100) av_register_all(); +#endif #endif first_run = false; } @@ -87,51 +90,70 @@ bool AVIDump::Start(int w, int h) bool AVIDump::CreateAVI() { #ifdef USE_FFMPEG - AVCodec* codec = nullptr; + AVCodec *codec = nullptr; // Use gameID_EmulatedTimestamp for filename std::string discID = g_paramSFO.GetDiscID(); std::string video_file_name = StringFromFormat("%s%s_%s.avi", GetSysDirectory(DIRECTORY_VIDEO).c_str(), discID.c_str(), KernelTimeNowFormatted().c_str()).c_str(); s_format_context = avformat_alloc_context(); - std::stringstream s_file_index_str; - s_file_index_str << video_file_name; - snprintf(s_format_context->filename, sizeof(s_format_context->filename), "%s", s_file_index_str.str().c_str()); - INFO_LOG(COMMON, "Recording Video to: %s", s_format_context->filename); +#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(58, 7, 0) + char *filename = av_strdup(video_file_name.c_str()); + // Freed when the context is freed. + s_format_context->url = filename; +#else + const char *filename = s_format_context->filename; + snprintf(s_format_context->filename, sizeof(s_format_context->filename), "%s", video_file_name.c_str()); +#endif + INFO_LOG(COMMON, "Recording Video to: %s", filename); + // Make sure that the path exists if (!File::Exists(GetSysDirectory(DIRECTORY_VIDEO))) File::CreateDir(GetSysDirectory(DIRECTORY_VIDEO)); - if (File::Exists(s_format_context->filename)) - File::Delete(s_format_context->filename); + if (File::Exists(filename)) + File::Delete(filename); - if (!(s_format_context->oformat = av_guess_format("avi", nullptr, nullptr)) || !(s_stream = avformat_new_stream(s_format_context, codec))) - { + s_format_context->oformat = av_guess_format("avi", nullptr, nullptr); + if (!s_format_context->oformat) + return false; + s_stream = avformat_new_stream(s_format_context, codec); + if (!s_stream) return false; - } - s_stream->codec->codec_id = g_Config.bUseFFV1 ? AV_CODEC_ID_FFV1 : s_format_context->oformat->video_codec; +#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(57, 48, 101) + s_codec_context = s_stream->codec; +#else + s_codec_context = avcodec_alloc_context3(codec); +#endif + s_codec_context->codec_id = g_Config.bUseFFV1 ? AV_CODEC_ID_FFV1 : s_format_context->oformat->video_codec; if (!g_Config.bUseFFV1) - s_stream->codec->codec_tag = MKTAG('X', 'V', 'I', 'D'); // Force XVID FourCC for better compatibility - s_stream->codec->codec_type = AVMEDIA_TYPE_VIDEO; - s_stream->codec->bit_rate = 400000; - s_stream->codec->width = s_width; - s_stream->codec->height = s_height; - s_stream->codec->time_base.num = 1001; - s_stream->codec->time_base.den = 60000; - s_stream->codec->gop_size = 12; - s_stream->codec->pix_fmt = g_Config.bUseFFV1 ? AV_PIX_FMT_BGRA : AV_PIX_FMT_YUV420P; + s_codec_context->codec_tag = MKTAG('X', 'V', 'I', 'D'); // Force XVID FourCC for better compatibility + s_codec_context->codec_type = AVMEDIA_TYPE_VIDEO; + s_codec_context->bit_rate = 400000; + s_codec_context->width = s_width; + s_codec_context->height = s_height; + s_codec_context->time_base.num = 1001; + s_codec_context->time_base.den = 60000; + s_codec_context->gop_size = 12; + s_codec_context->pix_fmt = g_Config.bUseFFV1 ? AV_PIX_FMT_BGRA : AV_PIX_FMT_YUV420P; - if (!(codec = avcodec_find_encoder(s_stream->codec->codec_id)) || (avcodec_open2(s_stream->codec, codec, nullptr) < 0)) - { +#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 48, 101) + if (avcodec_parameters_from_context(s_stream->codecpar, s_codec_context) < 0) + return false; +#endif + + codec = avcodec_find_encoder(s_codec_context->codec_id); + if (!codec) + return false; + if (avcodec_open2(s_codec_context, codec, nullptr) < 0) return false; - } s_src_frame = av_frame_alloc(); s_scaled_frame = av_frame_alloc(); - s_scaled_frame->format = s_stream->codec->pix_fmt; + s_scaled_frame->format = s_codec_context->pix_fmt; s_scaled_frame->width = s_width; s_scaled_frame->height = s_height; @@ -139,14 +161,13 @@ bool AVIDump::CreateAVI() { if (av_frame_get_buffer(s_scaled_frame, 1)) return false; #else - if (avcodec_default_get_buffer(s_stream->codec, s_scaled_frame)) + if (avcodec_default_get_buffer(s_codec_context, s_scaled_frame)) return false; #endif - NOTICE_LOG(G3D, "Opening file %s for dumping", s_format_context->filename); - if (avio_open(&s_format_context->pb, s_format_context->filename, AVIO_FLAG_WRITE) < 0 || avformat_write_header(s_format_context, nullptr)) - { - WARN_LOG(G3D, "Could not open %s", s_format_context->filename); + NOTICE_LOG(G3D, "Opening file %s for dumping", filename); + if (avio_open(&s_format_context->pb, filename, AVIO_FLAG_WRITE) < 0 || avformat_write_header(s_format_context, nullptr)) { + WARN_LOG(G3D, "Could not open %s", filename); return false; } @@ -166,8 +187,7 @@ static void PreparePacket(AVPacket* pkt) { #endif -void AVIDump::AddFrame() -{ +void AVIDump::AddFrame() { u32 w = 0; u32 h = 0; if (g_Config.bDumpVideoOutput) { @@ -192,44 +212,60 @@ void AVIDump::AddFrame() s_src_frame->height = s_height; // Convert image from BGR24 to desired pixel format, and scale to initial width and height - if ((s_sws_context = sws_getCachedContext(s_sws_context, w, h, AV_PIX_FMT_RGB24, s_width, s_height, s_stream->codec->pix_fmt, SWS_BICUBIC, nullptr, nullptr, nullptr))) - { + if ((s_sws_context = sws_getCachedContext(s_sws_context, w, h, AV_PIX_FMT_RGB24, s_width, s_height, s_codec_context->pix_fmt, SWS_BICUBIC, nullptr, nullptr, nullptr))) { sws_scale(s_sws_context, s_src_frame->data, s_src_frame->linesize, 0, h, s_scaled_frame->data, s_scaled_frame->linesize); } - s_scaled_frame->format = s_stream->codec->pix_fmt; + s_scaled_frame->format = s_codec_context->pix_fmt; s_scaled_frame->width = s_width; s_scaled_frame->height = s_height; // Encode and write the image. AVPacket pkt; PreparePacket(&pkt); +#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 48, 101) + int error = avcodec_send_frame(s_codec_context, s_scaled_frame); + int got_packet = 0; + if (avcodec_receive_packet(s_codec_context, &pkt) >= 0) { + got_packet = 1; + } +#else int got_packet; - int error = avcodec_encode_video2(s_stream->codec, &pkt, s_scaled_frame, &got_packet); - while (!error && got_packet) - { + int error = avcodec_encode_video2(s_codec_context, &pkt, s_scaled_frame, &got_packet); +#endif + while (error >= 0 && got_packet) { // Write the compressed frame in the media file. - if (pkt.pts != (s64)AV_NOPTS_VALUE) - { - pkt.pts = av_rescale_q(pkt.pts, s_stream->codec->time_base, s_stream->time_base); + if (pkt.pts != (s64)AV_NOPTS_VALUE) { + pkt.pts = av_rescale_q(pkt.pts, s_codec_context->time_base, s_stream->time_base); } - if (pkt.dts != (s64)AV_NOPTS_VALUE) - { - pkt.dts = av_rescale_q(pkt.dts, s_stream->codec->time_base, s_stream->time_base); + if (pkt.dts != (s64)AV_NOPTS_VALUE) { + pkt.dts = av_rescale_q(pkt.dts, s_codec_context->time_base, s_stream->time_base); } #if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(56, 60, 100) - if (s_stream->codec->coded_frame->key_frame) + if (s_codec_context->coded_frame->key_frame) pkt.flags |= AV_PKT_FLAG_KEY; #endif pkt.stream_index = s_stream->index; av_interleaved_write_frame(s_format_context, &pkt); // Handle delayed frames. +#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 48, 101) + av_packet_unref(&pkt); + error = avcodec_receive_packet(s_codec_context, &pkt); + got_packet = error >= 0 ? 1 : 0; +#else PreparePacket(&pkt); - error = avcodec_encode_video2(s_stream->codec, &pkt, nullptr, &got_packet); + error = avcodec_encode_video2(s_codec_context, &pkt, nullptr, &got_packet); +#endif } - if (error) +#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 48, 101) + av_packet_unref(&pkt); + if (error < 0 && error != AVERROR(EAGAIN) && error != AVERROR_EOF) ERROR_LOG(G3D, "Error while encoding video: %d", error); +#else + if (error < 0) + ERROR_LOG(G3D, "Error while encoding video: %d", error); +#endif #endif delete[] flipbuffer; } @@ -246,18 +282,18 @@ void AVIDump::Stop() { void AVIDump::CloseFile() { #ifdef USE_FFMPEG - - if (s_stream) - { - if (s_stream->codec) - { + if (s_codec_context) { #if LIBAVCODEC_VERSION_MAJOR < 55 - avcodec_default_release_buffer(s_stream->codec, s_src_frame); + avcodec_default_release_buffer(s_codec_context, s_src_frame); +#endif +#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 48, 101) + avcodec_free_context(&s_codec_context); +#else + avcodec_close(s_codec_context); + s_codec_context = nullptr; #endif - avcodec_close(s_stream->codec); - } - av_freep(&s_stream); } + av_freep(&s_stream); av_frame_free(&s_src_frame); av_frame_free(&s_scaled_frame); diff --git a/Core/Compatibility.cpp b/Core/Compatibility.cpp index aa531e49d0..9cddc208d3 100644 --- a/Core/Compatibility.cpp +++ b/Core/Compatibility.cpp @@ -91,6 +91,7 @@ void Compatibility::CheckSettings(IniFile &iniFile, const std::string &gameID) { CheckSetting(iniFile, gameID, "ShaderColorBitmask", &flags_.ShaderColorBitmask); CheckSetting(iniFile, gameID, "DisableFirstFrameReadback", &flags_.DisableFirstFrameReadback); CheckSetting(iniFile, gameID, "DisableRangeCulling", &flags_.DisableRangeCulling); + CheckSetting(iniFile, gameID, "MpegAvcWarmUp", &flags_.MpegAvcWarmUp); } void Compatibility::CheckSetting(IniFile &iniFile, const std::string &gameID, const char *option, bool *flag) { diff --git a/Core/Compatibility.h b/Core/Compatibility.h index 94bfede4ec..1907a5cb33 100644 --- a/Core/Compatibility.h +++ b/Core/Compatibility.h @@ -89,6 +89,7 @@ struct CompatFlags { bool ShaderColorBitmask; bool DisableFirstFrameReadback; bool DisableRangeCulling; + bool MpegAvcWarmUp; }; class IniFile; diff --git a/Core/Config.cpp b/Core/Config.cpp index 5c7a1fff7f..2bad098cbc 100644 --- a/Core/Config.cpp +++ b/Core/Config.cpp @@ -513,7 +513,7 @@ static ConfigSetting generalSettings[] = { ConfigSetting("FullscreenOnDoubleclick", &g_Config.bFullscreenOnDoubleclick, true, false, false), ReportedConfigSetting("MemStickInserted", &g_Config.bMemStickInserted, true, true, true), - ConfigSetting("LoadPlugins", &g_Config.bLoadPlugins, false, true, true), + ConfigSetting("EnablePlugins", &g_Config.bLoadPlugins, true, true, true), ConfigSetting(false), }; @@ -524,12 +524,11 @@ static bool DefaultSasThread() { static ConfigSetting cpuSettings[] = { ReportedConfigSetting("CPUCore", &g_Config.iCpuCore, &DefaultCpuCore, true, true), - ConfigSetting("VulkanMultithreading", &g_Config.bVulkanMultithreading, false, true, true), ReportedConfigSetting("SeparateSASThread", &g_Config.bSeparateSASThread, &DefaultSasThread, true, true), ReportedConfigSetting("SeparateIOThread", &g_Config.bSeparateIOThread, true, true, true), ReportedConfigSetting("IOTimingMethod", &g_Config.iIOTimingMethod, IOTIMING_REALISTIC, true, true), ConfigSetting("FastMemoryAccess", &g_Config.bFastMemory, true, true, true), - ReportedConfigSetting("FuncReplacements", &g_Config.bFuncReplacements, true, true, true), + ReportedConfigSetting("FunctionReplacements", &g_Config.bFuncReplacements, true, true, true), ConfigSetting("HideSlowWarnings", &g_Config.bHideSlowWarnings, true, true, false), ConfigSetting("HideStateWarnings", &g_Config.bHideStateWarnings, false, true, false), ConfigSetting("PreloadFunctions", &g_Config.bPreloadFunctions, false, true, true), @@ -553,7 +552,7 @@ static int DefaultInternalResolution() { static int DefaultUnthrottleMode() { #if PPSSPP_PLATFORM(ANDROID) || defined(USING_QT_UI) || PPSSPP_PLATFORM(UWP) || PPSSPP_PLATFORM(IOS) - return (int)UnthrottleMode::SKIP_DRAW; + return (int)UnthrottleMode::SKIP_FLIP; #else return (int)UnthrottleMode::CONTINUOUS; #endif @@ -773,7 +772,7 @@ static ConfigSetting graphicsSettings[] = { ReportedConfigSetting("AutoFrameSkip", &g_Config.bAutoFrameSkip, false, true, true), ConfigSetting("FrameRate", &g_Config.iFpsLimit1, 0, true, true), ConfigSetting("FrameRate2", &g_Config.iFpsLimit2, -1, true, true), - ConfigSetting("UnthrottleMode", &g_Config.iUnthrottleMode, &DefaultUnthrottleMode, &UnthrottleModeToString, &UnthrottleModeFromString, true, true), + ConfigSetting("UnthrottlingMode", &g_Config.iUnthrottleMode, &DefaultUnthrottleMode, &UnthrottleModeToString, &UnthrottleModeFromString, true, true), #if defined(USING_WIN_UI) ConfigSetting("RestartRequired", &g_Config.bRestartRequired, false, false), #endif @@ -830,8 +829,6 @@ static ConfigSetting graphicsSettings[] = { ConfigSetting("InflightFrames", &g_Config.iInflightFrames, 3, true, false), ConfigSetting("RenderDuplicateFrames", &g_Config.bRenderDuplicateFrames, false, true, true), - ConfigSetting("ClearFramebuffersOnFirstUseHack", &g_Config.bClearFramebuffersOnFirstUseHack, false, true, true), - ConfigSetting(false), }; @@ -975,6 +972,8 @@ static ConfigSetting controlSettings[] = { ConfigSetting("MouseSensitivity", &g_Config.fMouseSensitivity, 0.1f, true, true), ConfigSetting("MouseSmoothing", &g_Config.fMouseSmoothing, 0.9f, true, true), + ConfigSetting("SystemControls", &g_Config.bSystemControls, true, true, false), + ConfigSetting(false), }; @@ -984,7 +983,7 @@ static ConfigSetting networkSettings[] = { ConfigSetting("proAdhocServer", &g_Config.proAdhocServer, "localhost", true, true), ConfigSetting("PortOffset", &g_Config.iPortOffset, 0, true, true), ConfigSetting("MinTimeout", &g_Config.iMinTimeout, 0, true, true), - ConfigSetting("TCPNoDelay", &g_Config.bTCPNoDelay, false, true, true), + ConfigSetting("TCPNoDelay", &g_Config.bTCPNoDelay, true, true, true), ConfigSetting("ForcedFirstConnect", &g_Config.bForcedFirstConnect, false, true, true), ConfigSetting("EnableUPnP", &g_Config.bEnableUPnP, false, true, true), ConfigSetting("UPnPUseOriginalPort", &g_Config.bUPnPUseOriginalPort, false, true, true), @@ -1004,7 +1003,7 @@ static ConfigSetting networkSettings[] = { static int DefaultPSPModel() { // TODO: Can probably default this on, but not sure about its memory differences. -#if !defined(_M_X64) && !defined(_WIN32) +#if !PPSSPP_ARCH(AMD64) && !defined(_WIN32) return PSP_MODEL_FAT; #else return PSP_MODEL_SLIM; @@ -1523,6 +1522,7 @@ void Config::AddRecent(const std::string &file) { // We'll add it back below. This makes sure it's at the front, and only once. RemoveRecent(file); + const std::string filename = File::ResolvePath(file); recentIsos.insert(recentIsos.begin(), filename); if ((int)recentIsos.size() > iMaxRecent) diff --git a/Core/Config.h b/Core/Config.h index 7a76699d87..1a1f234ae6 100644 --- a/Core/Config.h +++ b/Core/Config.h @@ -174,7 +174,6 @@ public: int iFrameSkipType; int iUnthrottleMode; // See UnthrottleMode in ConfigValues.h. bool bAutoFrameSkip; - bool bClearFramebuffersOnFirstUseHack; bool bEnableCardboardVR; // Cardboard Master Switch int iCardboardScreenSize; // Screen Size (in %) @@ -410,6 +409,8 @@ public: float fMouseSensitivity; float fMouseSmoothing; + bool bSystemControls; + // Use the hardware scaler to scale up the image to save fillrate. Similar to Windows' window size, really. int iAndroidHwScale; // 0 = device resolution. 1 = 480x272 (extended to correct aspect), 2 = 960x544 etc. @@ -487,6 +488,7 @@ public: bool bShowFrameProfiler; bool bSimpleFrameStats; + // Various directories. Autoconfigured, not read from ini. std::string currentDirectory; std::string externalDirectory; std::string memStickDirectory; diff --git a/Core/Core.cpp b/Core/Core.cpp index f3cdd9b624..e7d95c685a 100644 --- a/Core/Core.cpp +++ b/Core/Core.cpp @@ -460,17 +460,15 @@ void Core_ExecException(u32 address, u32 pc, ExecExceptionType type) { const char *desc = ExecExceptionTypeAsString(type); WARN_LOG(MEMMAP, "%s: Invalid destination %08x PC %08x LR %08x", desc, address, currentMIPS->pc, currentMIPS->r[MIPS_REG_RA]); - if (!g_Config.bIgnoreBadMemAccess) { - ExceptionInfo &e = g_exceptionInfo; - e = {}; - e.type = ExceptionType::BAD_EXEC_ADDR; - e.info = ""; - e.exec_type = type; - e.address = address; - e.pc = pc; - Core_EnableStepping(true); - host->SetDebugMode(true); - } + ExceptionInfo &e = g_exceptionInfo; + e = {}; + e.type = ExceptionType::BAD_EXEC_ADDR; + e.info = ""; + e.exec_type = type; + e.address = address; + e.pc = pc; + Core_EnableStepping(true); + host->SetDebugMode(true); } void Core_Break() { diff --git a/Core/Core.vcxproj b/Core/Core.vcxproj index 060eeb4ddb..6700a2a48e 100644 --- a/Core/Core.vcxproj +++ b/Core/Core.vcxproj @@ -139,7 +139,7 @@ Level3 ..\ffmpeg\WindowsInclude;..\ffmpeg\Windows\x86\include;../common;..;../ext/glew;../ext/snappy;../ext/libpng17;../ext/zlib;../ext - _CRTDBG_MAP_ALLOC;USING_WIN_UI;_CRT_SECURE_NO_WARNINGS;USE_FFMPEG;WIN32;_ARCH_32=1;_M_IX86=1;_DEBUG;_LIB;_UNICODE;UNICODE;MINIUPNP_STATICLIB;%(PreprocessorDefinitions) + _CRTDBG_MAP_ALLOC;USING_WIN_UI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;USE_FFMPEG;WIN32;_ARCH_32=1;_M_IX86=1;_DEBUG;_LIB;_UNICODE;UNICODE;MINIUPNP_STATICLIB;%(PreprocessorDefinitions) StreamingSIMDExtensions2 Precise true @@ -162,7 +162,7 @@ Level3 ..\ffmpeg\WindowsInclude;..\ffmpeg\Windows\x86_64\include;../common;..;../ext/glew;../ext/snappy;../ext/libpng17;../ext/zlib;../ext - _CRTDBG_MAP_ALLOC;USING_WIN_UI;_CRT_SECURE_NO_WARNINGS;USE_FFMPEG;WIN32;_ARCH_64=1;_M_X64=1;_DEBUG;_LIB;_UNICODE;UNICODE;MINIUPNP_STATICLIB;%(PreprocessorDefinitions) + _CRTDBG_MAP_ALLOC;USING_WIN_UI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;USE_FFMPEG;WIN32;_ARCH_64=1;_M_X64=1;_DEBUG;_LIB;_UNICODE;UNICODE;MINIUPNP_STATICLIB;%(PreprocessorDefinitions) NotSet Precise false @@ -186,7 +186,7 @@ Level3 ..\ffmpeg\WindowsInclude;..\ffmpeg\Windows\aarch64\include;../common;..;../ext/glew;../ext/snappy;../ext/libpng17;../ext/zlib;../ext - _CRTDBG_MAP_ALLOC;USING_WIN_UI;_CRT_SECURE_NO_WARNINGS;USE_FFMPEG;WIN32;_ARCH_64=1;_DEBUG;_LIB;_UNICODE;UNICODE;%(PreprocessorDefinitions) + _CRTDBG_MAP_ALLOC;USING_WIN_UI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;USE_FFMPEG;WIN32;_ARCH_64=1;_DEBUG;_LIB;_UNICODE;UNICODE;%(PreprocessorDefinitions) NotSet Precise false @@ -210,7 +210,7 @@ Level3 ..\ffmpeg\WindowsInclude;..\ffmpeg\Windows\arm\include;../common;..;../ext/glew;../ext/snappy;../ext/libpng17;../ext/zlib;../ext - _CRTDBG_MAP_ALLOC;USING_WIN_UI;_CRT_SECURE_NO_WARNINGS;USE_FFMPEG;WIN32;_ARCH_32=1;_DEBUG;_LIB;_UNICODE;UNICODE;%(PreprocessorDefinitions) + _CRTDBG_MAP_ALLOC;USING_WIN_UI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;USE_FFMPEG;WIN32;_ARCH_32=1;_DEBUG;_LIB;_UNICODE;UNICODE;%(PreprocessorDefinitions) NotSet Precise false @@ -241,7 +241,7 @@ false StreamingSIMDExtensions2 Precise - USING_WIN_UI;_CRT_SECURE_NO_WARNINGS;USE_FFMPEG;WIN32;_ARCH_32=1;_M_IX86=1;_LIB;NDEBUG;_UNICODE;UNICODE;MINIUPNP_STATICLIB;%(PreprocessorDefinitions) + USING_WIN_UI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;USE_FFMPEG;WIN32;_ARCH_32=1;_M_IX86=1;_LIB;NDEBUG;_UNICODE;UNICODE;MINIUPNP_STATICLIB;%(PreprocessorDefinitions) Speed MultiThreaded true @@ -276,7 +276,7 @@ Speed false true - USING_WIN_UI;_CRT_SECURE_NO_WARNINGS;USE_FFMPEG;WIN32;_ARCH_64=1;_M_X64=1;_LIB;NDEBUG;_UNICODE;UNICODE;MINIUPNP_STATICLIB;%(PreprocessorDefinitions) + USING_WIN_UI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;USE_FFMPEG;WIN32;_ARCH_64=1;_M_X64=1;_LIB;NDEBUG;_UNICODE;UNICODE;MINIUPNP_STATICLIB;%(PreprocessorDefinitions) false MultiThreaded true @@ -307,7 +307,7 @@ Speed false true - USING_WIN_UI;_CRT_SECURE_NO_WARNINGS;USE_FFMPEG;WIN32;_ARCH_64=1;_LIB;NDEBUG;_UNICODE;UNICODE;%(PreprocessorDefinitions) + USING_WIN_UI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;USE_FFMPEG;WIN32;_ARCH_64=1;_LIB;NDEBUG;_UNICODE;UNICODE;%(PreprocessorDefinitions) false MultiThreaded true @@ -338,7 +338,7 @@ Speed false true - USING_WIN_UI;_CRT_SECURE_NO_WARNINGS;USE_FFMPEG;WIN32;_ARCH_32=1;_LIB;NDEBUG;_UNICODE;UNICODE;%(PreprocessorDefinitions) + USING_WIN_UI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;USE_FFMPEG;WIN32;_ARCH_32=1;_LIB;NDEBUG;_UNICODE;UNICODE;%(PreprocessorDefinitions) false MultiThreaded true @@ -429,6 +429,7 @@ + @@ -457,6 +458,7 @@ + @@ -976,6 +978,7 @@ + @@ -1004,6 +1007,7 @@ + diff --git a/Core/Core.vcxproj.filters b/Core/Core.vcxproj.filters index 93b6b137ef..f66273cddd 100644 --- a/Core/Core.vcxproj.filters +++ b/Core/Core.vcxproj.filters @@ -88,6 +88,9 @@ {0201ff27-4fa8-4cf3-bbb9-accfdc0c7046} + + {678fa299-0ff7-4983-982d-2da47b52e238} + @@ -971,6 +974,12 @@ Debugger\WebSocket + + MIPS\fake + + + Debugger + @@ -1661,6 +1670,12 @@ Debugger\WebSocket + + MIPS\fake + + + Debugger + diff --git a/Core/CoreTiming.cpp b/Core/CoreTiming.cpp index ca30771568..d7c725d4f7 100644 --- a/Core/CoreTiming.cpp +++ b/Core/CoreTiming.cpp @@ -16,9 +16,10 @@ // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. #include -#include #include #include +#include +#include #include "Common/Profiler/Profiler.h" @@ -47,7 +48,11 @@ struct EventType { const char *name; }; -std::vector event_types; +static std::vector event_types; +// Only used during restore. +static std::set usedEventTypes; +static std::set restoredEventTypes; +static int nextEventTypeRestoreId = -1; struct BaseEvent { s64 time; @@ -160,31 +165,53 @@ void FreeTsEvent(Event* ev) allocatedTsEvents--; } -int RegisterEvent(const char *name, TimedCallback callback) -{ +int RegisterEvent(const char *name, TimedCallback callback) { + for (const auto &ty : event_types) { + if (!strcmp(ty.name, name)) { + _assert_msg_(false, "Event type %s already registered", name); + // Try to make sure it doesn't work so we notice for sure. + return -1; + } + } + + int id = (int)event_types.size(); event_types.push_back(EventType{ callback, name }); - return (int)event_types.size() - 1; + usedEventTypes.insert(id); + return id; } -void AntiCrashCallback(u64 userdata, int cyclesLate) -{ +void AntiCrashCallback(u64 userdata, int cyclesLate) { ERROR_LOG(SAVESTATE, "Savestate broken: an unregistered event was called."); Core_EnableStepping(true); } -void RestoreRegisterEvent(int event_type, const char *name, TimedCallback callback) -{ - _assert_msg_(event_type >= 0, "Invalid event type %d", event_type) - if (event_type >= (int) event_types.size()) - event_types.resize(event_type + 1, EventType{ AntiCrashCallback, "INVALID EVENT" }); - +void RestoreRegisterEvent(int &event_type, const char *name, TimedCallback callback) { + // Some old states have a duplicate restore, do our best to fix... + if (restoredEventTypes.count(event_type) != 0) + event_type = -1; + if (event_type == -1) + event_type = nextEventTypeRestoreId++; + if (event_type >= event_types.size()) { + // Give it any unused event id starting from the end. + // Older save states with messed up ids have gaps near the end. + for (int i = (int)event_types.size() - 1; i >= 0; --i) { + if (usedEventTypes.count(i) == 0) { + event_type = i; + break; + } + } + } + _assert_msg_(event_type >= 0 && event_type < event_types.size(), "Invalid event type %d", event_type); event_types[event_type] = EventType{ callback, name }; + usedEventTypes.insert(event_type); + restoredEventTypes.insert(event_type); } -void UnregisterAllEvents() -{ +void UnregisterAllEvents() { _dbg_assert_msg_(first == nullptr, "Unregistering events with events pending - this isn't good."); event_types.clear(); + usedEventTypes.clear(); + restoredEventTypes.clear(); } void Init() @@ -566,8 +593,7 @@ void ForceCheck() #endif } -void Advance() -{ +void Advance() { PROFILE_THIS_SCOPE("advance"); int cyclesExecuted = slicelength - currentMIPS->downcount; globalTimer += cyclesExecuted; @@ -577,17 +603,14 @@ void Advance() MoveEvents(); ProcessFifoWaitEvents(); - if (!first) - { + if (!first) { // This should never happen in PPSSPP. // WARN_LOG_REPORT(TIME, "WARNING - no events in queue. Setting currentMIPS->downcount to 10000"); if (slicelength < 10000) { slicelength += 10000; - currentMIPS->downcount += slicelength; + currentMIPS->downcount += 10000; } - } - else - { + } else { // Note that events can eat cycles as well. int target = (int)(first->time - globalTimer); if (target > MAX_SLICE_LENGTH) @@ -599,36 +622,31 @@ void Advance() } } -void LogPendingEvents() -{ +void LogPendingEvents() { Event *ptr = first; - while (ptr) - { + while (ptr) { //INFO_LOG(CPU, "PENDING: Now: %lld Pending: %lld Type: %d", globalTimer, ptr->time, ptr->type); ptr = ptr->next; } } -void Idle(int maxIdle) -{ +void Idle(int maxIdle) { int cyclesDown = currentMIPS->downcount; if (maxIdle != 0 && cyclesDown > maxIdle) cyclesDown = maxIdle; - if (first && cyclesDown > 0) - { + if (first && cyclesDown > 0) { int cyclesExecuted = slicelength - currentMIPS->downcount; int cyclesNextEvent = (int) (first->time - globalTimer); if (cyclesNextEvent < cyclesExecuted + cyclesDown) - { cyclesDown = cyclesNextEvent - cyclesExecuted; - // Now, now... no time machines, please. - if (cyclesDown < 0) - cyclesDown = 0; - } } + // Now, now... no time machines, please. + if (cyclesDown < 0) + cyclesDown = 0; + // VERBOSE_LOG(CPU, "Idle for %i cycles! (%f ms)", cyclesDown, cyclesDown / (float)(CPU_HZ * 0.001f)); idledCycles += cyclesDown; @@ -665,25 +683,39 @@ void Event_DoState(PointerWrap &p, BaseEvent *ev) Do(p, ev->time); Do(p, ev->userdata); Do(p, ev->type); + usedEventTypes.insert(ev->type); } void Event_DoStateOld(PointerWrap &p, BaseEvent *ev) { Do(p, *ev); + usedEventTypes.insert(ev->type); } -void DoState(PointerWrap &p) -{ +void DoState(PointerWrap &p) { std::lock_guard lk(externalEventLock); auto s = p.Section("CoreTiming", 1, 3); if (!s) return; - int n = (int) event_types.size(); + int n = (int)event_types.size(); + int current = n; Do(p, n); + if (n > current) { + WARN_LOG(SAVESTATE, "Savestate failure: more events than current (can't ever remove an event)"); + p.SetError(p.ERROR_FAILURE); + return; + } + // These (should) be filled in later by the modules. - event_types.resize(n, EventType{ AntiCrashCallback, "INVALID EVENT" }); + for (int i = 0; i < current; ++i) { + event_types[i].callback = AntiCrashCallback; + event_types[i].name = "INVALID EVENT"; + } + nextEventTypeRestoreId = n - 1; + usedEventTypes.clear(); + restoredEventTypes.clear(); if (s >= 3) { DoLinkedList(p, first, (Event **) NULL); diff --git a/Core/CoreTiming.h b/Core/CoreTiming.h index 4c5cb013c8..e0c7dba35d 100644 --- a/Core/CoreTiming.h +++ b/Core/CoreTiming.h @@ -86,7 +86,7 @@ namespace CoreTiming // Returns the event_type identifier. int RegisterEvent(const char *name, TimedCallback callback); // For save states. - void RestoreRegisterEvent(int event_type, const char *name, TimedCallback callback); + void RestoreRegisterEvent(int &event_type, const char *name, TimedCallback callback); void UnregisterAllEvents(); // userdata MAY NOT CONTAIN POINTERS. userdata might get written and reloaded from disk, diff --git a/Core/CwCheat.cpp b/Core/CwCheat.cpp index be1edd7a23..c86ef008b4 100644 --- a/Core/CwCheat.cpp +++ b/Core/CwCheat.cpp @@ -11,6 +11,7 @@ #include "Core/CwCheat.h" #include "Core/Config.h" #include "Core/Host.h" +#include "Core/MemMapHelpers.h" #include "Core/MIPS/MIPS.h" #include "Core/ELF/ParamSFO.h" #include "Core/System.h" @@ -248,16 +249,9 @@ static void __CheatStop() { static void __CheatStart() { __CheatStop(); - std::string realGameID = g_paramSFO.GetValueString("DISC_ID"); - std::string gameID = realGameID; - const std::string gamePath = PSP_CoreParameter().fileToStart; - const bool badGameSFO = realGameID.empty() || !g_paramSFO.GetValueInt("DISC_TOTAL"); - if (badGameSFO && gamePath.find("/PSP/GAME/") != std::string::npos) { - gameID = g_paramSFO.GenerateFakeID(gamePath); - } - - cheatEngine = new CWCheatEngine(gameID); + cheatEngine = new CWCheatEngine(g_paramSFO.GetDiscID()); // This only generates ini files on boot, let's leave homebrew ini file for UI. + std::string realGameID = g_paramSFO.GetValueString("DISC_ID"); if (!realGameID.empty()) { cheatEngine->CreateCheatFile(); } @@ -301,6 +295,8 @@ void __CheatShutdown() { void __CheatDoState(PointerWrap &p) { auto s = p.Section("CwCheat", 0, 2); if (!s) { + CheatEvent = -1; + CoreTiming::RestoreRegisterEvent(CheatEvent, "CheatEvent", &hleCheat); return; } @@ -785,9 +781,13 @@ CheatOperation CWCheatEngine::InterpretNextOp(const CheatCode &cheat, size_t &i) return InterpretNextCwCheat(cheat, i); else if (cheat.fmt == CheatCodeFormat::TEMPAR) return InterpretNextTempAR(cheat, i); - else - _assert_(false); - return { CheatOp::Invalid }; + else { + // This shouldn't happen, but apparently does: #14082 + // Either I'm missing a path or we have memory corruption. + // Not sure whether to log here though, feels like we could end up with a + // ton of logspam... + return { CheatOp::Invalid }; + } } void CWCheatEngine::ApplyMemoryOperator(const CheatOperation &op, uint32_t(*oper)(uint32_t, uint32_t)) { @@ -918,7 +918,7 @@ void CWCheatEngine::ExecuteOp(const CheatOperation &op, const CheatCode &cheat, InvalidateICache(op.addr, op.val); InvalidateICache(op.copyBytesFrom.destAddr, op.val); - Memory::MemcpyUnchecked(op.copyBytesFrom.destAddr, op.addr, op.val); + Memory::Memcpy(op.copyBytesFrom.destAddr, op.addr, op.val, "CwCheat"); } break; @@ -1100,7 +1100,7 @@ void CWCheatEngine::ExecuteOp(const CheatOperation &op, const CheatCode &cheat, if (Memory::IsValidRange(dstAddr, val) && Memory::IsValidRange(srcAddr, val)) { InvalidateICache(dstAddr, val); InvalidateICache(srcAddr, val); - Memory::MemcpyUnchecked(dstAddr, srcAddr, val); + Memory::Memcpy(dstAddr, srcAddr, val, "CwCheat"); } // Don't perform any further action. type = -1; diff --git a/Core/Debugger/Breakpoints.cpp b/Core/Debugger/Breakpoints.cpp index c988712be3..ddb2d2baf6 100644 --- a/Core/Debugger/Breakpoints.cpp +++ b/Core/Debugger/Breakpoints.cpp @@ -40,14 +40,15 @@ static std::mutex memCheckMutex_; std::vector CBreakPoints::memChecks_; std::vector CBreakPoints::cleanupMemChecks_; -void MemCheck::Log(u32 addr, bool write, int size, u32 pc) { +void MemCheck::Log(u32 addr, bool write, int size, u32 pc, const char *reason) { if (result & BREAK_ACTION_LOG) { + const char *type = write ? "Write" : "Read"; if (logFormat.empty()) { - NOTICE_LOG(MEMMAP, "CHK %s%i at %08x (%s), PC=%08x (%s)", write ? "Write" : "Read", size * 8, addr, g_symbolMap->GetDescription(addr).c_str(), pc, g_symbolMap->GetDescription(pc).c_str()); + NOTICE_LOG(MEMMAP, "CHK %s%i(%s) at %08x (%s), PC=%08x (%s)", type, size * 8, reason, addr, g_symbolMap->GetDescription(addr).c_str(), pc, g_symbolMap->GetDescription(pc).c_str()); } else { std::string formatted; CBreakPoints::EvaluateLogFormat(currentDebugMIPS, logFormat, formatted); - NOTICE_LOG(MEMMAP, "CHK %s%i at %08x: %s", write ? "Write" : "Read", size * 8, addr, formatted.c_str()); + NOTICE_LOG(MEMMAP, "CHK %s%i(%s) at %08x: %s", type, size * 8, reason, addr, formatted.c_str()); } } } @@ -62,10 +63,10 @@ BreakAction MemCheck::Apply(u32 addr, bool write, int size, u32 pc) { return BREAK_ACTION_IGNORE; } -BreakAction MemCheck::Action(u32 addr, bool write, int size, u32 pc) { +BreakAction MemCheck::Action(u32 addr, bool write, int size, u32 pc, const char *reason) { int mask = write ? MEMCHECK_WRITE : MEMCHECK_READ; if (cond & mask) { - Log(addr, write, size, pc); + Log(addr, write, size, pc, reason); if ((result & BREAK_ACTION_PAUSE) && coreState != CORE_POWERUP) { Core_EnableStepping(true); host->SetDebugMode(true); @@ -94,7 +95,7 @@ void MemCheck::JitBeforeAction(u32 addr, bool write, int size, u32 pc) { // We have to break to find out if it changed. Core_EnableStepping(true); } else { - Action(addr, write, size, pc); + Action(addr, write, size, pc, "CPU"); } } @@ -116,7 +117,7 @@ void MemCheck::JitCleanup(bool changed) return; if (changed) - Log(lastAddr, true, lastSize, lastPC); + Log(lastAddr, true, lastSize, lastPC, "CPU"); // Resume if it should not have gone to stepping, or if it did not change. if ((!(result & BREAK_ACTION_PAUSE) || !changed) && coreState == CORE_STEPPING) @@ -504,7 +505,7 @@ MemCheck *CBreakPoints::GetMemCheckLocked(u32 address, int size) { return 0; } -BreakAction CBreakPoints::ExecMemCheck(u32 address, bool write, int size, u32 pc) +BreakAction CBreakPoints::ExecMemCheck(u32 address, bool write, int size, u32 pc, const char *reason) { if (!anyMemChecks_) return BREAK_ACTION_IGNORE; @@ -514,7 +515,7 @@ BreakAction CBreakPoints::ExecMemCheck(u32 address, bool write, int size, u32 pc check->Apply(address, write, size, pc); auto copy = *check; guard.unlock(); - return copy.Action(address, write, size, pc); + return copy.Action(address, write, size, pc, reason); } return BREAK_ACTION_IGNORE; } @@ -547,7 +548,7 @@ BreakAction CBreakPoints::ExecOpMemCheck(u32 address, u32 pc) check->Apply(address, write, size, pc); auto copy = *check; guard.unlock(); - return copy.Action(address, write, size, pc); + return copy.Action(address, write, size, pc, "CPU"); } } return BREAK_ACTION_IGNORE; diff --git a/Core/Debugger/Breakpoints.h b/Core/Debugger/Breakpoints.h index bd3dd670e4..c2a7d81b6a 100644 --- a/Core/Debugger/Breakpoints.h +++ b/Core/Debugger/Breakpoints.h @@ -96,13 +96,13 @@ struct MemCheck { // Called on the stored memcheck (affects numHits, etc.) BreakAction Apply(u32 addr, bool write, int size, u32 pc); // Called on a copy. - BreakAction Action(u32 addr, bool write, int size, u32 pc); + BreakAction Action(u32 addr, bool write, int size, u32 pc, const char *reason); void JitBeforeApply(u32 addr, bool write, int size, u32 pc); void JitBeforeAction(u32 addr, bool write, int size, u32 pc); bool JitApplyChanged(); void JitCleanup(bool changed); - void Log(u32 addr, bool write, int size, u32 pc); + void Log(u32 addr, bool write, int size, u32 pc, const char *reason); bool IsEnabled() const { return (result & BREAK_ACTION_PAUSE) != 0; @@ -151,7 +151,7 @@ public: static bool GetMemCheck(u32 start, u32 end, MemCheck *check); static bool GetMemCheckInRange(u32 address, int size, MemCheck *check); - static BreakAction ExecMemCheck(u32 address, bool write, int size, u32 pc); + static BreakAction ExecMemCheck(u32 address, bool write, int size, u32 pc, const char *reason); static BreakAction ExecOpMemCheck(u32 address, u32 pc); // Executes memchecks but used by the jit. Cleanup finalizes after jit is done. diff --git a/Core/Debugger/DisassemblyManager.cpp b/Core/Debugger/DisassemblyManager.cpp index d05a0984b6..e113ef38d1 100644 --- a/Core/Debugger/DisassemblyManager.cpp +++ b/Core/Debugger/DisassemblyManager.cpp @@ -15,6 +15,7 @@ // Official git repository and contact information can be found at // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. +#include "ppsspp_config.h" #include #include #include @@ -43,7 +44,7 @@ bool isInInterval(u32 start, u32 size, u32 value) static HashType computeHash(u32 address, u32 size) { -#ifdef _M_X64 +#if PPSSPP_ARCH(AMD64) return XXH3_64bits(Memory::GetPointer(address), size); #else return XXH3_64bits(Memory::GetPointer(address), size) & 0xFFFFFFFF; @@ -302,7 +303,6 @@ u32 DisassemblyManager::getNthPreviousAddress(u32 address, int n) { DisassemblyEntry* entry = it->second; int oldLineNum = entry->getLineNum(address,true); - int oldNumLines = entry->getNumLines(); if (n <= oldLineNum) { return entry->getLineAddress(oldLineNum-n); @@ -420,7 +420,6 @@ int DisassemblyFunction::getLineNum(u32 address, bool findStart) int last = (int)lineAddresses.size() - 1; for (int i = 0; i < last; i++) { - u32 next = lineAddresses[i + 1]; if (lineAddresses[i] == address) return i; } diff --git a/Core/Debugger/DisassemblyManager.h b/Core/Debugger/DisassemblyManager.h index 44ae397f6c..51b1f1959e 100644 --- a/Core/Debugger/DisassemblyManager.h +++ b/Core/Debugger/DisassemblyManager.h @@ -17,12 +17,13 @@ #pragma once +#include "ppsspp_config.h" #include #include "Common/CommonTypes.h" #include "Core/Debugger/SymbolMap.h" #include "Core/MIPS/MIPSAnalyst.h" -#ifdef _M_X64 +#if PPSSPP_ARCH(AMD64) typedef u64 HashType; #else typedef u32 HashType; diff --git a/Core/Debugger/MemBlockInfo.cpp b/Core/Debugger/MemBlockInfo.cpp new file mode 100644 index 0000000000..ef3f1ec239 --- /dev/null +++ b/Core/Debugger/MemBlockInfo.cpp @@ -0,0 +1,438 @@ +// Copyright (c) 2021- 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 +#include "Common/Log.h" +#include "Common/Serialize/Serializer.h" +#include "Common/Serialize/SerializeFuncs.h" +#include "Core/CoreTiming.h" +#include "Core/Debugger/Breakpoints.h" +#include "Core/Debugger/MemBlockInfo.h" +#include "Core/MIPS/MIPS.h" + +class MemSlabMap { +public: + MemSlabMap(); + ~MemSlabMap(); + + bool Mark(uint32_t addr, uint32_t size, uint64_t ticks, uint32_t pc, bool allocated, const std::string &tag); + bool Find(MemBlockFlags flags, uint32_t addr, uint32_t size, std::vector &results); + void Reset(); + void DoState(PointerWrap &p); + +private: + struct Slab { + uint32_t start = 0; + uint32_t end = 0; + uint64_t ticks = 0; + uint32_t pc = 0; + bool allocated = false; + std::string tag; + Slab *prev = nullptr; + Slab *next = nullptr; + + void DoState(PointerWrap &p); + }; + + static constexpr uint32_t MAX_SIZE = 0x40000000; + static constexpr uint32_t SLICES = 16384; + static constexpr uint32_t SLICE_SIZE = MAX_SIZE / SLICES; + + Slab *FindSlab(uint32_t addr); + void Clear(); + // Returns the new slab after size. + Slab *Split(Slab *slab, uint32_t size); + void MergeAdjacent(Slab *slab); + bool Same(const Slab *a, const Slab *b) const; + void Merge(Slab *a, Slab *b); + void FillHeads(Slab *slab); + + Slab *first_ = nullptr; + Slab *lastFind_ = nullptr; + std::vector heads_; +}; + +struct PendingNotifyMem { + MemBlockFlags flags; + uint32_t start; + uint32_t size; + uint64_t ticks; + uint32_t pc; + std::string tag; +}; + +static constexpr size_t MAX_PENDING_NOTIFIES = 512; +static MemSlabMap allocMap; +static MemSlabMap suballocMap; +static MemSlabMap writeMap; +static MemSlabMap textureMap; +static std::vector pendingNotifies; +static std::mutex pendingMutex; + +MemSlabMap::MemSlabMap() { + Reset(); +} + +MemSlabMap::~MemSlabMap() { + Clear(); +} + +bool MemSlabMap::Mark(uint32_t addr, uint32_t size, uint64_t ticks, uint32_t pc, bool allocated, const std::string &tag) { + uint32_t end = addr + size; + Slab *slab = FindSlab(addr); + Slab *firstMatch = nullptr; + while (slab != nullptr && slab->start < end) { + if (slab->start < addr) + slab = Split(slab, addr - slab->start); + // Don't replace slab, the return is the after part. + if (slab->end > end) { + Split(slab, end - slab->start); + } + + slab->allocated = allocated; + if (pc != 0) { + slab->ticks = ticks; + slab->pc = pc; + } + if (!tag.empty()) + slab->tag = tag; + + // Move on to the next one. + if (firstMatch == nullptr) + firstMatch = slab; + slab = slab->next; + } + + if (firstMatch != nullptr) { + // This will merge all those blocks to one. + MergeAdjacent(firstMatch); + return true; + } + return false; +} + +bool MemSlabMap::Find(MemBlockFlags flags, uint32_t addr, uint32_t size, std::vector &results) { + uint32_t end = addr + size; + Slab *slab = FindSlab(addr); + bool found = false; + while (slab != nullptr && slab->start < end) { + if (slab->pc != 0 || !slab->tag.empty()) { + results.push_back({ flags, slab->start, slab->end - slab->start, slab->ticks, slab->pc, slab->tag, slab->allocated }); + found = true; + } + slab = slab->next; + } + return found; +} + +void MemSlabMap::Reset() { + Clear(); + + first_ = new Slab(); + first_->end = MAX_SIZE; + lastFind_ = first_; + + heads_.resize(SLICES, first_); +} + +void MemSlabMap::DoState(PointerWrap &p) { + auto s = p.Section("MemSlabMap", 1); + if (!s) + return; + + int count = 0; + if (p.mode == p.MODE_READ) { + Clear(); + Do(p, count); + + first_ = new Slab(); + first_->DoState(p); + lastFind_ = first_; + --count; + + heads_.resize(SLICES, nullptr); + FillHeads(first_); + + Slab *slab = first_; + for (int i = 0; i < count; ++i) { + slab->next = new Slab(); + slab->next->DoState(p); + + slab->next->prev = slab; + slab = slab->next; + + FillHeads(slab); + } + } else { + for (Slab *slab = first_; slab != nullptr; slab = slab->next) + ++count; + Do(p, count); + + first_->DoState(p); + --count; + + Slab *slab = first_; + for (int i = 0; i < count; ++i) { + slab->next->DoState(p); + slab = slab->next; + } + } +} + +void MemSlabMap::Slab::DoState(PointerWrap &p) { + auto s = p.Section("MemSlabMapSlab", 1); + if (!s) + return; + + Do(p, start); + Do(p, end); + Do(p, ticks); + Do(p, pc); + Do(p, allocated); + Do(p, tag); +} + +void MemSlabMap::Clear() { + Slab *s = first_; + while (s != nullptr) { + Slab *next = s->next; + delete s; + s = next; + } + first_ = nullptr; + lastFind_ = nullptr; + heads_.clear(); +} + +MemSlabMap::Slab *MemSlabMap::FindSlab(uint32_t addr) { + // Jump ahead using our index. + Slab *slab = heads_[addr / SLICE_SIZE]; + // We often move forward, so check the last find. + if (lastFind_->start > slab->start && lastFind_->start <= addr) + slab = lastFind_; + + while (slab != nullptr && slab->start <= addr) { + if (slab->end > addr) { + lastFind_ = slab; + return slab; + } + slab = slab->next; + } + return nullptr; +} + +MemSlabMap::Slab *MemSlabMap::Split(Slab *slab, uint32_t size) { + Slab *next = new Slab(); + next->start = slab->start + size; + next->end = slab->end; + next->ticks = slab->ticks; + next->pc = slab->pc; + next->allocated = slab->allocated; + next->tag = slab->tag; + next->prev = slab; + next->next = slab->next; + + slab->next = next; + if (next->next) + next->next->prev = next; + + // If the split is big, we might have to update our index. + FillHeads(next); + + slab->end = slab->start + size; + return next; +} + +void MemSlabMap::MergeAdjacent(Slab *slab) { + while (slab->next != nullptr && Same(slab, slab->next)) { + Merge(slab, slab->next); + } + while (slab->prev != nullptr && Same(slab, slab->prev)) { + Merge(slab, slab->prev); + } +} + +bool MemSlabMap::Same(const Slab *a, const Slab *b) const { + if (a->allocated != b->allocated) + return false; + if (a->pc != b->pc) + return false; + if (a->tag != b->tag) + return false; + return true; +} + +void MemSlabMap::Merge(Slab *a, Slab *b) { + if (a->next == b) { + _assert_(a->end == b->start); + a->end = b->end; + a->next = b->next; + + if (a->next) + a->next->prev = a; + } else if (a->prev == b) { + _assert_(b->end == a->start); + a->start = b->start; + a->prev = b->prev; + + if (a->prev) + a->prev->next = a; + else if (first_ == b) + first_ = a; + } else { + _assert_(false); + } + // Take over index entries b had. + FillHeads(a); + if (b->ticks > a->ticks) { + a->ticks = b->ticks; + // In case we ignore PC for same. + a->pc = b->pc; + } + if (lastFind_ == b) + lastFind_ = a; + delete b; +} + +void MemSlabMap::FillHeads(Slab *slab) { + uint32_t slice = slab->start / SLICE_SIZE; + uint32_t endSlice = (slab->end - 1) / SLICE_SIZE; + + // For the first slice, only replace if it's the one we're removing. + if (slab->start == slice * SLICE_SIZE) { + heads_[slice] = slab; + } + + // Now replace all the rest - we definitely cover the start of them. + for (uint32_t i = slice + 1; i <= endSlice; ++i) { + heads_[i] = slab; + } +} + +void FlushPendingMemInfo() { + std::lock_guard guard(pendingMutex); + for (auto info : pendingNotifies) { + if (info.flags & MemBlockFlags::ALLOC) { + allocMap.Mark(info.start, info.size, info.ticks, info.pc, true, info.tag); + } else if (info.flags & MemBlockFlags::FREE) { + // Maintain the previous allocation tag for debugging. + allocMap.Mark(info.start, info.size, info.ticks, 0, false, ""); + suballocMap.Mark(info.start, info.size, info.ticks, 0, false, ""); + } + if (info.flags & MemBlockFlags::SUB_ALLOC) { + suballocMap.Mark(info.start, info.size, info.ticks, info.pc, true, info.tag); + } else if (info.flags & MemBlockFlags::SUB_FREE) { + // Maintain the previous allocation tag for debugging. + suballocMap.Mark(info.start, info.size, info.ticks, 0, false, ""); + } + if (info.flags & MemBlockFlags::TEXTURE) { + textureMap.Mark(info.start, info.size, info.ticks, info.pc, true, info.tag); + } + if (info.flags & MemBlockFlags::WRITE) { + writeMap.Mark(info.start, info.size, info.ticks, info.pc, true, info.tag); + } + } + pendingNotifies.clear(); +} + +void NotifyMemInfo(MemBlockFlags flags, uint32_t start, uint32_t size, const std::string &tag) { + NotifyMemInfoPC(flags, start, size, currentMIPS->pc, tag); +} + +void NotifyMemInfoPC(MemBlockFlags flags, uint32_t start, uint32_t size, uint32_t pc, const std::string &tag) { + if (size == 0) { + return; + } + // Clear the uncached and kernel bits. + start &= ~0xC0000000; + + PendingNotifyMem info{ flags, start, size }; + info.ticks = CoreTiming::GetTicks(); + info.pc = pc; + info.tag = tag; + + bool needFlush = false; + { + std::lock_guard guard(pendingMutex); + pendingNotifies.push_back(info); + needFlush = pendingNotifies.size() > MAX_PENDING_NOTIFIES; + } + + if (needFlush) { + FlushPendingMemInfo(); + } + + if (flags & MemBlockFlags::WRITE) { + CBreakPoints::ExecMemCheck(start, true, size, pc, tag.c_str()); + } else if (flags & MemBlockFlags::READ) { + CBreakPoints::ExecMemCheck(start, false, size, pc, tag.c_str()); + } +} + +std::vector FindMemInfo(uint32_t start, uint32_t size) { + FlushPendingMemInfo(); + start &= ~0xC0000000; + + std::vector results; + allocMap.Find(MemBlockFlags::ALLOC, start, size, results); + suballocMap.Find(MemBlockFlags::SUB_ALLOC, start, size, results); + writeMap.Find(MemBlockFlags::WRITE, start, size, results); + textureMap.Find(MemBlockFlags::TEXTURE, start, size, results); + return results; +} + +std::vector FindMemInfoByFlag(MemBlockFlags flags, uint32_t start, uint32_t size) { + FlushPendingMemInfo(); + start &= ~0xC0000000; + + std::vector results; + if (flags & MemBlockFlags::ALLOC) + allocMap.Find(MemBlockFlags::ALLOC, start, size, results); + if (flags & MemBlockFlags::SUB_ALLOC) + suballocMap.Find(MemBlockFlags::SUB_ALLOC, start, size, results); + if (flags & MemBlockFlags::WRITE) + writeMap.Find(MemBlockFlags::WRITE, start, size, results); + if (flags & MemBlockFlags::TEXTURE) + textureMap.Find(MemBlockFlags::TEXTURE, start, size, results); + return results; +} + +void MemBlockInfoInit() { + std::lock_guard guard(pendingMutex); + pendingNotifies.reserve(MAX_PENDING_NOTIFIES); +} + +void MemBlockInfoShutdown() { + std::lock_guard guard(pendingMutex); + allocMap.Reset(); + suballocMap.Reset(); + writeMap.Reset(); + textureMap.Reset(); + pendingNotifies.clear(); +} + +void MemBlockInfoDoState(PointerWrap &p) { + auto s = p.Section("MemBlockInfo", 0, 1); + if (!s) + return; + + FlushPendingMemInfo(); + allocMap.DoState(p); + suballocMap.DoState(p); + writeMap.DoState(p); + textureMap.DoState(p); +} diff --git a/Core/Debugger/MemBlockInfo.h b/Core/Debugger/MemBlockInfo.h new file mode 100644 index 0000000000..fa851ffdfe --- /dev/null +++ b/Core/Debugger/MemBlockInfo.h @@ -0,0 +1,57 @@ +// Copyright (c) 2021- 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 +#include "Common/Common.h" + +class PointerWrap; + +enum class MemBlockFlags { + ALLOC = 0x0001, + SUB_ALLOC = 0x0002, + WRITE = 0x0004, + TEXTURE = 0x0008, + // Not actually logged. + READ = 0x0800, + FREE = 0x1000, + SUB_FREE = 0x2000, +}; +ENUM_CLASS_BITOPS(MemBlockFlags); + +struct MemBlockInfo { + MemBlockFlags flags; + uint32_t start; + uint32_t size; + uint64_t ticks; + uint32_t pc; + std::string tag; + bool allocated; +}; + +void NotifyMemInfo(MemBlockFlags flags, uint32_t start, uint32_t size, const std::string &tag); +void NotifyMemInfoPC(MemBlockFlags flags, uint32_t start, uint32_t size, uint32_t pc, const std::string &tag); + +std::vector FindMemInfo(uint32_t start, uint32_t size); +std::vector FindMemInfoByFlag(MemBlockFlags flags, uint32_t start, uint32_t size); + +void MemBlockInfoInit(); +void MemBlockInfoShutdown(); +void MemBlockInfoDoState(PointerWrap &p); diff --git a/Core/Debugger/SymbolMap.cpp b/Core/Debugger/SymbolMap.cpp index 0efd301b0e..772f5ca1b5 100644 --- a/Core/Debugger/SymbolMap.cpp +++ b/Core/Debugger/SymbolMap.cpp @@ -21,6 +21,7 @@ #pragma optimize("gty", on) #endif +#include "ppsspp_config.h" #ifdef _WIN32 #include "Common/CommonWindows.h" #include @@ -1045,7 +1046,6 @@ void SymbolMap::FillSymbolListBox(HWND listbox,SymbolType symType) { SendMessage(listbox, LB_INITSTORAGE, (WPARAM)activeFunctions.size(), (LPARAM)activeFunctions.size() * 30); for (auto it = activeFunctions.begin(), end = activeFunctions.end(); it != end; ++it) { - const FunctionEntry& entry = it->second; const char* name = GetLabelName(it->first); if (name != NULL) wsprintf(temp, L"%S", name); @@ -1069,7 +1069,6 @@ void SymbolMap::FillSymbolListBox(HWND listbox,SymbolType symType) { } for (auto it = activeData.begin(), end = activeData.end(); it != end; ++it) { - const DataEntry& entry = it->second; const char* name = GetLabelName(it->first); if (name != NULL) diff --git a/Core/Debugger/WebSocket/MemorySubscriber.cpp b/Core/Debugger/WebSocket/MemorySubscriber.cpp index 8adc6fec5b..2d7741fad8 100644 --- a/Core/Debugger/WebSocket/MemorySubscriber.cpp +++ b/Core/Debugger/WebSocket/MemorySubscriber.cpp @@ -19,6 +19,8 @@ #include #include "Common/Data/Encoding/Base64.h" #include "Common/StringUtils.h" +#include "Core/Core.h" +#include "Core/HLE/ReplaceTables.h" #include "Core/MemMap.h" #include "Core/MIPS/MIPSDebugInterface.h" #include "Core/System.h" @@ -40,6 +42,48 @@ DebuggerSubscriber *WebSocketMemoryInit(DebuggerEventHandlerMap &map) { return nullptr; } +struct AutoDisabledReplacements { + ~AutoDisabledReplacements(); + + Memory::MemoryInitedLock *lock = nullptr; + std::map replacements; + std::vector emuhacks; + bool saved = false; + bool wasStepping = false; +}; + +// Important: Only use keepReplacements when reading, not writing. +static AutoDisabledReplacements LockMemoryAndCPU(bool keepReplacements) { + AutoDisabledReplacements result; + if (Core_IsStepping()) { + result.wasStepping = true; + } else { + Core_EnableStepping(true); + Core_WaitInactive(); + } + + result.lock = new Memory::MemoryInitedLock(); + if (!keepReplacements) { + result.saved = true; + // Okay, save so we can restore later. + result.replacements = SaveAndClearReplacements(); + if (MIPSComp::jit) + result.emuhacks = MIPSComp::jit->SaveAndClearEmuHackOps(); + } + return result; +} + +AutoDisabledReplacements::~AutoDisabledReplacements() { + if (saved) { + if (MIPSComp::jit) + MIPSComp::jit->RestoreSavedEmuHackOps(emuhacks); + RestoreSavedReplacements(replacements); + } + if (!wasStepping) + Core_EnableStepping(false); + delete lock; +} + // Read a byte from memory (memory.read_u8) // // Parameters: @@ -48,7 +92,7 @@ DebuggerSubscriber *WebSocketMemoryInit(DebuggerEventHandlerMap &map) { // Response (same event name): // - value: unsigned integer void WebSocketMemoryReadU8(DebuggerRequest &req) { - auto memLock = Memory::Lock(); + auto memLock = LockMemoryAndCPU(true); if (!currentDebugMIPS->isAlive() || !Memory::IsActive()) return req.Fail("CPU not started"); @@ -74,7 +118,7 @@ void WebSocketMemoryReadU8(DebuggerRequest &req) { // Response (same event name): // - value: unsigned integer void WebSocketMemoryReadU16(DebuggerRequest &req) { - auto memLock = Memory::Lock(); + auto memLock = LockMemoryAndCPU(true); if (!currentDebugMIPS->isAlive() || !Memory::IsActive()) return req.Fail("CPU not started"); @@ -100,7 +144,7 @@ void WebSocketMemoryReadU16(DebuggerRequest &req) { // Response (same event name): // - value: unsigned integer void WebSocketMemoryReadU32(DebuggerRequest &req) { - auto memLock = Memory::Lock(); + auto memLock = LockMemoryAndCPU(true); if (!currentDebugMIPS->isAlive() || !Memory::IsActive()) return req.Fail("CPU not started"); @@ -123,20 +167,24 @@ void WebSocketMemoryReadU32(DebuggerRequest &req) { // Parameters: // - address: unsigned integer address for the start of the memory range. // - size: unsigned integer specifying size of memory range. +// - replacements: optional, false to ignore PPSSPP replacements in MIPS code. // // Response (same event name): // - base64: base64 encode of binary data. void WebSocketMemoryRead(DebuggerRequest &req) { - auto memLock = Memory::Lock(); - if (!currentDebugMIPS->isAlive() || !Memory::IsActive()) - return req.Fail("CPU not started"); - uint32_t addr; if (!req.ParamU32("address", &addr)) return; uint32_t size; if (!req.ParamU32("size", &size)) return; + bool replacements = true; + if (!req.ParamBool("replacements", &replacements, DebuggerParamType::OPTIONAL)) + return; + + auto memLock = LockMemoryAndCPU(replacements); + if (!currentDebugMIPS->isAlive() || !Memory::IsActive()) + return req.Fail("CPU not started"); if (!Memory::IsValidAddress(addr)) return req.Fail("Invalid address"); @@ -171,7 +219,7 @@ void WebSocketMemoryRead(DebuggerRequest &req) { // Response (same event name) for 'base64': // - base64: base64 encode of binary data, not including NUL. void WebSocketMemoryReadString(DebuggerRequest &req) { - auto memLock = Memory::Lock(); + auto memLock = LockMemoryAndCPU(true); if (!currentDebugMIPS->isAlive() || !Memory::IsActive()) return req.Fail("CPU not started"); @@ -209,7 +257,7 @@ void WebSocketMemoryReadString(DebuggerRequest &req) { // Response (same event name): // - value: new value, unsigned integer void WebSocketMemoryWriteU8(DebuggerRequest &req) { - auto memLock = Memory::Lock(); + auto memLock = LockMemoryAndCPU(true); if (!currentDebugMIPS->isAlive() || !Memory::IsActive()) return req.Fail("CPU not started"); @@ -225,6 +273,7 @@ void WebSocketMemoryWriteU8(DebuggerRequest &req) { req.Fail("Invalid address"); return; } + currentMIPS->InvalidateICache(addr, 1); Memory::Write_U8(val, addr); JsonWriter &json = req.Respond(); @@ -240,7 +289,7 @@ void WebSocketMemoryWriteU8(DebuggerRequest &req) { // Response (same event name): // - value: new value, unsigned integer void WebSocketMemoryWriteU16(DebuggerRequest &req) { - auto memLock = Memory::Lock(); + auto memLock = LockMemoryAndCPU(true); if (!currentDebugMIPS->isAlive() || !Memory::IsActive()) return req.Fail("CPU not started"); @@ -256,6 +305,7 @@ void WebSocketMemoryWriteU16(DebuggerRequest &req) { req.Fail("Invalid address"); return; } + currentMIPS->InvalidateICache(addr, 2); Memory::Write_U16(val, addr); JsonWriter &json = req.Respond(); @@ -271,7 +321,7 @@ void WebSocketMemoryWriteU16(DebuggerRequest &req) { // Response (same event name): // - value: new value, unsigned integer void WebSocketMemoryWriteU32(DebuggerRequest &req) { - auto memLock = Memory::Lock(); + auto memLock = LockMemoryAndCPU(true); if (!currentDebugMIPS->isAlive() || !Memory::IsActive()) return req.Fail("CPU not started"); @@ -287,6 +337,7 @@ void WebSocketMemoryWriteU32(DebuggerRequest &req) { req.Fail("Invalid address"); return; } + currentMIPS->InvalidateICache(addr, 4); Memory::Write_U32(val, addr); JsonWriter &json = req.Respond(); @@ -301,7 +352,7 @@ void WebSocketMemoryWriteU32(DebuggerRequest &req) { // // Response (same event name) with no extra data. void WebSocketMemoryWrite(DebuggerRequest &req) { - auto memLock = Memory::Lock(); + auto memLock = LockMemoryAndCPU(true); if (!currentDebugMIPS->isAlive() || !Memory::IsActive()) return req.Fail("CPU not started"); @@ -320,6 +371,7 @@ void WebSocketMemoryWrite(DebuggerRequest &req) { else if (value.size() != (size_t)size || !Memory::IsValidRange(addr, size)) return req.Fail("Invalid size"); + currentMIPS->InvalidateICache(addr, size); Memory::MemcpyUnchecked(addr, &value[0], size); req.Respond(); } diff --git a/Core/Dialog/PSPDialog.cpp b/Core/Dialog/PSPDialog.cpp index 561b818684..f15ea6da63 100644 --- a/Core/Dialog/PSPDialog.cpp +++ b/Core/Dialog/PSPDialog.cpp @@ -21,28 +21,37 @@ #include "Common/Serialize/SerializeFuncs.h" #include "Common/StringUtils.h" #include "Core/CoreTiming.h" -#include "Core/HLE/sceCtrl.h" -#include "Core/Util/PPGeDraw.h" #include "Core/Dialog/PSPDialog.h" +#include "Core/HLE/sceCtrl.h" +#include "Core/HLE/scePower.h" +#include "Core/HLE/sceUtility.h" +#include "Core/MemMapHelpers.h" +#include "Core/Util/PPGeDraw.h" #define FADE_TIME 1.0 const float FONT_SCALE = 0.55f; -PSPDialog::PSPDialog() - : status(SCE_UTILITY_STATUS_NONE), pendingStatus(SCE_UTILITY_STATUS_NONE), - pendingStatusTicks(0), lastButtons(0), buttons(0) -{ - +PSPDialog::PSPDialog(UtilityDialogType type) : dialogType_(type) { } PSPDialog::~PSPDialog() { } -PSPDialog::DialogStatus PSPDialog::GetStatus() -{ +PSPDialog::DialogStatus PSPDialog::GetStatus() { if (pendingStatusTicks != 0 && CoreTiming::GetTicks() >= pendingStatusTicks) { - status = pendingStatus; - pendingStatusTicks = 0; + bool changeAllowed = true; + if (pendingStatus == SCE_UTILITY_STATUS_NONE && status == SCE_UTILITY_STATUS_SHUTDOWN) { + FinishVolatile(); + } else if (pendingStatus == SCE_UTILITY_STATUS_RUNNING && status == SCE_UTILITY_STATUS_INITIALIZE) { + if (!volatileLocked_) { + volatileLocked_ = KernelVolatileMemLock(0, 0, 0) == 0; + changeAllowed = volatileLocked_; + } + } + if (changeAllowed) { + status = pendingStatus; + pendingStatusTicks = 0; + } } PSPDialog::DialogStatus retval = status; @@ -57,6 +66,14 @@ PSPDialog::DialogStatus PSPDialog::GetStatus() void PSPDialog::ChangeStatus(DialogStatus newStatus, int delayUs) { if (delayUs <= 0) { + if (newStatus == SCE_UTILITY_STATUS_NONE && status == SCE_UTILITY_STATUS_SHUTDOWN) { + FinishVolatile(); + } else if (newStatus == SCE_UTILITY_STATUS_RUNNING && status == SCE_UTILITY_STATUS_INITIALIZE) { + if (!volatileLocked_) { + // TODO: Should probably make the status pending instead? + volatileLocked_ = KernelVolatileMemLock(0, 0, 0) == 0; + } + } status = newStatus; pendingStatusTicks = 0; } else { @@ -65,6 +82,24 @@ void PSPDialog::ChangeStatus(DialogStatus newStatus, int delayUs) { } } +void PSPDialog::FinishVolatile() { + if (!volatileLocked_) + return; + + if (KernelVolatileMemUnlock(0) == 0) { + volatileLocked_ = false; + // Simulate modifications to volatile memory. + Memory::Memset(PSP_GetVolatileMemoryStart(), 0, PSP_GetVolatileMemoryEnd() - PSP_GetVolatileMemoryStart()); + } +} + +int PSPDialog::FinishShutdown() { + if (ReadStatus() != SCE_UTILITY_STATUS_SHUTDOWN) + return -1; + ChangeStatus(SCE_UTILITY_STATUS_NONE, 0); + return 0; +} + void PSPDialog::ChangeStatusInit(int delayUs) { status = SCE_UTILITY_STATUS_INITIALIZE; ChangeStatus(SCE_UTILITY_STATUS_RUNNING, delayUs); @@ -72,7 +107,11 @@ void PSPDialog::ChangeStatusInit(int delayUs) { void PSPDialog::ChangeStatusShutdown(int delayUs) { status = SCE_UTILITY_STATUS_SHUTDOWN; - ChangeStatus(SCE_UTILITY_STATUS_NONE, delayUs); + auto params = GetCommonParam(); + if (params) + UtilityDialogShutdown(DialogType(), delayUs, params->accessThread); + else + ChangeStatus(SCE_UTILITY_STATUS_NONE, delayUs); } void PSPDialog::StartDraw() @@ -132,9 +171,8 @@ u32 PSPDialog::CalcFadedColor(u32 inColor) return (inColor & 0x00FFFFFF) | (alpha << 24); } -void PSPDialog::DoState(PointerWrap &p) -{ - auto s = p.Section("PSPDialog", 1, 2); +void PSPDialog::DoState(PointerWrap &p) { + auto s = p.Section("PSPDialog", 1, 3); if (!s) return; @@ -161,6 +199,12 @@ void PSPDialog::DoState(PointerWrap &p) } else { pendingStatusTicks = 0; } + + if (s >= 3) { + Do(p, volatileLocked_); + } else { + volatileLocked_ = false; + } } pspUtilityDialogCommon *PSPDialog::GetCommonParam() diff --git a/Core/Dialog/PSPDialog.h b/Core/Dialog/PSPDialog.h index 5aaeea0dcc..e340314d18 100644 --- a/Core/Dialog/PSPDialog.h +++ b/Core/Dialog/PSPDialog.h @@ -17,11 +17,11 @@ #pragma once -#include "Common/Render/TextureAtlas.h" - #include "Common/Common.h" #include "Common/CommonTypes.h" +#include "Common/Render/TextureAtlas.h" #include "Common/Swap.h" +#include "Core/HLE/sceUtility.h" #include "Core/Util/PPGeDraw.h" class PointerWrap; @@ -53,7 +53,7 @@ struct pspUtilityDialogCommon class PSPDialog { public: - PSPDialog(); + PSPDialog(UtilityDialogType type); virtual ~PSPDialog(); virtual int Update(int animSpeed) = 0; @@ -80,9 +80,14 @@ public: }; DialogStatus GetStatus(); + UtilityDialogType DialogType() { return dialogType_; } void StartDraw(); void EndDraw(); + + void FinishVolatile(); + int FinishShutdown(); + protected: PPGeStyle FadedStyle(PPGeAlign align, float scale); PPGeImageStyle FadedImageStyle(); @@ -94,23 +99,23 @@ protected: void ChangeStatus(DialogStatus newStatus, int delayUs); void ChangeStatusInit(int delayUs); void ChangeStatusShutdown(int delayUs); + DialogStatus ReadStatus() { + return status; + } // TODO: Remove this once all dialogs are updated. - virtual bool UseAutoStatus() { - return true; - } + virtual bool UseAutoStatus() = 0; void StartFade(bool fadeIn_); void UpdateFade(int animSpeed); virtual void FinishFadeOut(); u32 CalcFadedColor(u32 inColor); - DialogStatus status; - DialogStatus pendingStatus; - u64 pendingStatusTicks; + DialogStatus pendingStatus = SCE_UTILITY_STATUS_NONE; + u64 pendingStatusTicks = 0; - unsigned int lastButtons; - unsigned int buttons; + unsigned int lastButtons = 0; + unsigned int buttons = 0; float fadeTimer; bool isFading; @@ -121,4 +126,9 @@ protected: ImageID cancelButtonImg; int okButtonFlag; int cancelButtonFlag; + +private: + DialogStatus status = SCE_UTILITY_STATUS_NONE; + UtilityDialogType dialogType_; + bool volatileLocked_ = false; }; diff --git a/Core/Dialog/PSPGamedataInstallDialog.cpp b/Core/Dialog/PSPGamedataInstallDialog.cpp index 319314fd20..894a441479 100644 --- a/Core/Dialog/PSPGamedataInstallDialog.cpp +++ b/Core/Dialog/PSPGamedataInstallDialog.cpp @@ -55,7 +55,7 @@ namespace } } -PSPGamedataInstallDialog::PSPGamedataInstallDialog() { +PSPGamedataInstallDialog::PSPGamedataInstallDialog(UtilityDialogType type) : PSPDialog(type) { } PSPGamedataInstallDialog::~PSPGamedataInstallDialog() { diff --git a/Core/Dialog/PSPGamedataInstallDialog.h b/Core/Dialog/PSPGamedataInstallDialog.h index 5c9662e24e..3dfd8687ef 100644 --- a/Core/Dialog/PSPGamedataInstallDialog.h +++ b/Core/Dialog/PSPGamedataInstallDialog.h @@ -35,7 +35,7 @@ struct SceUtilityGamedataInstallParam { class PSPGamedataInstallDialog: public PSPDialog { public: - PSPGamedataInstallDialog(); + PSPGamedataInstallDialog(UtilityDialogType type); virtual ~PSPGamedataInstallDialog(); virtual int Init(u32 paramAddr); @@ -46,6 +46,12 @@ public: int Abort(); std::string GetGameDataInstallFileName(SceUtilityGamedataInstallParam *param, std::string filename); +protected: + // TODO: Manage status correctly. + bool UseAutoStatus() override { + return true; + } + private: void UpdateProgress(); void OpenNextFile(); diff --git a/Core/Dialog/PSPMsgDialog.cpp b/Core/Dialog/PSPMsgDialog.cpp index 8361adbc3e..324a40c5e7 100755 --- a/Core/Dialog/PSPMsgDialog.cpp +++ b/Core/Dialog/PSPMsgDialog.cpp @@ -35,10 +35,7 @@ static const float FONT_SCALE = 0.65f; const static int MSG_INIT_DELAY_US = 300000; const static int MSG_SHUTDOWN_DELAY_US = 26000; -PSPMsgDialog::PSPMsgDialog() - : PSPDialog() - , flag(0) -{ +PSPMsgDialog::PSPMsgDialog(UtilityDialogType type) : PSPDialog(type) { } PSPMsgDialog::~PSPMsgDialog() { @@ -344,7 +341,7 @@ int PSPMsgDialog::Update(int animSpeed) { messageDialog.result = 0; } - Memory::Memcpy(messageDialogAddr, &messageDialog ,messageDialog.common.size); + Memory::Memcpy(messageDialogAddr, &messageDialog, messageDialog.common.size, "MsgDialogParam"); return 0; } diff --git a/Core/Dialog/PSPMsgDialog.h b/Core/Dialog/PSPMsgDialog.h index 78490393f8..e5912d2ee3 100644 --- a/Core/Dialog/PSPMsgDialog.h +++ b/Core/Dialog/PSPMsgDialog.h @@ -58,7 +58,7 @@ struct pspMessageDialog class PSPMsgDialog: public PSPDialog { public: - PSPMsgDialog(); + PSPMsgDialog(UtilityDialogType type); virtual ~PSPMsgDialog(); virtual int Init(unsigned int paramAddr); @@ -92,7 +92,7 @@ private: DS_ABORT = 0x200, }; - u32 flag; + u32 flag = 0; pspMessageDialog messageDialog; int messageDialogAddr; diff --git a/Core/Dialog/PSPNetconfDialog.cpp b/Core/Dialog/PSPNetconfDialog.cpp index b7126d3348..4202f905af 100644 --- a/Core/Dialog/PSPNetconfDialog.cpp +++ b/Core/Dialog/PSPNetconfDialog.cpp @@ -54,7 +54,7 @@ struct ScanInfos { } PACK; -PSPNetconfDialog::PSPNetconfDialog() { +PSPNetconfDialog::PSPNetconfDialog(UtilityDialogType type) : PSPDialog(type) { } PSPNetconfDialog::~PSPNetconfDialog() { @@ -62,7 +62,7 @@ PSPNetconfDialog::~PSPNetconfDialog() { int PSPNetconfDialog::Init(u32 paramAddr) { // Already running - if (status != SCE_UTILITY_STATUS_NONE) + if (ReadStatus() != SCE_UTILITY_STATUS_NONE) return SCE_ERROR_UTILITY_INVALID_STATUS; requestAddr = paramAddr; @@ -465,13 +465,13 @@ int PSPNetconfDialog::Update(int animSpeed) { } if (GetStatus() == SCE_UTILITY_STATUS_FINISHED || pendingStatus == SCE_UTILITY_STATUS_FINISHED) - Memory::Memcpy(requestAddr, &request, request.common.size); + Memory::Memcpy(requestAddr, &request, request.common.size, "NetConfDialogParam"); return 0; } int PSPNetconfDialog::Shutdown(bool force) { - if (status != SCE_UTILITY_STATUS_FINISHED && !force) + if (ReadStatus() != SCE_UTILITY_STATUS_FINISHED && !force) return SCE_ERROR_UTILITY_INVALID_STATUS; PSPDialog::Shutdown(force); diff --git a/Core/Dialog/PSPNetconfDialog.h b/Core/Dialog/PSPNetconfDialog.h index e02ebae5a0..13d267f5d9 100644 --- a/Core/Dialog/PSPNetconfDialog.h +++ b/Core/Dialog/PSPNetconfDialog.h @@ -37,7 +37,7 @@ struct SceUtilityNetconfParam { class PSPNetconfDialog: public PSPDialog { public: - PSPNetconfDialog(); + PSPNetconfDialog(UtilityDialogType type); virtual ~PSPNetconfDialog(); virtual int Init(u32 paramAddr); diff --git a/Core/Dialog/PSPOskDialog.cpp b/Core/Dialog/PSPOskDialog.cpp index c049225f02..fd2cd25252 100755 --- a/Core/Dialog/PSPOskDialog.cpp +++ b/Core/Dialog/PSPOskDialog.cpp @@ -15,6 +15,7 @@ // Official git repository and contact information can be found at // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. +#include "ppsspp_config.h" #include #include "Common/Data/Text/I18n.h" @@ -156,7 +157,7 @@ int allowedInputFlagsMap[OSK_KEYBOARD_COUNT] = { PSP_UTILITY_OSK_INPUTTYPE_JAPANESE_UPPERCASE | PSP_UTILITY_OSK_INPUTTYPE_JAPANESE_SYMBOL, }; -PSPOskDialog::PSPOskDialog() : PSPDialog() { +PSPOskDialog::PSPOskDialog(UtilityDialogType type) : PSPDialog(type) { // This can break all kinds of stuff, changing the decimal point in sprintf for example. // Not sure what the intended effect is so commented out for now. // setlocale(LC_ALL, ""); diff --git a/Core/Dialog/PSPOskDialog.h b/Core/Dialog/PSPOskDialog.h index a244fd419a..8eb55586c0 100644 --- a/Core/Dialog/PSPOskDialog.h +++ b/Core/Dialog/PSPOskDialog.h @@ -213,7 +213,7 @@ enum class PSPOskNativeStatus { class PSPOskDialog: public PSPDialog { public: - PSPOskDialog(); + PSPOskDialog(UtilityDialogType type); virtual ~PSPOskDialog(); virtual int Init(u32 oskPtr); diff --git a/Core/Dialog/PSPPlaceholderDialog.cpp b/Core/Dialog/PSPPlaceholderDialog.cpp index 4672d07211..2fc3fbe92f 100644 --- a/Core/Dialog/PSPPlaceholderDialog.cpp +++ b/Core/Dialog/PSPPlaceholderDialog.cpp @@ -17,7 +17,7 @@ #include "PSPPlaceholderDialog.h" -PSPPlaceholderDialog::PSPPlaceholderDialog() : PSPDialog() { +PSPPlaceholderDialog::PSPPlaceholderDialog(UtilityDialogType type) : PSPDialog(type) { } @@ -25,20 +25,18 @@ PSPPlaceholderDialog::~PSPPlaceholderDialog() { } -int PSPPlaceholderDialog::Init() -{ - status = SCE_UTILITY_STATUS_INITIALIZE; +int PSPPlaceholderDialog::Init() { + ChangeStatus(SCE_UTILITY_STATUS_INITIALIZE, 0); return 0; } -int PSPPlaceholderDialog::Update(int animSpeed) -{ - if (status == SCE_UTILITY_STATUS_INITIALIZE) { - status = SCE_UTILITY_STATUS_RUNNING; - } else if (status == SCE_UTILITY_STATUS_RUNNING) { - status = SCE_UTILITY_STATUS_FINISHED; - } else if (status == SCE_UTILITY_STATUS_FINISHED) { - status = SCE_UTILITY_STATUS_SHUTDOWN; +int PSPPlaceholderDialog::Update(int animSpeed) { + if (ReadStatus() == SCE_UTILITY_STATUS_INITIALIZE) { + ChangeStatus(SCE_UTILITY_STATUS_RUNNING, 0); + } else if (ReadStatus() == SCE_UTILITY_STATUS_RUNNING) { + ChangeStatus(SCE_UTILITY_STATUS_FINISHED, 0); + } else if (ReadStatus() == SCE_UTILITY_STATUS_FINISHED) { + ChangeStatus(SCE_UTILITY_STATUS_SHUTDOWN, 0); } return 0; diff --git a/Core/Dialog/PSPPlaceholderDialog.h b/Core/Dialog/PSPPlaceholderDialog.h index 921ae9123f..4c81efe636 100644 --- a/Core/Dialog/PSPPlaceholderDialog.h +++ b/Core/Dialog/PSPPlaceholderDialog.h @@ -21,7 +21,7 @@ class PSPPlaceholderDialog: public PSPDialog { public: - PSPPlaceholderDialog(); + PSPPlaceholderDialog(UtilityDialogType type); virtual ~PSPPlaceholderDialog(); virtual int Init(); diff --git a/Core/Dialog/PSPSaveDialog.cpp b/Core/Dialog/PSPSaveDialog.cpp index ee584cd698..a42d4389f5 100755 --- a/Core/Dialog/PSPSaveDialog.cpp +++ b/Core/Dialog/PSPSaveDialog.cpp @@ -16,9 +16,13 @@ // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. #ifdef __MINGW32__ +#include +#ifndef _POSIX_THREAD_SAFE_FUNCTIONS #define _POSIX_THREAD_SAFE_FUNCTIONS 200112L #endif +#endif +#include #include #include "Common/Data/Text/I18n.h" @@ -52,12 +56,7 @@ const static int SAVEDATA_DIALOG_SIZE_V2 = 1500; const static int SAVEDATA_DIALOG_SIZE_V3 = 1536; -PSPSaveDialog::PSPSaveDialog() - : PSPDialog() - , display(DS_NONE) - , currentSelectedSave(0) - , ioThread(0) -{ +PSPSaveDialog::PSPSaveDialog(UtilityDialogType type) : PSPDialog(type) { param.SetPspParam(0); } @@ -348,7 +347,7 @@ void PSPSaveDialog::DisplaySaveList(bool canMove) { imageStyle.color = CalcFadedColor(0xFF777777); // Calc save image position on screen - float w, h , x, b; + float w, h, x; float y = 97; if (displayCount != currentSelectedSave) { w = 81; @@ -358,11 +357,6 @@ void PSPSaveDialog::DisplaySaveList(bool canMove) { w = 144; h = 80; x = 27; - b = 1.2f; - PPGeDrawRect(x-b, y-b, x+w+b, y, CalcFadedColor(0xD0FFFFFF)); // top border - PPGeDrawRect(x-b, y, x, y+h, CalcFadedColor(0xD0FFFFFF)); // left border - PPGeDrawRect(x-b, y+h, x+w+b, y+h+b, CalcFadedColor(0xD0FFFFFF)); //bottom border - PPGeDrawRect(x+w, y, x+w+b, y+h, CalcFadedColor(0xD0FFFFFF)); //right border } if (displayCount < currentSelectedSave) y -= 13 + 45 * (currentSelectedSave - displayCount); @@ -373,13 +367,27 @@ void PSPSaveDialog::DisplaySaveList(bool canMove) { if (y > 472.0f || y < -200.0f) continue; - int tw = 256; - int th = 256; - if (fileInfo.texture != NULL) { + int pad = 0; + if (fileInfo.texture != nullptr) { fileInfo.texture->SetTexture(); - tw = fileInfo.texture->Width(); - th = fileInfo.texture->Height(); - PPGeDrawImage(x, y, w, h, 0, 0, 1, 1, tw, th, imageStyle); + int tw = fileInfo.texture->Width(); + int th = fileInfo.texture->Height(); + float scale = (float)h / (float)th; + int scaledW = (int)(tw * scale); + pad = (w - scaledW) / 2; + w = scaledW; + + PPGeDrawImage(x + pad, y, w, h, 0, 0, 1, 1, tw, th, imageStyle); + } else { + PPGeDrawRect(x, y, x + w, y + h, 0x88666666); + } + if (displayCount == currentSelectedSave) { + float b = 1.2f; + uint32_t bc = CalcFadedColor(0xD0FFFFFF); + PPGeDrawRect(x + pad - b, y - b, x + pad + w + b, y, bc); // top border + PPGeDrawRect(x + pad - b, y, x + pad, y + h, bc); // left border + PPGeDrawRect(x + pad - b, y + h, x + pad + w + b, y + h + b, bc); //bottom border + PPGeDrawRect(x + pad + w, y, x + pad + w + b, y + h, bc); //right border } PPGeSetDefaultTexture(); } @@ -414,6 +422,10 @@ void PSPSaveDialog::DisplaySaveIcon(bool checkExists) curSave.texture->SetTexture(); tw = curSave.texture->Width(); th = curSave.texture->Height(); + float scale = (float)h / (float)th; + int scaledW = (int)(tw * scale); + x += (w - scaledW) / 2; + w = scaledW; } else { PPGeDisableTexture(); } @@ -463,11 +475,13 @@ static void FormatSaveDate(char *date, size_t sz, const tm &t) { void PSPSaveDialog::DisplaySaveDataInfo1() { std::lock_guard guard(paramLock); const SaveFileInfo &saveInfo = param.GetFileInfo(currentSelectedSave); + PPGeStyle saveTitleStyle = FadedStyle(PPGeAlign::BOX_LEFT, 0.55f); if (saveInfo.broken) { auto di = GetI18NCategory("Dialog"); PPGeStyle textStyle = FadedStyle(PPGeAlign::BOX_VCENTER, 0.6f); PPGeDrawText(di->T("Corrupted Data"), 180, 136, textStyle); + PPGeDrawText(saveInfo.title, 175, 159, saveTitleStyle); } else if (saveInfo.size == 0) { auto di = GetI18NCategory("Dialog"); PPGeStyle textStyle = FadedStyle(PPGeAlign::BOX_VCENTER, 0.6f); @@ -488,7 +502,6 @@ void PSPSaveDialog::DisplaySaveDataInfo1() { std::string saveDetailTxt = saveInfo.saveDetail; PPGeStyle titleStyle = FadedStyle(PPGeAlign::BOX_BOTTOM, 0.6f); - PPGeStyle saveTitleStyle = FadedStyle(PPGeAlign::BOX_LEFT, 0.55f); titleStyle.color = CalcFadedColor(0xFFC0C0C0); PPGeStyle textStyle = FadedStyle(PPGeAlign::BOX_LEFT, 0.5f); @@ -1013,8 +1026,8 @@ int PSPSaveDialog::Update(int animSpeed) break; } - if (status == SCE_UTILITY_STATUS_FINISHED || pendingStatus == SCE_UTILITY_STATUS_FINISHED) - Memory::Memcpy(requestAddr, &request, request.common.size); + if (ReadStatus() == SCE_UTILITY_STATUS_FINISHED || pendingStatus == SCE_UTILITY_STATUS_FINISHED) + Memory::Memcpy(requestAddr, &request, request.common.size, "SaveDialogParam"); return 0; } diff --git a/Core/Dialog/PSPSaveDialog.h b/Core/Dialog/PSPSaveDialog.h index fd1efa88d5..8058d8a962 100644 --- a/Core/Dialog/PSPSaveDialog.h +++ b/Core/Dialog/PSPSaveDialog.h @@ -71,7 +71,7 @@ class PSPSaveDialog: public PSPDialog { public: - PSPSaveDialog(); + PSPSaveDialog(UtilityDialogType type); virtual ~PSPSaveDialog(); virtual int Init(int paramAddr); @@ -136,14 +136,14 @@ private: DB_DELETE }; - DisplayState display; + DisplayState display = DS_NONE; SavedataParam param; SceUtilitySavedataParam request; // For detecting changes made by the game. SceUtilitySavedataParam originalRequest; - u32 requestAddr; - int currentSelectedSave; + u32 requestAddr = 0; + int currentSelectedSave = 0; int yesnoChoice; @@ -154,7 +154,7 @@ private: SAVEIO_DONE, }; - std::thread *ioThread; + std::thread *ioThread = nullptr; std::mutex paramLock; volatile SaveIOStatus ioThreadStatus; }; diff --git a/Core/Dialog/PSPScreenshotDialog.cpp b/Core/Dialog/PSPScreenshotDialog.cpp index acec8d08d4..92e4a17a91 100644 --- a/Core/Dialog/PSPScreenshotDialog.cpp +++ b/Core/Dialog/PSPScreenshotDialog.cpp @@ -52,8 +52,7 @@ struct SceUtilityScreenshotParams { // TODO }; -PSPScreenshotDialog::PSPScreenshotDialog() : PSPDialog() { - +PSPScreenshotDialog::PSPScreenshotDialog(UtilityDialogType type) : PSPDialog(type) { } PSPScreenshotDialog::~PSPScreenshotDialog() { @@ -61,7 +60,7 @@ PSPScreenshotDialog::~PSPScreenshotDialog() { int PSPScreenshotDialog::Init(u32 paramAddr) { // Already running - if (status != SCE_UTILITY_STATUS_NONE && status != SCE_UTILITY_STATUS_SHUTDOWN) { + if (ReadStatus() != SCE_UTILITY_STATUS_NONE && ReadStatus() != SCE_UTILITY_STATUS_SHUTDOWN) { ERROR_LOG_REPORT(HLE, "sceUtilityScreenshotInitStart(%08x): invalid status", paramAddr); return SCE_ERROR_UTILITY_INVALID_STATUS; } @@ -84,23 +83,23 @@ int PSPScreenshotDialog::Init(u32 paramAddr) { } mode = params_->mode; - status = SCE_UTILITY_STATUS_INITIALIZE; + ChangeStatus(SCE_UTILITY_STATUS_INITIALIZE, 0); return 0; } int PSPScreenshotDialog::Update(int animSpeed) { if (UseAutoStatus()) { - if (status == SCE_UTILITY_STATUS_INITIALIZE) { - status = SCE_UTILITY_STATUS_RUNNING; - } else if (status == SCE_UTILITY_STATUS_RUNNING) { + if (ReadStatus() == SCE_UTILITY_STATUS_INITIALIZE) { + ChangeStatus(SCE_UTILITY_STATUS_RUNNING, 0); + } else if (ReadStatus() == SCE_UTILITY_STATUS_RUNNING) { if (mode == SCE_UTILITY_SCREENSHOT_TYPE_CONT_START) { - status = SCE_UTILITY_STATUS_SCREENSHOT_UNKNOWN; + ChangeStatus(SCE_UTILITY_STATUS_SCREENSHOT_UNKNOWN, 0); } else { - status = SCE_UTILITY_STATUS_FINISHED; + ChangeStatus(SCE_UTILITY_STATUS_FINISHED, 0); } - } else if (status == SCE_UTILITY_STATUS_FINISHED) { - status = SCE_UTILITY_STATUS_SHUTDOWN; + } else if (ReadStatus() == SCE_UTILITY_STATUS_FINISHED) { + ChangeStatus(SCE_UTILITY_STATUS_SHUTDOWN, 0); } } return 0; @@ -108,11 +107,11 @@ int PSPScreenshotDialog::Update(int animSpeed) { int PSPScreenshotDialog::ContStart() { // Based on JPCSP http://code.google.com/p/jpcsp/source/detail?r=3381 - if (status != SCE_UTILITY_STATUS_SCREENSHOT_UNKNOWN) + if (ReadStatus() != SCE_UTILITY_STATUS_SCREENSHOT_UNKNOWN) return SCE_ERROR_UTILITY_INVALID_STATUS; // Check with JPCSPTrace log of Dream Club Portable - status = SCE_UTILITY_STATUS_FINISHED; + ChangeStatus(SCE_UTILITY_STATUS_FINISHED, 0); return 0; } @@ -129,3 +128,9 @@ void PSPScreenshotDialog::DoState(PointerWrap &p) { Do(p, params_); } } + +pspUtilityDialogCommon *PSPScreenshotDialog::GetCommonParam() { + if (params_.IsValid()) + return ¶ms_->base; + return nullptr; +} diff --git a/Core/Dialog/PSPScreenshotDialog.h b/Core/Dialog/PSPScreenshotDialog.h index 17e7d083aa..ae83e9e8ca 100644 --- a/Core/Dialog/PSPScreenshotDialog.h +++ b/Core/Dialog/PSPScreenshotDialog.h @@ -24,15 +24,21 @@ struct SceUtilityScreenshotParams; class PSPScreenshotDialog : public PSPDialog { public: - PSPScreenshotDialog(); + PSPScreenshotDialog(UtilityDialogType type); virtual ~PSPScreenshotDialog(); virtual int Init(u32 paramAddr); virtual int Update(int animSpeed) override; virtual int ContStart(); virtual void DoState(PointerWrap &p) override; + virtual pspUtilityDialogCommon *GetCommonParam() override; protected: + // TODO: Manage status correctly. + bool UseAutoStatus() override { + return true; + } + int mode; PSPPointer params_; }; diff --git a/Core/Dialog/SavedataParam.cpp b/Core/Dialog/SavedataParam.cpp index 354fd3b823..137d272e20 100644 --- a/Core/Dialog/SavedataParam.cpp +++ b/Core/Dialog/SavedataParam.cpp @@ -844,11 +844,13 @@ bool SavedataParam::GetExpectedHash(const std::string &dirPath, const std::strin void SavedataParam::LoadFile(const std::string& dirPath, const std::string& filename, PspUtilitySavedataFileData *fileData) { std::string filePath = dirPath + "/" + filename; - s64 readSize = -1; - if(!fileData->buf.IsValid()) + if (!fileData->buf.IsValid()) return; + u8 *buf = fileData->buf; - if(ReadPSPFile(filePath, &buf, fileData->bufSize, &readSize)) + u32 size = Memory::ValidSize(fileData->buf.ptr, fileData->bufSize); + s64 readSize = -1; + if (ReadPSPFile(filePath, &buf, size, &readSize)) fileData->size = readSize; } @@ -1576,6 +1578,7 @@ void SavedataParam::SetFileInfo(SaveFileInfo &saveInfo, PSPFileInfo &info, std:: } } else { saveInfo.broken = true; + truncate_cpy(saveInfo.title, saveDir.c_str()); } } diff --git a/Core/Dialog/SavedataParam.h b/Core/Dialog/SavedataParam.h index dea8140c33..3cf343914b 100644 --- a/Core/Dialog/SavedataParam.h +++ b/Core/Dialog/SavedataParam.h @@ -271,27 +271,22 @@ struct SceUtilitySavedataParam // Non native, this one we can reorganize as we like struct SaveFileInfo { - s64 size; + s64 size = 0; std::string saveName; std::string saveDir; - int idx; + int idx = 0; - char title[128]; - char saveTitle[128]; - char saveDetail[1024]; + char title[128]{}; + char saveTitle[128]{}; + char saveDetail[1024]{}; - bool broken; + bool broken = false; - tm modif_time; + tm modif_time{}; - PPGeImage *texture; + PPGeImage *texture = nullptr; - SaveFileInfo() : size(0), saveName(""), idx(0), texture(NULL), broken(false) - { - memset(title, 0, 128); - memset(saveTitle, 0, 128); - memset(saveDetail, 0, 1024); - memset(&modif_time, 0, sizeof(modif_time)); + SaveFileInfo() { } void DoState(PointerWrap &p); diff --git a/Core/ELF/ElfReader.cpp b/Core/ELF/ElfReader.cpp index 93863d7c6d..917bcb48f1 100644 --- a/Core/ELF/ElfReader.cpp +++ b/Core/ELF/ElfReader.cpp @@ -19,7 +19,7 @@ #include "Core/Reporting.h" #include "Core/MIPS/MIPSTables.h" #include "Core/ELF/ElfReader.h" -#include "Core/Debugger/Breakpoints.h" +#include "Core/Debugger/MemBlockInfo.h" #include "Core/Debugger/SymbolMap.h" #include "Core/HLE/sceKernelMemory.h" #include "Core/HLE/sceKernelModule.h" @@ -136,7 +136,7 @@ bool ElfReader::LoadRelocations(const Elf32_Rel *rels, int numRelocs) DEBUG_LOG(LOADER,"Corresponding lo found at %08x", corrLoAddr); } if (Memory::IsValidAddress(corrLoAddr)) { - s16 lo = (s32)(s16)(u16)(Memory::ReadUnchecked_U32(corrLoAddr) & 0xFFFF); //signed?? + s16 lo = (s16)Memory::ReadUnchecked_U16(corrLoAddr); cur += lo; cur += relocateTo; addrToHiLo(cur, hi, lo); @@ -170,11 +170,7 @@ bool ElfReader::LoadRelocations(const Elf32_Rel *rels, int numRelocs) break; case R_MIPS_16: - { - char temp[256]; - op = (op & 0xFFFF0000) | (((int)(op & 0xFFFF) + (int)relocateTo) & 0xFFFF); - MIPSDisAsm(MIPSOpcode(op), 0, temp); - } + op = (op & 0xFFFF0000) | (((int)(op & 0xFFFF) + (int)relocateTo) & 0xFFFF); break; case R_MIPS_NONE: @@ -190,6 +186,7 @@ bool ElfReader::LoadRelocations(const Elf32_Rel *rels, int numRelocs) break; } Memory::Write_U32(op, addr); + NotifyMemInfo(MemBlockFlags::WRITE, addr, 4, "Relocation"); } if (numErrors) { WARN_LOG(LOADER, "%i bad relocations found!!!", numErrors); @@ -348,6 +345,7 @@ void ElfReader::LoadRelocations2(int rel_seg) } Memory::Write_U32(op, rel_offset); + NotifyMemInfo(MemBlockFlags::WRITE, addr, 4, "Relocation2"); rcount += 1; } } @@ -475,10 +473,11 @@ int ElfReader::LoadInto(u32 loadAddress, bool fromTop) if (srcSize < dstSize) { memset(dst + srcSize, 0, dstSize - srcSize); //zero out bss + NotifyMemInfo(MemBlockFlags::WRITE, writeAddr + srcSize, dstSize - srcSize, "ELFZero"); } memcpy(dst, src, srcSize); - CBreakPoints::ExecMemCheck(writeAddr, true, dstSize, currentMIPS->pc); + NotifyMemInfo(MemBlockFlags::WRITE, writeAddr, srcSize, "ELFLoad"); DEBUG_LOG(LOADER,"Loadable Segment Copied to %08x, size %08x", writeAddr, (u32)p->p_memsz); } } diff --git a/Core/ELF/ParamSFO.cpp b/Core/ELF/ParamSFO.cpp index b12b508942..5b966f1bfb 100644 --- a/Core/ELF/ParamSFO.cpp +++ b/Core/ELF/ParamSFO.cpp @@ -21,25 +21,26 @@ #include "Common/CommonTypes.h" #include "Common/Log.h" #include "Common/StringUtils.h" +#include "Common/Swap.h" #include "Core/ELF/ParamSFO.h" #include "Core/Core.h" struct Header { - u32 magic; /* Always PSF */ - u32 version; /* Usually 1.1 */ - u32 key_table_start; /* Start position of key_table */ - u32 data_table_start; /* Start position of data_table */ - u32 index_table_entries; /* Number of entries in index_table*/ + u32_le magic; /* Always PSF */ + u32_le version; /* Usually 1.1 */ + u32_le key_table_start; /* Start position of key_table */ + u32_le data_table_start; /* Start position of data_table */ + u32_le index_table_entries; /* Number of entries in index_table*/ }; struct IndexTable { - u16 key_table_offset; /* Offset of the param_key from start of key_table */ - u16 param_fmt; /* Type of data of param_data in the data_table */ - u32 param_len; /* Used Bytes by param_data in the data_table */ - u32 param_max_len; /* Total bytes reserved for param_data in the data_table */ - u32 data_table_offset; /* Offset of the param_data from start of data_table */ + u16_le key_table_offset; /* Offset of the param_key from start of key_table */ + u16_le param_fmt; /* Type of data of param_data in the data_table */ + u32_le param_len; /* Used Bytes by param_data in the data_table */ + u32_le param_max_len; /* Total bytes reserved for param_data in the data_table */ + u32_le data_table_offset; /* Offset of the param_data from start of data_table */ }; void ParamSFOData::SetValue(std::string key, unsigned int value, int max_size) { @@ -113,7 +114,7 @@ bool ParamSFOData::ReadSFO(const u8 *paramsfo, size_t size) { case 0x0404: { // Unsigned int - const u32 *data = (const u32 *)(data_start + indexTables[i].data_table_offset); + const u32_le *data = (const u32_le *)(data_start + indexTables[i].data_table_offset); SetValue(key,*data,indexTables[i].param_max_len); VERBOSE_LOG(LOADER, "%s %08x", key, *data); } @@ -218,7 +219,7 @@ bool ParamSFOData::WriteSFO(u8 **paramsfo, size_t *size) { index_ptr->param_fmt = 0x0404; index_ptr->param_len = 4; - *(int*)data_ptr = it->second.i_value; + *(s32_le *)data_ptr = it->second.i_value; } else if (it->second.type == VT_UTF8_SPE) { diff --git a/Core/FileLoaders/DiskCachingFileLoader.cpp b/Core/FileLoaders/DiskCachingFileLoader.cpp index 412f409a84..93b2bebd38 100644 --- a/Core/FileLoaders/DiskCachingFileLoader.cpp +++ b/Core/FileLoaders/DiskCachingFileLoader.cpp @@ -98,6 +98,10 @@ size_t DiskCachingFileLoader::ReadAt(s64 absolutePos, size_t bytes, void *data, // While in case the cache size is too small for the entire read. while (readSize < bytes) { readSize += cache_->SaveIntoCache(backend_, absolutePos + readSize, bytes - readSize, (u8 *)data + readSize, flags); + // We're done, nothing more to read. + if (readSize == bytes) { + break; + } // If there are already-cached blocks afterward, we have to read them. size_t bytesFromCache = cache_->ReadFromCache(absolutePos + readSize, bytes - readSize, (u8 *)data + readSize); readSize += bytesFromCache; @@ -441,6 +445,9 @@ bool DiskCachingFileLoaderCache::ReadBlockData(u8 *dest, BlockInfo &info, size_t if (!f_) { return false; } + if (size == 0) { + return true; + } s64 blockOffset = GetBlockOffset(info.block); // Before we read, make sure the buffers are flushed. diff --git a/Core/FileLoaders/LocalFileLoader.cpp b/Core/FileLoaders/LocalFileLoader.cpp index 1a6205bfab..9af65c1887 100644 --- a/Core/FileLoaders/LocalFileLoader.cpp +++ b/Core/FileLoaders/LocalFileLoader.cpp @@ -31,8 +31,28 @@ #include #endif +#ifndef _WIN32 +LocalFileLoader::LocalFileLoader(int fd, const std::string &filename) : fd_(fd), filename_(filename), isOpenedByFd_(fd != -1) { + if (fd != -1) { + DetectSizeFd(); + } +} + +void LocalFileLoader::DetectSizeFd() { +#if PPSSPP_PLATFORM(ANDROID) || (defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS < 64) + off64_t off = lseek64(fd_, 0, SEEK_END); + filesize_ = off; + lseek64(fd_, 0, SEEK_SET); +#else + off_t off = lseek(fd_, 0, SEEK_END); + filesize_ = off; + lseek(fd_, 0, SEEK_SET); +#endif +} +#endif + LocalFileLoader::LocalFileLoader(const std::string &filename) - : filesize_(0), filename_(filename) { + : filesize_(0), filename_(filename), isOpenedByFd_(false) { if (filename.empty()) { ERROR_LOG(FILESYS, "LocalFileLoader can't load empty filenames"); return; @@ -43,15 +63,8 @@ LocalFileLoader::LocalFileLoader(const std::string &filename) if (fd_ == -1) { return; } -#if PPSSPP_PLATFORM(ANDROID) || (defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS < 64) - off64_t off = lseek64(fd_, 0, SEEK_END); - filesize_ = off; - lseek64(fd_, 0, SEEK_SET); -#else - off_t off = lseek(fd_, 0, SEEK_END); - filesize_ = off; - lseek(fd_, 0, SEEK_SET); -#endif + + DetectSizeFd(); #else // _WIN32 @@ -65,7 +78,7 @@ LocalFileLoader::LocalFileLoader(const std::string &filename) return; } LARGE_INTEGER end_offset; - const LARGE_INTEGER zero = { 0 }; + const LARGE_INTEGER zero{}; if (SetFilePointerEx(handle_, zero, &end_offset, FILE_END) == 0) { // Couldn't seek in the file. Close it and give up? This should never happen. CloseHandle(handle_); @@ -92,6 +105,9 @@ LocalFileLoader::~LocalFileLoader() { bool LocalFileLoader::Exists() { // If we couldn't open it for reading, we say it does not exist. #ifndef _WIN32 + if (isOpenedByFd_) { + return true; + } if (fd_ != -1 || IsDirectory()) { #else if (handle_ != INVALID_HANDLE_VALUE || IsDirectory()) { @@ -119,6 +135,9 @@ std::string LocalFileLoader::Path() const { } size_t LocalFileLoader::ReadAt(s64 absolutePos, size_t bytes, size_t count, void *data, Flags flags) { + if (bytes == 0) + return 0; + #if PPSSPP_PLATFORM(SWITCH) // Toolchain has no fancy IO API. We must lock. std::lock_guard guard(readLock_); diff --git a/Core/FileLoaders/LocalFileLoader.h b/Core/FileLoaders/LocalFileLoader.h index bb5910494f..d3933b55c4 100644 --- a/Core/FileLoaders/LocalFileLoader.h +++ b/Core/FileLoaders/LocalFileLoader.h @@ -27,6 +27,7 @@ typedef void *HANDLE; class LocalFileLoader : public FileLoader { public: LocalFileLoader(const std::string &filename); + LocalFileLoader(const int fd, const std::string &filename); virtual ~LocalFileLoader(); virtual bool Exists() override; @@ -37,6 +38,7 @@ public: private: #ifndef _WIN32 + void DetectSizeFd(); int fd_; #else HANDLE handle_; @@ -44,4 +46,5 @@ private: u64 filesize_; std::string filename_; std::mutex readLock_; + bool isOpenedByFd_; }; diff --git a/Core/FileSystems/BlockDevices.cpp b/Core/FileSystems/BlockDevices.cpp index 9d4f85ee6a..5314fac125 100644 --- a/Core/FileSystems/BlockDevices.cpp +++ b/Core/FileSystems/BlockDevices.cpp @@ -50,11 +50,13 @@ BlockDevice *constructBlockDevice(FileLoader *fileLoader) { return new FileBlockDevice(fileLoader); } -u32 BlockDevice::CalculateCRC() { +u32 BlockDevice::CalculateCRC(volatile bool *cancel) { u32 crc = crc32(0, Z_NULL, 0); u8 block[2048]; for (u32 i = 0; i < GetNumBlocks(); ++i) { + if (cancel && *cancel) + return 0; if (!ReadBlock(i, block, true)) { ERROR_LOG(FILESYS, "Failed to read block for CRC"); return 0; diff --git a/Core/FileSystems/BlockDevices.h b/Core/FileSystems/BlockDevices.h index 2db068c777..3268b27fa7 100644 --- a/Core/FileSystems/BlockDevices.h +++ b/Core/FileSystems/BlockDevices.h @@ -47,7 +47,7 @@ public: virtual u32 GetNumBlocks() = 0; virtual bool IsDisc() = 0; - u32 CalculateCRC(); + u32 CalculateCRC(volatile bool *cancel = nullptr); void NotifyReadError(); protected: diff --git a/Core/FileSystems/DirectoryFileSystem.cpp b/Core/FileSystems/DirectoryFileSystem.cpp index d6594ee52e..2fe206c977 100644 --- a/Core/FileSystems/DirectoryFileSystem.cpp +++ b/Core/FileSystems/DirectoryFileSystem.cpp @@ -17,7 +17,15 @@ #include "ppsspp_config.h" +#ifdef __MINGW32__ +#include +#ifndef _POSIX_THREAD_SAFE_FUNCTIONS +#define _POSIX_THREAD_SAFE_FUNCTIONS 200112L +#endif +#endif + #include +#include #include #include "Common/Data/Text/I18n.h" @@ -41,6 +49,7 @@ #ifdef _WIN32 #include "Common/CommonWindows.h" #include +#undef FILE_OPEN #else #include #include @@ -422,7 +431,7 @@ size_t DirectoryFileHandle::Seek(s32 position, FileMove type) LARGE_INTEGER distance; distance.QuadPart = position; LARGE_INTEGER cursor; - DWORD newPos = SetFilePointerEx(hFile, distance, &cursor, moveMethod); + SetFilePointerEx(hFile, distance, &cursor, moveMethod); result = (size_t)cursor.QuadPart; #else int moveMethod = 0; @@ -622,7 +631,7 @@ int DirectoryFileSystem::OpenFile(std::string filename, FileAccess access, const err = ReplayApplyDisk(ReplayAction::FILE_OPEN, err, CoreTiming::GetGlobalTimeUs()); if (err != 0) { #ifdef _WIN32 - ERROR_LOG(FILESYS, "DirectoryFileSystem::OpenFile: FAILED, %i - access = %i", GetLastError(), (int)access); + ERROR_LOG(FILESYS, "DirectoryFileSystem::OpenFile: FAILED, %i - access = %i", (int)GetLastError(), (int)access); #else ERROR_LOG(FILESYS, "DirectoryFileSystem::OpenFile: FAILED, %i - access = %i", errno, (int)access); #endif diff --git a/Core/FileSystems/ISOFileSystem.cpp b/Core/FileSystems/ISOFileSystem.cpp index 4484ac8110..d5f20409bd 100644 --- a/Core/FileSystems/ISOFileSystem.cpp +++ b/Core/FileSystems/ISOFileSystem.cpp @@ -63,13 +63,27 @@ bool parseLBN(std::string filename, u32 *sectorStart, u32 *readSize) { #pragma pack(push) #pragma pack(1) +struct u32_le_be_pair { + u8 valueLE[4]; + u8 valueBE[4]; + operator u32() const { + return valueLE[0] + (valueLE[1] << 8) + (valueLE[2] << 16) + (valueLE[3] << 24); + } +}; + +struct u16_le_be_pair { + u8 valueLE[2]; + u8 valueBE[2]; + operator u16() const { + return valueLE[0] + (valueLE[1] << 8); + } +}; + struct DirectoryEntry { u8 size; u8 sectorsInExtendedRecord; - u32_le firstDataSectorLE; // LBA - u32_be firstDataSectorBE; - u32_le dataLengthLE; // Size - u32_be dataLengthBE; + u32_le_be_pair firstDataSector; // LBA + u32_le_be_pair dataLength; // Size u8 years; u8 month; u8 day; @@ -80,38 +94,9 @@ struct DirectoryEntry { u8 flags; // 2 = directory u8 fileUnitSize; u8 interleaveGap; - u16_le volSeqNumberLE; - u16_be volSeqNumberBE; + u16_le_be_pair volSeqNumber; u8 identifierLength; //identifier comes right after u8 firstIdChar; - -#if COMMON_LITTLE_ENDIAN - u32 firstDataSector() const - { - return firstDataSectorLE; - } - u32 dataLength() const - { - return dataLengthLE; - } - u32 volSeqNumber() const - { - return volSeqNumberLE; - } -#else - u32 firstDataSector() const - { - return firstDataSectorBE; - } - u32 dataLength() const - { - return dataLengthBE; - } - u32 volSeqNumber() const - { - return volSeqNumberBE; - } -#endif }; struct DirectorySector { @@ -126,25 +111,16 @@ struct VolDescriptor { char sysid[32]; char volid[32]; char zeros[8]; - u32_le numSectorsLE; - u32_be numSectoreBE; + u32_le_be_pair numSectors; char morezeros[32]; - u16_le volSetSizeLE; - u16_be volSetSizeBE; - u16_le volSeqNumLE; - u16_be volSeqNumBE; - u16_le sectorSizeLE; - u16_be sectorSizeBE; - u32_le pathTableLengthLE; - u32_be pathTableLengthBE; - u16_le firstLETableSectorLE; - u16_be firstLETableSectorBE; - u16_le secondLETableSectorLE; - u16_be secondLETableSectorBE; - u16_le firstBETableSectorLE; - u16_be firstBETableSectorBE; - u16_le secondBETableSectorLE; - u16_be secondBETableSectorBE; + u16_le_be_pair volSetSize; + u16_le_be_pair volSeqNum; + u16_le_be_pair sectorSize; + u32_le_be_pair pathTableLength; + u16_le_be_pair firstLETableSector; + u16_le_be_pair secondLETableSector; + u16_le_be_pair firstBETableSector; + u16_le_be_pair secondBETableSector; DirectoryEntry root; char volumeSetIdentifier[128]; char publisherIdentifier[128]; @@ -192,8 +168,8 @@ ISOFileSystem::ISOFileSystem(IHandleAllocator *_hAlloc, BlockDevice *_blockDevic return; } - treeroot->startsector = desc.root.firstDataSector(); - treeroot->dirsize = desc.root.dataLength(); + treeroot->startsector = desc.root.firstDataSector; + treeroot->dirsize = desc.root.dataLength; } ISOFileSystem::~ISOFileSystem() { @@ -244,16 +220,15 @@ void ISOFileSystem::ReadDirectory(TreeEntry *root) { relative = false; } - entry->size = dir.dataLength(); - entry->startingPosition = dir.firstDataSector() * 2048; + entry->size = dir.dataLength; + entry->startingPosition = dir.firstDataSector * 2048; entry->isDirectory = !isFile; entry->flags = dir.flags; entry->parent = root; - entry->startsector = dir.firstDataSector(); - entry->dirsize = dir.dataLength(); + entry->startsector = dir.firstDataSector; + entry->dirsize = dir.dataLength; entry->valid = isFile; // Can pre-mark as valid if file, as we don't recurse into those. - // Let's not excessively spam the log - I commented this line out. - //DEBUG_LOG(FILESYS, "%s: %s %08x %08x %i", entry->isDirectory?"D":"F", entry->name.c_str(), dir.firstDataSectorLE, entry->startingPosition, entry->startingPosition); + VERBOSE_LOG(FILESYS, "%s: %s %08x %08x %i", entry->isDirectory ? "D" : "F", entry->name.c_str(), (u32)dir.firstDataSector, entry->startingPosition, entry->startingPosition); if (entry->isDirectory && !relative) { if (entry->startsector == root->startsector) { @@ -438,11 +413,11 @@ int ISOFileSystem::Ioctl(u32 handle, u32 cmd, u32 indataPtr, u32 inlen, u32 outd VolDescriptor desc; blockDevice->ReadBlock(16, (u8 *)&desc); - if (outlen < (u32)desc.pathTableLengthLE) { + if (outlen < (u32)desc.pathTableLength) { return SCE_KERNEL_ERROR_ERRNO_INVALID_ARGUMENT; } else { - int block = (u16)desc.firstLETableSectorLE; - u32 size = (u32)desc.pathTableLengthLE; + int block = (u16)desc.firstLETableSector; + u32 size = Memory::ValidSize(outdataPtr, (u32)desc.pathTableLength); u8 *out = Memory::GetPointer(outdataPtr); int blocks = size / blockDevice->GetBlockSize(); diff --git a/Core/FileSystems/VirtualDiscFileSystem.cpp b/Core/FileSystems/VirtualDiscFileSystem.cpp index fde9808d8c..0805688d88 100644 --- a/Core/FileSystems/VirtualDiscFileSystem.cpp +++ b/Core/FileSystems/VirtualDiscFileSystem.cpp @@ -16,6 +16,13 @@ // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. #include "ppsspp_config.h" +#ifdef __MINGW32__ +#include +#ifndef _POSIX_THREAD_SAFE_FUNCTIONS +#define _POSIX_THREAD_SAFE_FUNCTIONS 200112L +#endif +#endif +#include #include "Common/File/FileUtil.h" #include "Common/StringUtils.h" @@ -365,7 +372,7 @@ int VirtualDiscFileSystem::OpenFile(std::string filename, FileAccess access, con if (!success) { #ifdef _WIN32 - ERROR_LOG(FILESYS, "VirtualDiscFileSystem::OpenFile: FAILED, %i", GetLastError()); + ERROR_LOG(FILESYS, "VirtualDiscFileSystem::OpenFile: FAILED, %i", (int)GetLastError()); #else ERROR_LOG(FILESYS, "VirtualDiscFileSystem::OpenFile: FAILED"); #endif @@ -391,7 +398,7 @@ int VirtualDiscFileSystem::OpenFile(std::string filename, FileAccess access, con if (!success) { #ifdef _WIN32 - ERROR_LOG(FILESYS, "VirtualDiscFileSystem::OpenFile: FAILED, %i - access = %i", GetLastError(), (int)access); + ERROR_LOG(FILESYS, "VirtualDiscFileSystem::OpenFile: FAILED, %i - access = %i", (int)GetLastError(), (int)access); #else ERROR_LOG(FILESYS, "VirtualDiscFileSystem::OpenFile: FAILED, access = %i", (int)access); #endif diff --git a/Core/Font/PGF.cpp b/Core/Font/PGF.cpp index ae3afa57b8..c1a8802571 100644 --- a/Core/Font/PGF.cpp +++ b/Core/Font/PGF.cpp @@ -41,7 +41,7 @@ static int getBits(int numBits, const u8 *buf, size_t pos) { _dbg_assert_msg_(numBits <= 32, "Unable to return more than 32 bits, %d requested", numBits); const size_t wordpos = pos >> 5; - const u32 *wordbuf = (const u32 *)buf; + const u32_le *wordbuf = (const u32_le *)buf; const u8 bitoff = pos & 31; // Might just be in one, has to be within two. @@ -53,8 +53,10 @@ static int getBits(int numBits, const u8 *buf, size_t pos) { const u8 done = 32 - bitoff; const u8 remaining = numBits - done; - const u32 mask = (1 << remaining) - 1; - v |= (wordbuf[wordpos + 1] & mask) << done; + if (remaining > 0) { + const u32 mask = (1 << remaining) - 1; + v |= (wordbuf[wordpos + 1] & mask) << done; + } return v; } } @@ -348,7 +350,7 @@ bool PGF::GetCharInfo(int charCode, PGFCharInfo *charInfo, int altCharCode, int charInfo->sfp26Height = glyph.dimensionHeight; charInfo->sfp26Ascender = glyph.yAdjustH; // Font y goes upwards. If top is 10 and height is 11, the descender is approx. -1 (below 0.) - charInfo->sfp26Descender = charInfo->sfp26Ascender - (s32_le)charInfo->sfp26Height; + charInfo->sfp26Descender = charInfo->sfp26Ascender - (s32)charInfo->sfp26Height; charInfo->sfp26BearingHX = glyph.xAdjustH; charInfo->sfp26BearingHY = glyph.yAdjustH; charInfo->sfp26BearingVX = glyph.xAdjustV; diff --git a/Core/HDRemaster.h b/Core/HDRemaster.h index 8d6939f91c..47884976ed 100644 --- a/Core/HDRemaster.h +++ b/Core/HDRemaster.h @@ -17,6 +17,7 @@ #pragma once +#include #include "Common/CommonTypes.h" // This bool is the key to having the HD remasters work. diff --git a/Core/HLE/FunctionWrappers.h b/Core/HLE/FunctionWrappers.h index 01124ea157..73cc956d12 100644 --- a/Core/HLE/FunctionWrappers.h +++ b/Core/HLE/FunctionWrappers.h @@ -147,7 +147,7 @@ template void WrapI_IIIIIIU() { // Hm, do so many params get passed in registers? template void WrapI_IIIIIIIIU() { - u32 param8 = *(u32*)Memory::GetPointer(currentMIPS->r[29]); //Fixed 9th parameter, thanks to Kingcom + u32 param8 = *(const u32_le *)Memory::GetPointer(currentMIPS->r[29]); //Fixed 9th parameter, thanks to Kingcom u32 retval = func(PARAM(0), PARAM(1), PARAM(2), PARAM(3), PARAM(4), PARAM(5), PARAM(6), PARAM(7), param8); RETURN(retval); } diff --git a/Core/HLE/HLE.cpp b/Core/HLE/HLE.cpp index eb4575fea6..d31007beff 100644 --- a/Core/HLE/HLE.cpp +++ b/Core/HLE/HLE.cpp @@ -365,28 +365,30 @@ bool hleExecuteDebugBreak(const HLEFunction &func) return true; } -u32 hleDelayResult(u32 result, const char *reason, int usec) -{ - if (__KernelIsDispatchEnabled()) - { - CoreTiming::ScheduleEvent(usToCycles(usec), delayedResultEvent, __KernelGetCurThread()); +u32 hleDelayResult(u32 result, const char *reason, int usec) { + if (!__KernelIsDispatchEnabled()) { + WARN_LOG(HLE, "%s: Dispatch disabled, not delaying HLE result (right thing to do?)", latestSyscall ? latestSyscall->name : "?"); + } else { + SceUID thread = __KernelGetCurThread(); + if (KernelIsThreadWaiting(thread)) + ERROR_LOG(HLE, "%s: Delaying a thread that's already waiting", latestSyscall ? latestSyscall->name : "?"); + CoreTiming::ScheduleEvent(usToCycles(usec), delayedResultEvent, thread); __KernelWaitCurThread(WAITTYPE_HLEDELAY, 1, result, 0, false, reason); } - else - WARN_LOG(HLE, "Dispatch disabled, not delaying HLE result (right thing to do?)"); return result; } -u64 hleDelayResult(u64 result, const char *reason, int usec) -{ - if (__KernelIsDispatchEnabled()) - { - u64 param = (result & 0xFFFFFFFF00000000) | __KernelGetCurThread(); +u64 hleDelayResult(u64 result, const char *reason, int usec) { + if (!__KernelIsDispatchEnabled()) { + WARN_LOG(HLE, "%s: Dispatch disabled, not delaying HLE result (right thing to do?)", latestSyscall ? latestSyscall->name : "?"); + } else { + SceUID thread = __KernelGetCurThread(); + if (KernelIsThreadWaiting(thread)) + ERROR_LOG(HLE, "%s: Delaying a thread that's already waiting", latestSyscall ? latestSyscall->name : "?"); + u64 param = (result & 0xFFFFFFFF00000000) | thread; CoreTiming::ScheduleEvent(usToCycles(usec), delayedResultEvent, param); - __KernelWaitCurThread(WAITTYPE_HLEDELAY, 1, (u32) result, 0, false, reason); + __KernelWaitCurThread(WAITTYPE_HLEDELAY, 1, (u32)result, 0, false, reason); } - else - WARN_LOG(HLE, "Dispatch disabled, not delaying HLE result (right thing to do?)"); return result; } @@ -628,7 +630,7 @@ inline void CallSyscallWithFlags(const HLEFunction *info) if (flags & HLE_CLEAR_STACK_BYTES) { u32 stackStart = __KernelGetCurThreadStackStart(); if (currentMIPS->r[MIPS_REG_SP] - info->stackBytesToClear >= stackStart) { - Memory::Memset(currentMIPS->r[MIPS_REG_SP] - info->stackBytesToClear, 0, info->stackBytesToClear); + Memory::Memset(currentMIPS->r[MIPS_REG_SP] - info->stackBytesToClear, 0, info->stackBytesToClear, "HLEStackClear"); } } @@ -732,6 +734,7 @@ void CallSyscall(MIPSOpcode op) int funcnum = callno & 0xFFF; int modulenum = (callno & 0xFF000) >> 12; double total = time_now_d() - start - hleSteppingTime; + _dbg_assert_msg_(total >= 0.0, "Time spent in syscall became negative"); hleSteppingTime = 0.0; updateSyscallStats(modulenum, funcnum, total); } diff --git a/Core/HLE/HLEHelperThread.cpp b/Core/HLE/HLEHelperThread.cpp index a142820b92..0975d1ea96 100644 --- a/Core/HLE/HLEHelperThread.cpp +++ b/Core/HLE/HLEHelperThread.cpp @@ -29,11 +29,11 @@ HLEHelperThread::HLEHelperThread() : id_(-1), entry_(0) { } -HLEHelperThread::HLEHelperThread(const char *threadName, u32 instructions[], u32 instrCount, u32 prio, int stacksize) { +HLEHelperThread::HLEHelperThread(const char *threadName, const u32 instructions[], u32 instrCount, u32 prio, int stacksize) { u32 instrBytes = instrCount * sizeof(u32); u32 totalBytes = instrBytes + sizeof(u32) * 2; AllocEntry(totalBytes); - Memory::Memcpy(entry_, instructions, instrBytes); + Memory::Memcpy(entry_, instructions, instrBytes, "HelperMIPS"); // Just to simplify things, we add the return here. Memory::Write_U32(MIPS_MAKE_JR_RA(), entry_ + instrBytes + 0); @@ -59,8 +59,8 @@ HLEHelperThread::~HLEHelperThread() { } void HLEHelperThread::AllocEntry(u32 size) { - entry_ = kernelMemory.Alloc(size); - Memory::Memset(entry_, 0, size); + entry_ = kernelMemory.Alloc(size, false, "HLEHelper"); + Memory::Memset(entry_, 0, size, "HLEHelperClear"); currentMIPS->InvalidateICache(entry_, size); } diff --git a/Core/HLE/HLEHelperThread.h b/Core/HLE/HLEHelperThread.h index 39df4c7402..d932c2ea23 100644 --- a/Core/HLE/HLEHelperThread.h +++ b/Core/HLE/HLEHelperThread.h @@ -25,7 +25,7 @@ class HLEHelperThread { public: // For savestates. HLEHelperThread(); - HLEHelperThread(const char *threadName, u32 instructions[], u32 instrCount, u32 prio, int stacksize); + HLEHelperThread(const char *threadName, const u32 instructions[], u32 instrCount, u32 prio, int stacksize); HLEHelperThread(const char *threadName, const char *module, const char *func, u32 prio, int stacksize); ~HLEHelperThread(); void DoState(PointerWrap &p); diff --git a/Core/HLE/ReplaceTables.cpp b/Core/HLE/ReplaceTables.cpp index 16b8ca7fe8..992c893971 100644 --- a/Core/HLE/ReplaceTables.cpp +++ b/Core/HLE/ReplaceTables.cpp @@ -15,14 +15,18 @@ // Official git repository and contact information can be found at // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. +#include "ppsspp_config.h" #include #include #include #include "Common/Common.h" +#include "Common/Data/Convert/SmallDataConvert.h" #include "Common/Log.h" +#include "Common/Swap.h" #include "Core/Config.h" #include "Core/Debugger/Breakpoints.h" +#include "Core/Debugger/MemBlockInfo.h" #include "Core/Debugger/SymbolMap.h" #include "Core/MemMap.h" #include "Core/MIPS/JitCommon/JitCommon.h" @@ -36,7 +40,7 @@ #include "GPU/GPUInterface.h" #include "GPU/GPUState.h" -#if defined(_M_IX86) || defined(_M_X64) +#if PPSSPP_ARCH(X86) || PPSSPP_ARCH(AMD64) #include #endif @@ -152,8 +156,8 @@ static int Replace_memcpy() { } RETURN(destPtr); - CBreakPoints::ExecMemCheck(srcPtr, false, bytes, currentMIPS->pc); - CBreakPoints::ExecMemCheck(destPtr, true, bytes, currentMIPS->pc); + NotifyMemInfo(MemBlockFlags::READ, srcPtr, bytes, "ReplaceMemcpy"); + NotifyMemInfo(MemBlockFlags::WRITE, destPtr, bytes, "ReplaceMemcpy"); return 10 + bytes / 4; // approximation } @@ -194,8 +198,8 @@ static int Replace_memcpy_jak() { currentMIPS->r[MIPS_REG_A3] = destPtr + bytes; RETURN(destPtr); - CBreakPoints::ExecMemCheck(srcPtr, false, bytes, currentMIPS->pc); - CBreakPoints::ExecMemCheck(destPtr, true, bytes, currentMIPS->pc); + NotifyMemInfo(MemBlockFlags::READ, srcPtr, bytes, "ReplaceMemcpy"); + NotifyMemInfo(MemBlockFlags::WRITE, destPtr, bytes, "ReplaceMemcpy"); return 5 + bytes * 8 + 2; // approximation. This is a slow memcpy - a byte copy loop.. } @@ -222,8 +226,8 @@ static int Replace_memcpy16() { } RETURN(destPtr); - CBreakPoints::ExecMemCheck(srcPtr, false, bytes, currentMIPS->pc); - CBreakPoints::ExecMemCheck(destPtr, true, bytes, currentMIPS->pc); + NotifyMemInfo(MemBlockFlags::READ, srcPtr, bytes, "ReplaceMemcpy16"); + NotifyMemInfo(MemBlockFlags::WRITE, destPtr, bytes, "ReplaceMemcpy16"); return 10 + bytes / 4; // approximation } @@ -260,8 +264,8 @@ static int Replace_memcpy_swizzled() { RETURN(0); - CBreakPoints::ExecMemCheck(srcPtr, false, pitch * h, currentMIPS->pc); - CBreakPoints::ExecMemCheck(destPtr, true, pitch * h, currentMIPS->pc); + NotifyMemInfo(MemBlockFlags::READ, srcPtr, pitch * h, "ReplaceMemcpySwizzle"); + NotifyMemInfo(MemBlockFlags::WRITE, destPtr, pitch * h, "ReplaceMemcpySwizzle"); return 10 + (pitch * h) / 4; // approximation } @@ -288,8 +292,8 @@ static int Replace_memmove() { } RETURN(destPtr); - CBreakPoints::ExecMemCheck(srcPtr, false, bytes, currentMIPS->pc); - CBreakPoints::ExecMemCheck(destPtr, true, bytes, currentMIPS->pc); + NotifyMemInfo(MemBlockFlags::READ, srcPtr, bytes, "ReplaceMemmove"); + NotifyMemInfo(MemBlockFlags::WRITE, destPtr, bytes, "ReplaceMemmove"); return 10 + bytes / 4; // approximation } @@ -310,7 +314,7 @@ static int Replace_memset() { } RETURN(destPtr); - CBreakPoints::ExecMemCheck(destPtr, true, bytes, currentMIPS->pc); + NotifyMemInfo(MemBlockFlags::WRITE, destPtr, bytes, "ReplaceMemset"); return 10 + bytes / 4; // approximation } @@ -341,7 +345,7 @@ static int Replace_memset_jak() { currentMIPS->r[MIPS_REG_A3] = -1; RETURN(destPtr); - CBreakPoints::ExecMemCheck(destPtr, true, bytes, currentMIPS->pc); + NotifyMemInfo(MemBlockFlags::WRITE, destPtr, bytes, "ReplaceMemset"); return 5 + bytes * 6 + 2; // approximation (hm, inspecting the disasm this should be 5 + 6 * bytes + 2, but this is what works..) } @@ -406,13 +410,25 @@ static int Replace_fabsf() { } static int Replace_vmmul_q_transp() { - float *out = (float *)Memory::GetPointer(PARAM(0)); - const float *a = (const float *)Memory::GetPointer(PARAM(1)); - const float *b = (const float *)Memory::GetPointer(PARAM(2)); + float_le *out = (float_le *)Memory::GetPointer(PARAM(0)); + const float_le *a = (const float_le *)Memory::GetPointer(PARAM(1)); + const float_le *b = (const float_le *)Memory::GetPointer(PARAM(2)); // TODO: Actually use an optimized matrix multiply here... if (out && b && a) { +#ifdef COMMON_BIG_ENDIAN + float outn[16], an[16], bn[16]; + for (int i = 0; i < 16; ++i) { + an[i] = a[i]; + bn[i] = b[i]; + } + Matrix4ByMatrix4(outn, bn, an); + for (int i = 0; i < 16; ++i) { + out[i] = outn[i]; + } +#else Matrix4ByMatrix4(out, b, a); +#endif } return 16; } @@ -421,8 +437,8 @@ static int Replace_vmmul_q_transp() { // a1 = matrix // a2 = source address static int Replace_gta_dl_write_matrix() { - u32 *ptr = (u32 *)Memory::GetPointer(PARAM(0)); - u32 *src = (u32_le *)Memory::GetPointer(PARAM(2)); + u32_le *ptr = (u32_le *)Memory::GetPointer(PARAM(0)); + u32_le *src = (u32_le *)Memory::GetPointer(PARAM(2)); u32 matrix = PARAM(1) << 24; if (!ptr || !src) { @@ -430,13 +446,13 @@ static int Replace_gta_dl_write_matrix() { return 38; } - u32 *dest = (u32_le *)Memory::GetPointer(ptr[0]); + u32_le *dest = (u32_le *)Memory::GetPointer(ptr[0]); if (!dest) { RETURN(0); return 38; } -#if defined(_M_IX86) || defined(_M_X64) +#if PPSSPP_ARCH(X86) || PPSSPP_ARCH(AMD64) __m128i topBytes = _mm_set1_epi32(matrix); __m128i m0 = _mm_loadu_si128((const __m128i *)src); __m128i m1 = _mm_loadu_si128((const __m128i *)(src + 4)); @@ -480,15 +496,15 @@ static int Replace_gta_dl_write_matrix() { // TODO: Inline into a few NEON or SSE instructions - especially if a1 is a known immediate! // Anyway, not sure if worth it. There's not that many matrices written per frame normally. static int Replace_dl_write_matrix() { - u32 *dlStruct = (u32 *)Memory::GetPointer(PARAM(0)); - u32 *src = (u32 *)Memory::GetPointer(PARAM(2)); + u32_le *dlStruct = (u32_le *)Memory::GetPointer(PARAM(0)); + u32_le *src = (u32_le *)Memory::GetPointer(PARAM(2)); if (!dlStruct || !src) { RETURN(0); return 60; } - u32 *dest = (u32 *)Memory::GetPointer(dlStruct[2]); + u32_le *dest = (u32_le *)Memory::GetPointer(dlStruct[2]); if (!dest) { RETURN(0); return 60; @@ -518,7 +534,7 @@ static int Replace_dl_write_matrix() { if (count == 16) { // Ultra SIMD friendly! These intrinsics generate pretty much perfect code, // no point in hand rolling. -#if defined(_M_IX86) || defined(_M_X64) +#if PPSSPP_ARCH(X86) || PPSSPP_ARCH(AMD64) __m128i topBytes = _mm_set1_epi32(matrix); __m128i m0 = _mm_loadu_si128((const __m128i *)src); __m128i m1 = _mm_loadu_si128((const __m128i *)(src + 4)); @@ -554,7 +570,7 @@ static int Replace_dl_write_matrix() { } #endif } else { -#if defined(_M_IX86) || defined(_M_X64) +#if PPSSPP_ARCH(X86) || PPSSPP_ARCH(AMD64) __m128i topBytes = _mm_set1_epi32(matrix); __m128i m0 = _mm_loadu_si128((const __m128i *)src); __m128i m1 = _mm_loadu_si128((const __m128i *)(src + 4)); @@ -589,9 +605,9 @@ static int Replace_dl_write_matrix() { #endif } - CBreakPoints::ExecMemCheck(PARAM(2), false, count * sizeof(float), currentMIPS->pc); - CBreakPoints::ExecMemCheck(PARAM(0) + 2 * sizeof(u32), true, sizeof(u32), currentMIPS->pc); - CBreakPoints::ExecMemCheck(dlStruct[2], true, (count + 1) * sizeof(u32), currentMIPS->pc); + NotifyMemInfo(MemBlockFlags::READ, PARAM(2), count * sizeof(float), "ReplaceDLWriteMatrix"); + NotifyMemInfo(MemBlockFlags::WRITE, PARAM(0) + 2 * sizeof(u32), sizeof(u32), "ReplaceDLWriteMatrix"); + NotifyMemInfo(MemBlockFlags::WRITE, dlStruct[2], (count + 1) * sizeof(u32), "ReplaceDLWriteMatrix"); dlStruct[2] += (1 + count) * 4; RETURN(dlStruct[2]); @@ -639,7 +655,7 @@ static int Hook_godseaterburst_blit_texture() { const u32 fb_address = Memory::Read_U32(fb_info); if (Memory::IsVRAMAddress(fb_address)) { gpu->PerformMemoryDownload(fb_address, 0x00044000); - CBreakPoints::ExecMemCheck(fb_address, true, 0x00044000, currentMIPS->pc); + NotifyMemInfo(MemBlockFlags::WRITE, fb_address, 0x00044000, "godseaterburst_blit_texture"); } return 0; } @@ -653,7 +669,7 @@ static int Hook_hexyzforce_monoclome_thread() { const u32 fb_address = Memory::Read_U32(fb_info); if (Memory::IsVRAMAddress(fb_address)) { gpu->PerformMemoryDownload(fb_address, 0x00088000); - CBreakPoints::ExecMemCheck(fb_address, true, 0x00088000, currentMIPS->pc); + NotifyMemInfo(MemBlockFlags::WRITE, fb_address, 0x00088000, "hexyzforce_monoclome_thread"); } return 0; } @@ -670,7 +686,7 @@ static int Hook_topx_create_saveicon() { const u32 fb_address = currentMIPS->r[MIPS_REG_V0]; if (Memory::IsVRAMAddress(fb_address)) { gpu->PerformMemoryDownload(fb_address, 0x00044000); - CBreakPoints::ExecMemCheck(fb_address, true, 0x00044000, currentMIPS->pc); + NotifyMemInfo(MemBlockFlags::WRITE, fb_address, 0x00044000, "topx_create_saveicon"); } return 0; } @@ -679,7 +695,7 @@ static int Hook_ff1_battle_effect() { const u32 fb_address = currentMIPS->r[MIPS_REG_A1]; if (Memory::IsVRAMAddress(fb_address)) { gpu->PerformMemoryDownload(fb_address, 0x00088000); - CBreakPoints::ExecMemCheck(fb_address, true, 0x00088000, currentMIPS->pc); + NotifyMemInfo(MemBlockFlags::WRITE, fb_address, 0x00088000, "ff1_battle_effect"); } return 0; } @@ -689,7 +705,7 @@ static int Hook_dissidia_recordframe_avi() { const u32 fb_address = currentMIPS->r[MIPS_REG_A1]; if (Memory::IsVRAMAddress(fb_address)) { gpu->PerformMemoryDownload(fb_address, 0x00044000); - CBreakPoints::ExecMemCheck(fb_address, true, 0x00044000, currentMIPS->pc); + NotifyMemInfo(MemBlockFlags::WRITE, fb_address, 0x00044000, "dissidia_recordframe_avi"); } return 0; } @@ -710,7 +726,7 @@ static int Hook_brandish_download_frame() { const u32 dest_address = currentMIPS->r[MIPS_REG_A1]; if (Memory::IsRAMAddress(dest_address)) { gpu->PerformMemoryDownload(fb_address, 0x00044000); - CBreakPoints::ExecMemCheck(fb_address, true, 0x00044000, currentMIPS->pc); + NotifyMemInfo(MemBlockFlags::WRITE, fb_address, 0x00044000, "brandish_download_frame"); } return 0; } @@ -721,7 +737,7 @@ static int Hook_growlanser_create_saveicon() { const u32 sz = fmt == GE_FORMAT_8888 ? 0x00088000 : 0x00044000; if (Memory::IsVRAMAddress(fb_address) && fmt <= 3) { gpu->PerformMemoryDownload(fb_address, sz); - CBreakPoints::ExecMemCheck(fb_address, true, sz, currentMIPS->pc); + NotifyMemInfo(MemBlockFlags::WRITE, fb_address, sz, "growlanser_create_saveicon"); } return 0; } @@ -732,7 +748,7 @@ static int Hook_sd_gundam_g_generation_download_frame() { const u32 sz = fmt == GE_FORMAT_8888 ? 0x00088000 : 0x00044000; if (Memory::IsVRAMAddress(fb_address) && fmt <= 3) { gpu->PerformMemoryDownload(fb_address, sz); - CBreakPoints::ExecMemCheck(fb_address, true, sz, currentMIPS->pc); + NotifyMemInfo(MemBlockFlags::WRITE, fb_address, sz, "sd_gundam_g_generation_download_frame"); } return 0; } @@ -741,7 +757,7 @@ static int Hook_narisokonai_download_frame() { const u32 fb_address = currentMIPS->r[MIPS_REG_V0]; if (Memory::IsVRAMAddress(fb_address)) { gpu->PerformMemoryDownload(fb_address, 0x00044000); - CBreakPoints::ExecMemCheck(fb_address, true, 0x00044000, currentMIPS->pc); + NotifyMemInfo(MemBlockFlags::WRITE, fb_address, 0x00044000, "narisokonai_download_frame"); } return 0; } @@ -750,7 +766,7 @@ static int Hook_kirameki_school_life_download_frame() { const u32 fb_address = currentMIPS->r[MIPS_REG_A2]; if (Memory::IsVRAMAddress(fb_address)) { gpu->PerformMemoryDownload(fb_address, 0x00088000); - CBreakPoints::ExecMemCheck(fb_address, true, 0x00088000, currentMIPS->pc); + NotifyMemInfo(MemBlockFlags::WRITE, fb_address, 0x00088000, "kirameki_school_life_download_frame"); } return 0; } @@ -759,7 +775,7 @@ static int Hook_orenoimouto_download_frame() { const u32 fb_address = currentMIPS->r[MIPS_REG_A4]; if (Memory::IsVRAMAddress(fb_address)) { gpu->PerformMemoryDownload(fb_address, 0x00088000); - CBreakPoints::ExecMemCheck(fb_address, true, 0x00088000, currentMIPS->pc); + NotifyMemInfo(MemBlockFlags::WRITE, fb_address, 0x00088000, "orenoimouto_download_frame"); } return 0; } @@ -768,7 +784,7 @@ static int Hook_sakurasou_download_frame() { const u32 fb_address = currentMIPS->r[MIPS_REG_V0]; if (Memory::IsVRAMAddress(fb_address)) { gpu->PerformMemoryDownload(fb_address, 0x00088000); - CBreakPoints::ExecMemCheck(fb_address, true, 0x00088000, currentMIPS->pc); + NotifyMemInfo(MemBlockFlags::WRITE, fb_address, 0x00088000, "sakurasou_download_frame"); } return 0; } @@ -777,7 +793,7 @@ static int Hook_suikoden1_and_2_download_frame_1() { const u32 fb_address = currentMIPS->r[MIPS_REG_S4]; if (Memory::IsVRAMAddress(fb_address)) { gpu->PerformMemoryDownload(fb_address, 0x00088000); - CBreakPoints::ExecMemCheck(fb_address, true, 0x00088000, currentMIPS->pc); + NotifyMemInfo(MemBlockFlags::WRITE, fb_address, 0x00088000, "suikoden1_and_2_download_frame_1"); } return 0; } @@ -786,7 +802,7 @@ static int Hook_suikoden1_and_2_download_frame_2() { const u32 fb_address = currentMIPS->r[MIPS_REG_S2]; if (Memory::IsVRAMAddress(fb_address)) { gpu->PerformMemoryDownload(fb_address, 0x00088000); - CBreakPoints::ExecMemCheck(fb_address, true, 0x00088000, currentMIPS->pc); + NotifyMemInfo(MemBlockFlags::WRITE, fb_address, 0x00088000, "suikoden1_and_2_download_frame_2"); } return 0; } @@ -797,7 +813,7 @@ static int Hook_rezel_cross_download_frame() { const u32 sz = fmt == GE_FORMAT_8888 ? 0x00088000 : 0x00044000; if (Memory::IsVRAMAddress(fb_address) && fmt <= 3) { gpu->PerformMemoryDownload(fb_address, sz); - CBreakPoints::ExecMemCheck(fb_address, true, sz, currentMIPS->pc); + NotifyMemInfo(MemBlockFlags::WRITE, fb_address, sz, "rezel_cross_download_frame"); } return 0; } @@ -806,7 +822,7 @@ static int Hook_kagaku_no_ensemble_download_frame() { const u32 fb_address = currentMIPS->r[MIPS_REG_V0]; if (Memory::IsVRAMAddress(fb_address)) { gpu->PerformMemoryDownload(fb_address, 0x00088000); - CBreakPoints::ExecMemCheck(fb_address, true, 0x00088000, currentMIPS->pc); + NotifyMemInfo(MemBlockFlags::WRITE, fb_address, 0x00088000, "kagaku_no_ensemble_download_frame"); } return 0; } @@ -815,7 +831,7 @@ static int Hook_soranokiseki_fc_download_frame() { const u32 fb_address = currentMIPS->r[MIPS_REG_A2]; if (Memory::IsVRAMAddress(fb_address)) { gpu->PerformMemoryDownload(fb_address, 0x00044000); - CBreakPoints::ExecMemCheck(fb_address, true, 0x00044000, currentMIPS->pc); + NotifyMemInfo(MemBlockFlags::WRITE, fb_address, 0x00044000, "soranokiseki_fc_download_frame"); } return 0; } @@ -836,7 +852,7 @@ static int Hook_soranokiseki_sc_download_frame() { const u32 dest_address = currentMIPS->r[MIPS_REG_A1]; if (Memory::IsRAMAddress(dest_address)) { gpu->PerformMemoryDownload(fb_address, 0x00044000); - CBreakPoints::ExecMemCheck(fb_address, true, 0x00044000, currentMIPS->pc); + NotifyMemInfo(MemBlockFlags::WRITE, fb_address, 0x00044000, "soranokiseki_sc_download_frame"); } return 0; } @@ -845,7 +861,7 @@ static int Hook_bokunonatsuyasumi4_download_frame() { const u32 fb_address = currentMIPS->r[MIPS_REG_A3]; if (Memory::IsVRAMAddress(fb_address)) { gpu->PerformMemoryDownload(fb_address, 0x00044000); - CBreakPoints::ExecMemCheck(fb_address, true, 0x00044000, currentMIPS->pc); + NotifyMemInfo(MemBlockFlags::WRITE, fb_address, 0x00044000, "bokunonatsuyasumi4_download_frame"); } return 0; } @@ -857,7 +873,7 @@ static int Hook_danganronpa2_1_download_frame() { const u32 fb_address = fb_base + fb_offset_fix; if (Memory::IsVRAMAddress(fb_address)) { gpu->PerformMemoryDownload(fb_address, 0x00088000); - CBreakPoints::ExecMemCheck(fb_address, true, 0x00088000, currentMIPS->pc); + NotifyMemInfo(MemBlockFlags::WRITE, fb_address, 0x00088000, "danganronpa2_1_download_frame"); } return 0; } @@ -869,7 +885,7 @@ static int Hook_danganronpa2_2_download_frame() { const u32 fb_address = fb_base + fb_offset_fix; if (Memory::IsVRAMAddress(fb_address)) { gpu->PerformMemoryDownload(fb_address, 0x00088000); - CBreakPoints::ExecMemCheck(fb_address, true, 0x00088000, currentMIPS->pc); + NotifyMemInfo(MemBlockFlags::WRITE, fb_address, 0x00088000, "danganronpa2_2_download_frame"); } return 0; } @@ -881,7 +897,7 @@ static int Hook_danganronpa1_1_download_frame() { const u32 fb_address = fb_base + fb_offset_fix; if (Memory::IsVRAMAddress(fb_address)) { gpu->PerformMemoryDownload(fb_address, 0x00088000); - CBreakPoints::ExecMemCheck(fb_address, true, 0x00088000, currentMIPS->pc); + NotifyMemInfo(MemBlockFlags::WRITE, fb_address, 0x00088000, "danganronpa1_1_download_frame"); } return 0; } @@ -895,7 +911,7 @@ static int Hook_danganronpa1_2_download_frame() { const u32 fb_address = fb_base + fb_offset_fix; if (Memory::IsVRAMAddress(fb_address)) { gpu->PerformMemoryDownload(fb_address, 0x00088000); - CBreakPoints::ExecMemCheck(fb_address, true, 0x00088000, currentMIPS->pc); + NotifyMemInfo(MemBlockFlags::WRITE, fb_address, 0x00088000, "danganronpa1_2_download_frame"); } return 0; } @@ -904,7 +920,7 @@ static int Hook_kankabanchoutbr_download_frame() { const u32 fb_address = currentMIPS->r[MIPS_REG_A1]; if (Memory::IsVRAMAddress(fb_address)) { gpu->PerformMemoryDownload(fb_address, 0x00044000); - CBreakPoints::ExecMemCheck(fb_address, true, 0x00044000, currentMIPS->pc); + NotifyMemInfo(MemBlockFlags::WRITE, fb_address, 0x00044000, "kankabanchoutbr_download_frame"); } return 0; } @@ -913,7 +929,7 @@ static int Hook_orenoimouto_download_frame_2() { const u32 fb_address = currentMIPS->r[MIPS_REG_A4]; if (Memory::IsVRAMAddress(fb_address)) { gpu->PerformMemoryDownload(fb_address, 0x00088000); - CBreakPoints::ExecMemCheck(fb_address, true, 0x00088000, currentMIPS->pc); + NotifyMemInfo(MemBlockFlags::WRITE, fb_address, 0x00088000, "orenoimouto_download_frame_2"); } return 0; } @@ -922,7 +938,7 @@ static int Hook_rewrite_download_frame() { const u32 fb_address = currentMIPS->r[MIPS_REG_A0]; if (Memory::IsVRAMAddress(fb_address)) { gpu->PerformMemoryDownload(fb_address, 0x00088000); - CBreakPoints::ExecMemCheck(fb_address, true, 0x00088000, currentMIPS->pc); + NotifyMemInfo(MemBlockFlags::WRITE, fb_address, 0x00088000, "rewrite_download_frame"); } return 0; } @@ -931,7 +947,7 @@ static int Hook_kudwafter_download_frame() { const u32 fb_address = currentMIPS->r[MIPS_REG_A0]; if (Memory::IsVRAMAddress(fb_address)) { gpu->PerformMemoryDownload(fb_address, 0x00088000); - CBreakPoints::ExecMemCheck(fb_address, true, 0x00088000, currentMIPS->pc); + NotifyMemInfo(MemBlockFlags::WRITE, fb_address, 0x00088000, "kudwafter_download_frame"); } return 0; } @@ -940,7 +956,7 @@ static int Hook_kumonohatateni_download_frame() { const u32 fb_address = currentMIPS->r[MIPS_REG_A0]; if (Memory::IsVRAMAddress(fb_address)) { gpu->PerformMemoryDownload(fb_address, 0x00088000); - CBreakPoints::ExecMemCheck(fb_address, true, 0x00088000, currentMIPS->pc); + NotifyMemInfo(MemBlockFlags::WRITE, fb_address, 0x00088000, "kumonohatateni_download_frame"); } return 0; } @@ -949,7 +965,7 @@ static int Hook_otomenoheihou_download_frame() { const u32 fb_address = currentMIPS->r[MIPS_REG_A0]; if (Memory::IsVRAMAddress(fb_address)) { gpu->PerformMemoryDownload(fb_address, 0x00088000); - CBreakPoints::ExecMemCheck(fb_address, true, 0x00088000, currentMIPS->pc); + NotifyMemInfo(MemBlockFlags::WRITE, fb_address, 0x00088000, "otomenoheihou_download_frame"); } return 0; } @@ -958,7 +974,7 @@ static int Hook_grisaianokajitsu_download_frame() { const u32 fb_address = currentMIPS->r[MIPS_REG_A0]; if (Memory::IsVRAMAddress(fb_address)) { gpu->PerformMemoryDownload(fb_address, 0x00088000); - CBreakPoints::ExecMemCheck(fb_address, true, 0x00088000, currentMIPS->pc); + NotifyMemInfo(MemBlockFlags::WRITE, fb_address, 0x00088000, "grisaianokajitsu_download_frame"); } return 0; } @@ -967,7 +983,7 @@ static int Hook_kokoroconnect_download_frame() { const u32 fb_address = currentMIPS->r[MIPS_REG_A3]; if (Memory::IsVRAMAddress(fb_address)) { gpu->PerformMemoryDownload(fb_address, 0x00088000); - CBreakPoints::ExecMemCheck(fb_address, true, 0x00088000, currentMIPS->pc); + NotifyMemInfo(MemBlockFlags::WRITE, fb_address, 0x00088000, "kokoroconnect_download_frame"); } return 0; } @@ -976,7 +992,7 @@ static int Hook_toheart2_download_frame() { const u32 fb_address = currentMIPS->r[MIPS_REG_A1]; if (Memory::IsVRAMAddress(fb_address)) { gpu->PerformMemoryDownload(fb_address, 0x00044000); - CBreakPoints::ExecMemCheck(fb_address, true, 0x00044000, currentMIPS->pc); + NotifyMemInfo(MemBlockFlags::WRITE, fb_address, 0x00044000, "toheart2_download_frame"); } return 0; } @@ -985,7 +1001,7 @@ static int Hook_toheart2_download_frame_2() { const u32 fb_address = currentMIPS->r[MIPS_REG_A0]; if (Memory::IsVRAMAddress(fb_address)) { gpu->PerformMemoryDownload(fb_address, 0x00088000); - CBreakPoints::ExecMemCheck(fb_address, true, 0x00088000, currentMIPS->pc); + NotifyMemInfo(MemBlockFlags::WRITE, fb_address, 0x00088000, "toheart2_download_frame_2"); } return 0; } @@ -994,7 +1010,7 @@ static int Hook_flowers_download_frame() { const u32 fb_address = currentMIPS->r[MIPS_REG_A0]; if (Memory::IsVRAMAddress(fb_address)) { gpu->PerformMemoryDownload(fb_address, 0x00088000); - CBreakPoints::ExecMemCheck(fb_address, true, 0x00088000, currentMIPS->pc); + NotifyMemInfo(MemBlockFlags::WRITE, fb_address, 0x00088000, "flowers_download_frame"); } return 0; } @@ -1003,7 +1019,7 @@ static int Hook_motorstorm_download_frame() { const u32 fb_address = Memory::Read_U32(currentMIPS->r[MIPS_REG_A1] + 0x18); if (Memory::IsVRAMAddress(fb_address)) { gpu->PerformMemoryDownload(fb_address, 0x00088000); - CBreakPoints::ExecMemCheck(fb_address, true, 0x00088000, currentMIPS->pc); + NotifyMemInfo(MemBlockFlags::WRITE, fb_address, 0x00088000, "motorstorm_download_frame"); } return 0; } @@ -1012,7 +1028,7 @@ static int Hook_utawarerumono_download_frame() { const u32 fb_address = currentMIPS->r[MIPS_REG_A0]; if (Memory::IsVRAMAddress(fb_address)) { gpu->PerformMemoryDownload(fb_address, 0x00088000); - CBreakPoints::ExecMemCheck(fb_address, true, 0x00088000, currentMIPS->pc); + NotifyMemInfo(MemBlockFlags::WRITE, fb_address, 0x00088000, "utawarerumono_download_frame"); } return 0; } @@ -1021,7 +1037,7 @@ static int Hook_photokano_download_frame() { const u32 fb_address = currentMIPS->r[MIPS_REG_A1]; if (Memory::IsVRAMAddress(fb_address)) { gpu->PerformMemoryDownload(fb_address, 0x00088000); - CBreakPoints::ExecMemCheck(fb_address, true, 0x00088000, currentMIPS->pc); + NotifyMemInfo(MemBlockFlags::WRITE, fb_address, 0x00088000, "photokano_download_frame"); } return 0; } @@ -1030,7 +1046,7 @@ static int Hook_photokano_download_frame_2() { const u32 fb_address = currentMIPS->r[MIPS_REG_A1]; if (Memory::IsVRAMAddress(fb_address)) { gpu->PerformMemoryDownload(fb_address, 0x00088000); - CBreakPoints::ExecMemCheck(fb_address, true, 0x00088000, currentMIPS->pc); + NotifyMemInfo(MemBlockFlags::WRITE, fb_address, 0x00088000, "photokano_download_frame_2"); } return 0; } @@ -1039,7 +1055,7 @@ static int Hook_gakuenheaven_download_frame() { const u32 fb_address = currentMIPS->r[MIPS_REG_A0]; if (Memory::IsVRAMAddress(fb_address)) { gpu->PerformMemoryDownload(fb_address, 0x00088000); - CBreakPoints::ExecMemCheck(fb_address, true, 0x00088000, currentMIPS->pc); + NotifyMemInfo(MemBlockFlags::WRITE, fb_address, 0x00088000, "gakuenheaven_download_frame"); } return 0; } @@ -1048,7 +1064,21 @@ static int Hook_youkosohitsujimura_download_frame() { const u32 fb_address = currentMIPS->r[MIPS_REG_V0]; if (Memory::IsVRAMAddress(fb_address)) { gpu->PerformMemoryDownload(fb_address, 0x00088000); - CBreakPoints::ExecMemCheck(fb_address, true, 0x00088000, currentMIPS->pc); + NotifyMemInfo(MemBlockFlags::WRITE, fb_address, 0x00088000, "youkosohitsujimura_download_frame"); + } + return 0; +} + +static int Hook_zettai_hero_update_minimap_tex() { + const MIPSOpcode storeOffset = Memory::Read_Instruction(currentMIPS->pc + 4, true); + const uint32_t texAddr = currentMIPS->r[MIPS_REG_A0] + SignExtend16ToS32(storeOffset); + const uint32_t texSize = 64 * 64 * 1; + const uint32_t writeAddr = currentMIPS->r[MIPS_REG_V1] + SignExtend16ToS32(storeOffset); + if (Memory::IsValidRange(texAddr, texSize) && writeAddr >= texAddr && writeAddr < texAddr + texSize) { + const uint8_t currentValue = Memory::Read_U8(writeAddr); + if (currentValue != currentMIPS->r[MIPS_REG_A3]) { + gpu->InvalidateCache(texAddr, texSize, GPU_INVALIDATE_FORCE); + } } return 0; } @@ -1065,7 +1095,7 @@ static int Hook_sdgundamggenerationportable_download_frame() { const u32 fb_address = currentMIPS->r[MIPS_REG_A3]; if (Memory::IsVRAMAddress(fb_address)) { gpu->PerformMemoryDownload(fb_address, 0x00088000); - CBreakPoints::ExecMemCheck(fb_address, true, 0x00088000, currentMIPS->pc); + NotifyMemInfo(MemBlockFlags::WRITE, fb_address, 0x00088000, "sdgundamggenerationportable_download_frame"); } return 0; } @@ -1075,7 +1105,7 @@ static int Hook_atvoffroadfurypro_download_frame() { const u32 fb_size = (currentMIPS->r[MIPS_REG_S4] >> 3) * currentMIPS->r[MIPS_REG_S3]; if (Memory::IsVRAMAddress(fb_address)) { gpu->PerformMemoryDownload(fb_address, fb_size); - CBreakPoints::ExecMemCheck(fb_address, true, fb_size, currentMIPS->pc); + NotifyMemInfo(MemBlockFlags::WRITE, fb_address, fb_size, "atvoffroadfurypro_download_frame"); } return 0; } @@ -1085,7 +1115,7 @@ static int Hook_atvoffroadfuryblazintrails_download_frame() { const u32 fb_size = (currentMIPS->r[MIPS_REG_S3] >> 3) * currentMIPS->r[MIPS_REG_S2]; if (Memory::IsVRAMAddress(fb_address)) { gpu->PerformMemoryDownload(fb_address, fb_size); - CBreakPoints::ExecMemCheck(fb_address, true, fb_size, currentMIPS->pc); + NotifyMemInfo(MemBlockFlags::WRITE, fb_address, fb_size, "atvoffroadfuryblazintrails_download_frame"); } return 0; } @@ -1094,7 +1124,7 @@ static int Hook_littlebustersce_download_frame() { const u32 fb_address = currentMIPS->r[MIPS_REG_A0]; if (Memory::IsVRAMAddress(fb_address)) { gpu->PerformMemoryDownload(fb_address, 0x00088000); - CBreakPoints::ExecMemCheck(fb_address, true, 0x00088000, currentMIPS->pc); + NotifyMemInfo(MemBlockFlags::WRITE, fb_address, 0x00088000, "littlebustersce_download_frame"); } return 0; } @@ -1103,7 +1133,7 @@ static int Hook_shinigamitoshoujo_download_frame() { const u32 fb_address = currentMIPS->r[MIPS_REG_S2]; if (Memory::IsVRAMAddress(fb_address)) { gpu->PerformMemoryDownload(fb_address, 0x00088000); - CBreakPoints::ExecMemCheck(fb_address, true, 0x00088000, currentMIPS->pc); + NotifyMemInfo(MemBlockFlags::WRITE, fb_address, 0x00088000, "shinigamitoshoujo_download_frame"); } return 0; } @@ -1113,7 +1143,7 @@ static int Hook_atvoffroadfuryprodemo_download_frame() { const u32 fb_size = ((currentMIPS->r[MIPS_REG_A0] + currentMIPS->r[MIPS_REG_A1]) >> 3) * currentMIPS->r[MIPS_REG_S2]; if (Memory::IsVRAMAddress(fb_address)) { gpu->PerformMemoryDownload(fb_address, fb_size); - CBreakPoints::ExecMemCheck(fb_address, true, fb_size, currentMIPS->pc); + NotifyMemInfo(MemBlockFlags::WRITE, fb_address, fb_size, "atvoffroadfuryprodemo_download_frame"); } return 0; } @@ -1122,7 +1152,7 @@ static int Hook_unendingbloodycall_download_frame() { const u32 fb_address = currentMIPS->r[MIPS_REG_T3]; if (Memory::IsVRAMAddress(fb_address)) { gpu->PerformMemoryDownload(fb_address, 0x00088000); - CBreakPoints::ExecMemCheck(fb_address, true, 0x00088000, currentMIPS->pc); + NotifyMemInfo(MemBlockFlags::WRITE, fb_address, 0x00088000, "unendingbloodycall_download_frame"); } return 0; } @@ -1131,7 +1161,7 @@ static int Hook_omertachinmokunookitethelegacy_download_frame() { const u32 fb_address = Memory::Read_U32(currentMIPS->r[MIPS_REG_SP] + 4); if (Memory::IsVRAMAddress(fb_address)) { gpu->PerformMemoryDownload(fb_address, 0x00044000); - CBreakPoints::ExecMemCheck(fb_address, true, 0x00044000, currentMIPS->pc); + NotifyMemInfo(MemBlockFlags::WRITE, fb_address, 0x00044000, "omertachinmokunookitethelegacy_download_frame"); } return 0; } @@ -1151,7 +1181,7 @@ static int Hook_katamari_render_check() { const u32 totalBytes = width * heightBlocks * heightBlockCount; gpu->PerformMemoryDownload(fb_address, totalBytes); - CBreakPoints::ExecMemCheck(fb_address, true, totalBytes, currentMIPS->pc); + NotifyMemInfo(MemBlockFlags::WRITE, fb_address, totalBytes, "katamari_render_check"); } return 0; } @@ -1160,7 +1190,7 @@ static int Hook_katamari_screenshot_to_565() { u32 fb_address; if (GetMIPSStaticAddress(fb_address, 0x0040, 0x0044)) { gpu->PerformMemoryDownload(0x04000000 | fb_address, 0x00088000); - CBreakPoints::ExecMemCheck(0x04000000 | fb_address, true, 0x00088000, currentMIPS->pc); + NotifyMemInfo(MemBlockFlags::WRITE, 0x04000000 | fb_address, 0x00088000, "katamari_screenshot_to_565"); } return 0; } @@ -1183,7 +1213,7 @@ static int Hook_marvelalliance1_copy_a1_before() { marvelalliance1_copy_size = currentMIPS->r[MIPS_REG_V0] - currentMIPS->r[MIPS_REG_V1]; gpu->PerformMemoryDownload(marvelalliance1_copy_src, marvelalliance1_copy_size); - CBreakPoints::ExecMemCheck(marvelalliance1_copy_src, true, marvelalliance1_copy_size, currentMIPS->pc); + NotifyMemInfo(MemBlockFlags::WRITE, marvelalliance1_copy_src, marvelalliance1_copy_size, "marvelalliance1_copy_a1_before"); return 0; } @@ -1194,14 +1224,14 @@ static int Hook_marvelalliance1_copy_a2_before() { marvelalliance1_copy_size = currentMIPS->r[MIPS_REG_A1] - currentMIPS->r[MIPS_REG_A2]; gpu->PerformMemoryDownload(marvelalliance1_copy_src, marvelalliance1_copy_size); - CBreakPoints::ExecMemCheck(marvelalliance1_copy_src, true, marvelalliance1_copy_size, currentMIPS->pc); + NotifyMemInfo(MemBlockFlags::WRITE, marvelalliance1_copy_src, marvelalliance1_copy_size, "marvelalliance1_copy_a2_before"); return 0; } static int Hook_marvelalliance1_copy_after() { gpu->PerformMemoryUpload(marvelalliance1_copy_dst, marvelalliance1_copy_size); - CBreakPoints::ExecMemCheck(marvelalliance1_copy_dst, false, marvelalliance1_copy_size, currentMIPS->pc); + NotifyMemInfo(MemBlockFlags::READ, marvelalliance1_copy_dst, marvelalliance1_copy_size, "marvelalliance1_copy_after"); return 0; } @@ -1234,7 +1264,35 @@ static int Hook_motorstorm_pixel_read() { u32 fb_height = Memory::Read_U16(currentMIPS->r[MIPS_REG_A0] + 0x26); u32 fb_stride = Memory::Read_U16(currentMIPS->r[MIPS_REG_A0] + 0x28); gpu->PerformMemoryDownload(fb_address, fb_height * fb_stride); - CBreakPoints::ExecMemCheck(fb_address, true, fb_height * fb_stride, currentMIPS->pc); + NotifyMemInfo(MemBlockFlags::WRITE, fb_address, fb_height * fb_stride, "motorstorm_pixel_read"); + return 0; +} + +static int Hook_worms_copy_normalize_alpha() { + // At this point in the function (0x0CC), s1 is the framebuf and a2 is the size. + u32 fb_address = currentMIPS->r[MIPS_REG_S1]; + u32 fb_size = currentMIPS->r[MIPS_REG_A2]; + if (Memory::IsVRAMAddress(fb_address) && Memory::IsValidRange(fb_address, fb_size)) { + gpu->PerformMemoryDownload(fb_address, fb_size); + NotifyMemInfo(MemBlockFlags::WRITE, fb_address, fb_size, "worms_copy_normalize_alpha"); + } + return 0; +} + +static int Hook_openseason_data_decode() { + static u32 firstWritePtr = 0; + + u32 curWritePtr = currentMIPS->r[MIPS_REG_A0]; + u32 endPtr = currentMIPS->r[MIPS_REG_A1]; + u32 writeBytes = currentMIPS->r[MIPS_REG_V0]; + u32 startPtr = curWritePtr - writeBytes; + if (Memory::IsVRAMAddress(startPtr) && (firstWritePtr == 0 || startPtr < firstWritePtr)) { + firstWritePtr = startPtr; + } + if (Memory::IsVRAMAddress(endPtr) && curWritePtr == endPtr) { + gpu->PerformMemoryUpload(firstWritePtr, endPtr - firstWritePtr); + firstWritePtr = 0; + } return 0; } @@ -1323,6 +1381,7 @@ static const ReplacementTableEntry entries[] = { { "photokano_download_frame_2", &Hook_photokano_download_frame_2, 0, REPFLAG_HOOKENTER, }, { "gakuenheaven_download_frame", &Hook_gakuenheaven_download_frame, 0, REPFLAG_HOOKENTER, }, { "youkosohitsujimura_download_frame", &Hook_youkosohitsujimura_download_frame, 0, REPFLAG_HOOKENTER, 0x94 }, + { "zettai_hero_update_minimap_tex", &Hook_zettai_hero_update_minimap_tex, 0, REPFLAG_HOOKEXIT, }, { "tonyhawkp8_upload_tutorial_frame", &Hook_tonyhawkp8_upload_tutorial_frame, 0, REPFLAG_HOOKENTER, }, { "sdgundamggenerationportable_download_frame", &Hook_sdgundamggenerationportable_download_frame, 0, REPFLAG_HOOKENTER, 0x34 }, { "atvoffroadfurypro_download_frame", &Hook_atvoffroadfurypro_download_frame, 0, REPFLAG_HOOKENTER, 0xA0 }, @@ -1350,6 +1409,8 @@ static const ReplacementTableEntry entries[] = { { "starocean_clear_framebuf", &Hook_starocean_clear_framebuf_before, 0, REPFLAG_HOOKENTER, 0 }, { "starocean_clear_framebuf", &Hook_starocean_clear_framebuf_after, 0, REPFLAG_HOOKEXIT, 0 }, { "motorstorm_pixel_read", &Hook_motorstorm_pixel_read, 0, REPFLAG_HOOKENTER, 0 }, + { "worms_copy_normalize_alpha", &Hook_worms_copy_normalize_alpha, 0, REPFLAG_HOOKENTER, 0x0CC }, + { "openseason_data_decode", &Hook_openseason_data_decode, 0, REPFLAG_HOOKENTER, 0x2F0 }, {} }; diff --git a/Core/HLE/proAdhoc.cpp b/Core/HLE/proAdhoc.cpp index 5a0ec169a7..98891072eb 100644 --- a/Core/HLE/proAdhoc.cpp +++ b/Core/HLE/proAdhoc.cpp @@ -21,7 +21,10 @@ // This is a direct port of Coldbird's code from http://code.google.com/p/aemu/ // All credit goes to him! +#include "ppsspp_config.h" + #if defined(_WIN32) +#include #include "Common/CommonWindows.h" #endif @@ -31,7 +34,9 @@ #include #include #include +#if !PPSSPP_PLATFORM(SWITCH) #include +#endif // !PPSSPP_PLATFORM(SWITCH) #endif #ifndef MSG_NOSIGNAL @@ -39,6 +44,13 @@ #define MSG_NOSIGNAL 0x00 #endif +#if defined(HAVE_LIBNX) || PPSSPP_PLATFORM(SWITCH) +#undef __BSD_VISIBLE +#define __BSD_VISIBLE 1 +#include +#define TCP_MAXSEG 2 +#endif // defined(HAVE_LIBNX) || PPSSPP_PLATFORM(SWITCH) + #include #include "Common/Data/Text/I18n.h" @@ -56,10 +68,17 @@ #include "Core/Instance.h" #include "proAdhoc.h" +#if PPSSPP_PLATFORM(SWITCH) && !defined(INADDR_NONE) +// Missing toolchain define +#define INADDR_NONE 0xFFFFFFFF +#endif + uint16_t portOffset; uint32_t minSocketTimeoutUS; uint32_t fakePoolSize = 0; SceNetAdhocMatchingContext * contexts = NULL; +char* dummyPeekBuf64k = NULL; +int dummyPeekBuf64kSize = 65536; int one = 1; bool friendFinderRunning = false; SceNetAdhocctlPeerInfo * friends = NULL; @@ -128,21 +147,32 @@ bool isPDPPortInUse(uint16_t port) { return false; } -bool isPTPPortInUse(uint16_t port, bool forListen) { +bool isPTPPortInUse(uint16_t port, bool forListen, SceNetEtherAddr* dstmac, uint16_t dstport) { // Iterate Sockets for (int i = 0; i < MAX_SOCKET; i++) { auto sock = adhocSockets[i]; if (sock != NULL && sock->type == SOCK_PTP) - // It's allowed to Listen and Open the same PTP port, But it's not allowed to Listen or Open the same PTP port twice. - if (sock->data.ptp.lport == port && - ((forListen && sock->data.ptp.state == ADHOC_PTP_STATE_LISTEN) || - (!forListen && sock->data.ptp.state != ADHOC_PTP_STATE_LISTEN))) + // It's allowed to Listen and Open the same PTP port, But it's not allowed to Listen or Open the same PTP port twice (unless destination mac or port are different). + if (sock->data.ptp.lport == port && + ((forListen && sock->data.ptp.state == ADHOC_PTP_STATE_LISTEN) || + (!forListen && sock->data.ptp.state != ADHOC_PTP_STATE_LISTEN && + sock->data.ptp.pport == dstport && dstmac != nullptr && isMacMatch(&sock->data.ptp.paddr, dstmac)))) + { return true; + } } // Unused Port return false; } +// Replacement for inet_ntoa since it's getting deprecated +std::string ip2str(in_addr in) { + char str[INET_ADDRSTRLEN] = "..."; + u8* ipptr = (u8*)∈ + snprintf(str, sizeof(str), "%u.%u.%u.%u", ipptr[0], ipptr[1], ipptr[2], ipptr[3]); + return std::string(str); +} + std::string mac2str(SceNetEtherAddr* mac) { char str[18] = ":::::"; @@ -190,7 +220,7 @@ void addFriend(SceNetAdhocctlConnectPacketS2C * packet) { // Already existed if (peer != NULL) { u32 tmpip = packet->ip; - WARN_LOG(SCENET, "Friend Peer Already Existed! Updating [%s][%s][%s]", mac2str(&packet->mac).c_str(), inet_ntoa(*(struct in_addr*)&tmpip), packet->name.data); //inet_ntoa(*(in_addr*)&packet->ip) + WARN_LOG(SCENET, "Friend Peer Already Existed! Updating [%s][%s][%s]", mac2str(&packet->mac).c_str(), ip2str(*(struct in_addr*)&tmpip).c_str(), packet->name.data); //inet_ntoa(*(in_addr*)&packet->ip) peer->nickname = packet->name; peer->mac_addr = packet->mac; peer->ip_addr = packet->ip; @@ -431,7 +461,7 @@ void deleteFriendByIP(uint32_t ip) { */ u32 tmpip = peer->ip_addr; - INFO_LOG(SCENET, "Removing Friend Peer %s [%s]", mac2str(&peer->mac_addr).c_str(), inet_ntoa(*(struct in_addr *)&tmpip)); //inet_ntoa(*(in_addr*)&peer->ip_addr) + INFO_LOG(SCENET, "Removing Friend Peer %s [%s]", mac2str(&peer->mac_addr).c_str(), ip2str(*(struct in_addr *)&tmpip).c_str()); //inet_ntoa(*(in_addr*)&peer->ip_addr) // Free Memory //free(peer); @@ -1136,18 +1166,18 @@ void AfterMatchingMipsCall::SetData(int ContextID, int eventId, u32_le BufAddr) bool SetMatchingInCallback(SceNetAdhocMatchingContext* context, bool IsInCB) { if (context == NULL) return false; - context->eventlock->lock(); //peerlock.lock(); + peerlock.lock(); context->IsMatchingInCB = IsInCB; - context->eventlock->unlock(); //peerlock.unlock(); + peerlock.unlock(); return IsInCB; } bool IsMatchingInCallback(SceNetAdhocMatchingContext* context) { bool inCB = false; if (context == NULL) return inCB; - context->eventlock->lock(); //peerlock.lock(); + peerlock.lock(); inCB = (context->IsMatchingInCB); - context->eventlock->unlock(); //peerlock.unlock(); + peerlock.unlock(); return inCB; } @@ -1517,7 +1547,7 @@ int friendFinder(){ // Log Incoming Peer u32_le ipaddr = packet->ip; - INFO_LOG(SCENET, "FriendFinder: Incoming OPCODE_CONNECT [%s][%s][%s]", mac2str(&packet->mac).c_str(), inet_ntoa(*(in_addr*)&ipaddr), packet->name.data); + INFO_LOG(SCENET, "FriendFinder: Incoming OPCODE_CONNECT [%s][%s][%s]", mac2str(&packet->mac).c_str(), ip2str(*(in_addr*)&ipaddr).c_str(), packet->name.data); // Add User addFriend(packet); @@ -1742,19 +1772,23 @@ int getActivePeerCount(const bool excludeTimedout) { } int getLocalIp(sockaddr_in* SocketAddress) { + if (isLocalServer) { + SocketAddress->sin_addr = g_localhostIP.in.sin_addr; + return 0; + } + +#if !PPSSPP_PLATFORM(SWITCH) if (metasocket != (int)INVALID_SOCKET) { struct sockaddr_in localAddr; localAddr.sin_addr.s_addr = INADDR_ANY; socklen_t addrLen = sizeof(localAddr); int ret = getsockname(metasocket, (struct sockaddr*)&localAddr, &addrLen); if (SOCKET_ERROR != ret) { - if (isLocalServer) { - localAddr.sin_addr = g_localhostIP.in.sin_addr; - } SocketAddress->sin_addr = localAddr.sin_addr; return 0; } } +#endif // !PPSSPP_PLATFORM(SWITCH) // Fallback if not connected to AdhocServer #if defined(_WIN32) @@ -1764,17 +1798,18 @@ int getLocalIp(sockaddr_in* SocketAddress) { if (::gethostname(szHostName, sizeof(szHostName))) { // Error handling } - // Get local IP addresses - struct hostent* pHost = 0; - pHost = ::gethostbyname(szHostName); // On Non-Windows (UNIX/POSIX) gethostbyname("localhost") will always returns a useless 127.0.0.1, while on Windows it returns LAN IP when available - if (pHost) { - memcpy(&SocketAddress->sin_addr, pHost->h_addr_list[0], pHost->h_length); - if (isLocalServer) { - SocketAddress->sin_addr = g_localhostIP.in.sin_addr; - } + // Get local network IP addresses (LAN/VPN/loopback) + struct addrinfo hints, * res = 0; + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_INET; // AF_UNSPEC; + hints.ai_socktype = SOCK_DGRAM; + hints.ai_flags = AI_ADDRCONFIG; // getaddrinfo with AI_ADDRCONFIG will fail when there is no local network connected? https://github.com/stephane/libmodbus/issues/575 + // Note: getaddrinfo could cause freezes on Android if there is no network https://github.com/hrydgard/ppsspp/issues/13300 + if (getaddrinfo(szHostName, NULL, &hints, &res) == 0 && res != NULL) { + memcpy(&SocketAddress->sin_addr, &((struct sockaddr_in*)res->ai_addr)->sin_addr, sizeof(SocketAddress->sin_addr)); + freeaddrinfo(res); return 0; } - return -1; #elif defined(getifaddrs) // On Android: Requires __ANDROID_API__ >= 24 struct ifaddrs* ifAddrStruct = NULL; @@ -1793,12 +1828,8 @@ int getLocalIp(sockaddr_in* SocketAddress) { } } freeifaddrs(ifAddrStruct); - if (isLocalServer) { - SocketAddress->sin_addr = g_localhostIP.in.sin_addr; - } return 0; } - return -1; #else // Alternative way int sock = socket(AF_INET, SOCK_DGRAM, 0); @@ -1811,24 +1842,21 @@ int getLocalIp(sockaddr_in* SocketAddress) { serv.sin_addr.s_addr = inet_addr(kGoogleDnsIp); serv.sin_port = htons(kDnsPort); - int err = connect(sock, (const sockaddr*)&serv, sizeof(serv)); + int err = connect(sock, (struct sockaddr*)&serv, sizeof(serv)); if (err != SOCKET_ERROR) { - sockaddr_in name; + struct sockaddr_in name; socklen_t namelen = sizeof(name); - err = getsockname(sock, (sockaddr*)&name, &namelen); + err = getsockname(sock, (struct sockaddr*)&name, &namelen); if (err != SOCKET_ERROR) { SocketAddress->sin_addr = name.sin_addr; // May be we should cache this so it doesn't need to use connect all the time, or even better cache it when connecting to adhoc server to get an accurate IP closesocket(sock); - if (isLocalServer) { - SocketAddress->sin_addr = g_localhostIP.in.sin_addr; - } return 0; } } closesocket(sock); } - return -1; #endif + return -1; } uint32_t getLocalIp(int sock) { @@ -1893,13 +1921,20 @@ uint16_t getLocalPort(int sock) { return ntohs(localAddr.sin_port); } -u_long getAvailToRecv(int sock) { +u_long getAvailToRecv(int sock, int udpBufferSize) { u_long n = 0; // Typical MTU size is 1500 + int err = -1; #if defined(_WIN32) // May not be available on all platform - ioctlsocket(sock, FIONREAD, &n); + err = ioctlsocket(sock, FIONREAD, &n); #else - ioctl(sock, FIONREAD, &n); + err = ioctl(sock, FIONREAD, &n); #endif + if (err < 0) + return 0; + + if (udpBufferSize > 0 && n > 0) { + // TODO: Cap number of bytes of full DGRAM message(s) up to buffer size, but may cause Warriors Orochi 2 to get FPS drops + } return n; } @@ -2001,7 +2036,7 @@ int setUDPConnReset(int udpsock, bool enabled) { return -1; } -#if !defined(TCP_KEEPIDLE) +#if !defined(TCP_KEEPIDLE) && !PPSSPP_PLATFORM(SWITCH) #define TCP_KEEPIDLE TCP_KEEPALIVE //TCP_KEEPIDLE on Linux is equivalent to TCP_KEEPALIVE on macOS #endif // VS 2017 compatibility @@ -2017,6 +2052,7 @@ int setSockKeepAlive(int sock, bool keepalive, const int keepinvl, const int kee int optval = keepalive ? 1 : 0; int optlen = sizeof(optval); int result = setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, (char*)&optval, optlen); +#if !PPSSPP_PLATFORM(SWITCH) if (result == 0 && keepalive) { if (getsockopt(sock, SOL_SOCKET, SO_TYPE, (char*)&optval, (socklen_t*)&optlen) == 0 && optval == SOCK_STREAM) { optlen = sizeof(optval); @@ -2028,6 +2064,7 @@ int setSockKeepAlive(int sock, bool keepalive, const int keepinvl, const int kee setsockopt(sock, IPPROTO_TCP, TCP_KEEPCNT, (char*)&optval, optlen); } } +#endif // !PPSSPP_PLATFORM(SWITCH) return result; } @@ -2113,10 +2150,10 @@ int initNetwork(SceNetAdhocctlAdhocId *adhoc_id){ g_localhostIP.in.sin_port = 0; // Bind Local Address to Socket - iResult = bind(metasocket, &g_localhostIP.addr, sizeof(sockaddr)); + iResult = bind(metasocket, &g_localhostIP.addr, sizeof(g_localhostIP.addr)); if (iResult == SOCKET_ERROR) { - ERROR_LOG(SCENET, "Bind to alternate localhost[%s] failed(%i).", inet_ntoa(g_localhostIP.in.sin_addr), iResult); - host->NotifyUserMessage(std::string(n->T("Failed to Bind Localhost IP")) + " " + inet_ntoa(g_localhostIP.in.sin_addr), 2.0, 0x0000ff); + ERROR_LOG(SCENET, "Bind to alternate localhost[%s] failed(%i).", ip2str(g_localhostIP.in.sin_addr).c_str(), iResult); + host->NotifyUserMessage(std::string(n->T("Failed to Bind Localhost IP")) + " " + ip2str(g_localhostIP.in.sin_addr).c_str(), 2.0, 0x0000ff); } } @@ -2156,7 +2193,7 @@ int initNetwork(SceNetAdhocctlAdhocId *adhoc_id){ sleep_ms(10); } if (IsSocketReady(metasocket, false, true) <= 0) { - ERROR_LOG(SCENET, "Socket error (%i) when connecting to AdhocServer [%s/%s:%u]", errorcode, g_Config.proAdhocServer.c_str(), inet_ntoa(g_adhocServerIP.in.sin_addr), ntohs(g_adhocServerIP.in.sin_port)); + ERROR_LOG(SCENET, "Socket error (%i) when connecting to AdhocServer [%s/%s:%u]", errorcode, g_Config.proAdhocServer.c_str(), ip2str(g_adhocServerIP.in.sin_addr).c_str(), ntohs(g_adhocServerIP.in.sin_port)); host->NotifyUserMessage(n->T("Failed to connect to Adhoc Server"), 1.0f, 0x0000ff); return iResult; } diff --git a/Core/HLE/proAdhoc.h b/Core/HLE/proAdhoc.h index 28363fc1c8..dc7d0e8996 100644 --- a/Core/HLE/proAdhoc.h +++ b/Core/HLE/proAdhoc.h @@ -66,6 +66,7 @@ #undef EISCONN #undef EALREADY #undef ETIMEDOUT +#undef EOPNOTSUPP #define errno WSAGetLastError() #define ESHUTDOWN WSAESHUTDOWN #define ECONNABORTED WSAECONNABORTED @@ -77,12 +78,16 @@ #define EISCONN WSAEISCONN #define EALREADY WSAEALREADY #define ETIMEDOUT WSAETIMEDOUT +#define EOPNOTSUPP WSAEOPNOTSUPP inline bool connectInProgress(int errcode){ return (errcode == WSAEWOULDBLOCK || errcode == WSAEINPROGRESS || errcode == WSAEALREADY); } inline bool isDisconnected(int errcode) { return (errcode == WSAECONNRESET || errcode == WSAECONNABORTED || errcode == WSAESHUTDOWN); } #else #define INVALID_SOCKET -1 #define SOCKET_ERROR -1 #define closesocket close +#ifndef ESHUTDOWN +#define ESHUTDOWN ENETDOWN +#endif inline bool connectInProgress(int errcode){ return (errcode == EAGAIN || errcode == EWOULDBLOCK || errcode == EINPROGRESS || errcode == EALREADY); } inline bool isDisconnected(int errcode) { return (errcode == EPIPE || errcode == ECONNRESET || errcode == ECONNABORTED || errcode == ESHUTDOWN); } #endif @@ -356,8 +361,8 @@ typedef struct SceNetAdhocPtpStat { SceNetEtherAddr paddr; u16_le lport; u16_le pport; - s32_le snd_sb_cc; // Number of bytes existed in buffer to be sent/flushed? - s32_le rcv_sb_cc; // Number of bytes available in buffer to be received? + u32_le snd_sb_cc; // Number of bytes existed in sendBuffer to be sent/flushed + u32_le rcv_sb_cc; // Number of bytes available in recvBuffer to be received s32_le state; } PACK SceNetAdhocPtpStat; @@ -919,6 +924,8 @@ extern int defaultWlanChannel; // Default WLAN Channel for Auto, JPCSP uses 11 extern uint32_t fakePoolSize; extern SceNetAdhocMatchingContext * contexts; +extern char* dummyPeekBuf64k; +extern int dummyPeekBuf64kSize; extern int one; extern bool friendFinderRunning; extern SceNetAdhocctlPeerInfo * friends; @@ -976,9 +983,14 @@ bool isPDPPortInUse(uint16_t port); * Check whether PTP Port is in use or not (only sockets with non-Listening state will be considered as in use) * @param port To-be-checked Port Number * @param forListen to check for listening or non-listening port + * @param dstmac destination address (non-listening only) + * @param dstport destination port (non-listening only) * @return 1 if in use or... 0 */ -bool isPTPPortInUse(uint16_t port, bool forListen); +bool isPTPPortInUse(uint16_t port, bool forListen, SceNetEtherAddr* dstmac = nullptr, uint16_t dstport = 0); + +// Convert IPv4 address to string (Replacement for inet_ntoa since it's getting deprecated) +std::string ip2str(in_addr in); // Convert MAC address to string std::string mac2str(SceNetEtherAddr* mac); @@ -1273,8 +1285,10 @@ bool isPrivateIP(uint32_t ip); /* * Get Number of bytes available in buffer to be Received + * @param sock fd + * @param udpBufferSize (UDP only) */ -u_long getAvailToRecv(int sock); +u_long getAvailToRecv(int sock, int udpBufferSize = 0); /* * Get UDP Socket Max Message Size diff --git a/Core/HLE/proAdhocServer.cpp b/Core/HLE/proAdhocServer.cpp index 15757a668e..2734978cb6 100644 --- a/Core/HLE/proAdhocServer.cpp +++ b/Core/HLE/proAdhocServer.cpp @@ -21,11 +21,20 @@ // This is a direct port of Coldbird's code from http://code.google.com/p/aemu/ // All credit goes to him! +#include "ppsspp_config.h" + #include #include #include #include +#if defined(HAVE_LIBNX) || PPSSPP_PLATFORM(SWITCH) +#include +#include +// Missing include, *shrugs* +extern "C" struct hostent *gethostbyname(const char *name); +#endif // defined(HAVE_LIBNX) || PPSSPP_PLATFORM(SWITCH) + #if !defined(__APPLE__) #include #endif @@ -33,6 +42,7 @@ #include // Net stuff #if defined(_WIN32) +#include #include #else #include @@ -527,7 +537,7 @@ void login_user_stream(int fd, uint32_t ip) while(u != NULL && u->resolver.ip != ip) u = u->next; if (u != NULL) { // IP Already existed - WARN_LOG(SCENET, "AdhocServer: Already Existing IP: %s\n", inet_ntoa(*(in_addr*)&u->resolver.ip)); + WARN_LOG(SCENET, "AdhocServer: Already Existing IP: %s\n", ip2str(*(in_addr*)&u->resolver.ip).c_str()); } // Unique IP Address @@ -557,7 +567,7 @@ void login_user_stream(int fd, uint32_t ip) user->last_recv = time(NULL); // Notify User - INFO_LOG(SCENET, "AdhocServer: New Connection from %s", inet_ntoa(*(in_addr*)&user->resolver.ip)); + INFO_LOG(SCENET, "AdhocServer: New Connection from %s", ip2str(*(in_addr*)&user->resolver.ip).c_str()); // Fix User Counter _db_user_count++; @@ -600,7 +610,7 @@ void login_user_data(SceNetAdhocctlUserNode * user, SceNetAdhocctlLoginPacketC2S while (u != NULL && !IsMatch(u->resolver.mac, data->mac)) u = u->next; if (u != NULL) { // MAC Already existed - WARN_LOG(SCENET, "AdhocServer: Already Existing MAC: %02x:%02x:%02x:%02x:%02x:%02x [%s]\n", data->mac.data[0], data->mac.data[1], data->mac.data[2], data->mac.data[3], data->mac.data[4], data->mac.data[5], inet_ntoa(*(in_addr*)&u->resolver.ip)); + WARN_LOG(SCENET, "AdhocServer: Already Existing MAC: %s [%s]\n", mac2str(&data->mac).c_str(), ip2str(*(in_addr*)&u->resolver.ip).c_str()); } // Game Product Override @@ -651,7 +661,7 @@ void login_user_data(SceNetAdhocctlUserNode * user, SceNetAdhocctlLoginPacketC2S char safegamestr[10]; memset(safegamestr, 0, sizeof(safegamestr)); strncpy(safegamestr, game->game.data, PRODUCT_CODE_LENGTH); - INFO_LOG(SCENET, "AdhocServer: %s (MAC: %02x:%02x:%02x:%02x:%02x:%02x - IP: %s) started playing %s", (char *)user->resolver.name.data, user->resolver.mac.data[0], user->resolver.mac.data[1], user->resolver.mac.data[2], user->resolver.mac.data[3], user->resolver.mac.data[4], user->resolver.mac.data[5], inet_ntoa(*(in_addr*)&user->resolver.ip), safegamestr); + INFO_LOG(SCENET, "AdhocServer: %s (MAC: %s - IP: %s) started playing %s", (char *)user->resolver.name.data, mac2str(&user->resolver.mac).c_str(), ip2str(*(in_addr*)&user->resolver.ip).c_str(), safegamestr); // Update Status Log update_status(); @@ -665,7 +675,7 @@ void login_user_data(SceNetAdhocctlUserNode * user, SceNetAdhocctlLoginPacketC2S else { // Notify User - WARN_LOG(SCENET, "AdhocServer: Invalid Login Packet Contents from %s", inet_ntoa(*(in_addr*)&user->resolver.ip)); + WARN_LOG(SCENET, "AdhocServer: Invalid Login Packet Contents from %s", ip2str(*(in_addr*)&user->resolver.ip).c_str()); } // Logout User - Out of Memory or Invalid Arguments @@ -700,7 +710,7 @@ void logout_user(SceNetAdhocctlUserNode * user) char safegamestr[10]; memset(safegamestr, 0, sizeof(safegamestr)); strncpy(safegamestr, user->game->game.data, PRODUCT_CODE_LENGTH); - INFO_LOG(SCENET, "AdhocServer: %s (MAC: %02x:%02x:%02x:%02x:%02x:%02x - IP: %s) stopped playing %s", (char *)user->resolver.name.data, user->resolver.mac.data[0], user->resolver.mac.data[1], user->resolver.mac.data[2], user->resolver.mac.data[3], user->resolver.mac.data[4], user->resolver.mac.data[5], inet_ntoa(*(in_addr*)&user->resolver.ip), safegamestr); + INFO_LOG(SCENET, "AdhocServer: %s (MAC: %s - IP: %s) stopped playing %s", (char *)user->resolver.name.data, mac2str(&user->resolver.mac).c_str(), ip2str(*(in_addr*)&user->resolver.ip).c_str(), safegamestr); // Fix Game Player Count user->game->playercount--; @@ -726,7 +736,7 @@ void logout_user(SceNetAdhocctlUserNode * user) else { // Notify User - WARN_LOG(SCENET, "AdhocServer: Dropped Connection to %s", inet_ntoa(*(in_addr*)&user->resolver.ip)); + WARN_LOG(SCENET, "AdhocServer: Dropped Connection to %s", ip2str(*(in_addr*)&user->resolver.ip).c_str()); } // Free Memory @@ -914,7 +924,7 @@ void connect_user(SceNetAdhocctlUserNode * user, SceNetAdhocctlGroupName * group char safegroupstr[9]; memset(safegroupstr, 0, sizeof(safegroupstr)); strncpy(safegroupstr, (char *)user->group->group.data, ADHOCCTL_GROUPNAME_LEN); - INFO_LOG(SCENET, "AdhocServer: %s (MAC: %02x:%02x:%02x:%02x:%02x:%02x - IP: %s) joined %s group %s", (char *)user->resolver.name.data, user->resolver.mac.data[0], user->resolver.mac.data[1], user->resolver.mac.data[2], user->resolver.mac.data[3], user->resolver.mac.data[4], user->resolver.mac.data[5], inet_ntoa(*(in_addr*)&user->resolver.ip), safegamestr, safegroupstr); + INFO_LOG(SCENET, "AdhocServer: %s (MAC: %s - IP: %s) joined %s group %s", (char *)user->resolver.name.data, mac2str(&user->resolver.mac).c_str(), ip2str(*(in_addr*)&user->resolver.ip).c_str(), safegamestr, safegroupstr); // Update Status Log update_status(); @@ -937,7 +947,7 @@ void connect_user(SceNetAdhocctlUserNode * user, SceNetAdhocctlGroupName * group char safegroupstr2[9]; memset(safegroupstr2, 0, sizeof(safegroupstr2)); strncpy(safegroupstr2, (char *)user->group->group.data, ADHOCCTL_GROUPNAME_LEN); - WARN_LOG(SCENET, "AdhocServer: %s (MAC: %02x:%02x:%02x:%02x:%02x:%02x - IP: %s) attempted to join %s group %s without disconnecting from %s first", (char *)user->resolver.name.data, user->resolver.mac.data[0], user->resolver.mac.data[1], user->resolver.mac.data[2], user->resolver.mac.data[3], user->resolver.mac.data[4], user->resolver.mac.data[5], inet_ntoa(*(in_addr*)&user->resolver.ip), safegamestr, safegroupstr, safegroupstr2); + WARN_LOG(SCENET, "AdhocServer: %s (MAC: %s - IP: %s) attempted to join %s group %s without disconnecting from %s first", (char *)user->resolver.name.data, mac2str(&user->resolver.mac).c_str(), ip2str(*(in_addr*)&user->resolver.ip).c_str(), safegamestr, safegroupstr, safegroupstr2); } } @@ -951,7 +961,7 @@ void connect_user(SceNetAdhocctlUserNode * user, SceNetAdhocctlGroupName * group char safegroupstr[9]; memset(safegroupstr, 0, sizeof(safegroupstr)); strncpy(safegroupstr, (char *)group->data, ADHOCCTL_GROUPNAME_LEN); - WARN_LOG(SCENET, "AdhocServer: %s (MAC: %02x:%02x:%02x:%02x:%02x:%02x - IP: %s) attempted to join invalid %s group %s", (char *)user->resolver.name.data, user->resolver.mac.data[0], user->resolver.mac.data[1], user->resolver.mac.data[2], user->resolver.mac.data[3], user->resolver.mac.data[4], user->resolver.mac.data[5], inet_ntoa(*(in_addr*)&user->resolver.ip), safegamestr, safegroupstr); + WARN_LOG(SCENET, "AdhocServer: %s (MAC: %s - IP: %s) attempted to join invalid %s group %s", (char *)user->resolver.name.data, mac2str(&user->resolver.mac).c_str(), ip2str(*(in_addr*)&user->resolver.ip).c_str(), safegamestr, safegroupstr); } // Invalid State, Out of Memory or Invalid Group Name @@ -1010,7 +1020,7 @@ void disconnect_user(SceNetAdhocctlUserNode * user) char safegroupstr[9]; memset(safegroupstr, 0, sizeof(safegroupstr)); strncpy(safegroupstr, (char *)user->group->group.data, ADHOCCTL_GROUPNAME_LEN); - INFO_LOG(SCENET, "AdhocServer: %s (MAC: %02x:%02x:%02x:%02x:%02x:%02x - IP: %s) left %s group %s", (char *)user->resolver.name.data, user->resolver.mac.data[0], user->resolver.mac.data[1], user->resolver.mac.data[2], user->resolver.mac.data[3], user->resolver.mac.data[4], user->resolver.mac.data[5], inet_ntoa(*(in_addr*)&user->resolver.ip), safegamestr, safegroupstr); + INFO_LOG(SCENET, "AdhocServer: %s (MAC: %s - IP: %s) left %s group %s", (char *)user->resolver.name.data, mac2str(&user->resolver.mac).c_str(), ip2str(*(in_addr*)&user->resolver.ip).c_str(), safegamestr, safegroupstr); // Empty Group if(user->group->playercount == 0) @@ -1050,7 +1060,7 @@ void disconnect_user(SceNetAdhocctlUserNode * user) char safegamestr[10]; memset(safegamestr, 0, sizeof(safegamestr)); strncpy(safegamestr, user->game->game.data, PRODUCT_CODE_LENGTH); - WARN_LOG(SCENET, "AdhocServer: %s (MAC: %02x:%02x:%02x:%02x:%02x:%02x - IP: %s) attempted to leave %s group without joining one first", (char *)user->resolver.name.data, user->resolver.mac.data[0], user->resolver.mac.data[1], user->resolver.mac.data[2], user->resolver.mac.data[3], user->resolver.mac.data[4], user->resolver.mac.data[5], inet_ntoa(*(in_addr*)&user->resolver.ip), safegamestr); + WARN_LOG(SCENET, "AdhocServer: %s (MAC: %s - IP: %s) attempted to leave %s group without joining one first", (char *)user->resolver.name.data, mac2str(&user->resolver.mac).c_str(), ip2str(*(in_addr*)&user->resolver.ip).c_str(), safegamestr); } // Delete User @@ -1108,7 +1118,7 @@ void send_scan_results(SceNetAdhocctlUserNode * user) char safegamestr[10]; memset(safegamestr, 0, sizeof(safegamestr)); strncpy(safegamestr, user->game->game.data, PRODUCT_CODE_LENGTH); - INFO_LOG(SCENET, "AdhocServer: %s (MAC: %02x:%02x:%02x:%02x:%02x:%02x - IP: %s) requested information on %d %s groups", (char *)user->resolver.name.data, user->resolver.mac.data[0], user->resolver.mac.data[1], user->resolver.mac.data[2], user->resolver.mac.data[3], user->resolver.mac.data[4], user->resolver.mac.data[5], inet_ntoa(*(in_addr*)&user->resolver.ip), user->game->groupcount, safegamestr); + INFO_LOG(SCENET, "AdhocServer: %s (MAC: %s - IP: %s) requested information on %d %s groups", (char *)user->resolver.name.data, mac2str(&user->resolver.mac).c_str(), ip2str(*(in_addr*)&user->resolver.ip).c_str(), user->game->groupcount, safegamestr); // Exit Function return; @@ -1124,7 +1134,7 @@ void send_scan_results(SceNetAdhocctlUserNode * user) char safegroupstr[9]; memset(safegroupstr, 0, sizeof(safegroupstr)); strncpy(safegroupstr, (char *)user->group->group.data, ADHOCCTL_GROUPNAME_LEN); - WARN_LOG(SCENET, "AdhocServer: %s (MAC: %02x:%02x:%02x:%02x:%02x:%02x - IP: %s) attempted to scan for %s groups without disconnecting from %s first", (char *)user->resolver.name.data, user->resolver.mac.data[0], user->resolver.mac.data[1], user->resolver.mac.data[2], user->resolver.mac.data[3], user->resolver.mac.data[4], user->resolver.mac.data[5], inet_ntoa(*(in_addr*)&user->resolver.ip), safegamestr, safegroupstr); + WARN_LOG(SCENET, "AdhocServer: %s (MAC: %s - IP: %s) attempted to scan for %s groups without disconnecting from %s first", (char *)user->resolver.name.data, mac2str(&user->resolver.mac).c_str(), ip2str(*(in_addr*)&user->resolver.ip).c_str(), safegamestr, safegroupstr); } // Delete User @@ -1222,7 +1232,7 @@ void spread_message(SceNetAdhocctlUserNode *user, const char *message) char safegroupstr[9]; memset(safegroupstr, 0, sizeof(safegroupstr)); strncpy(safegroupstr, (char *)user->group->group.data, ADHOCCTL_GROUPNAME_LEN); - INFO_LOG(SCENET, "AdhocServer: %s (MAC: %02x:%02x:%02x:%02x:%02x:%02x - IP: %s) sent \"%s\" to %d players in %s group %s", (char *)user->resolver.name.data, user->resolver.mac.data[0], user->resolver.mac.data[1], user->resolver.mac.data[2], user->resolver.mac.data[3], user->resolver.mac.data[4], user->resolver.mac.data[5], inet_ntoa(*(in_addr*)&user->resolver.ip), message, counter, safegamestr, safegroupstr); + INFO_LOG(SCENET, "AdhocServer: %s (MAC: %s - IP: %s) sent \"%s\" to %d players in %s group %s", (char *)user->resolver.name.data, mac2str(&user->resolver.mac).c_str(), ip2str(*(in_addr*)&user->resolver.ip).c_str(), message, counter, safegamestr, safegroupstr); } // Exit Function @@ -1236,7 +1246,7 @@ void spread_message(SceNetAdhocctlUserNode *user, const char *message) char safegamestr[10]; memset(safegamestr, 0, sizeof(safegamestr)); strncpy(safegamestr, user->game->game.data, PRODUCT_CODE_LENGTH); - WARN_LOG(SCENET, "AdhocServer: %s (MAC: %02x:%02x:%02x:%02x:%02x:%02x - IP: %s) attempted to send a text message without joining a %s group first", (char *)user->resolver.name.data, user->resolver.mac.data[0], user->resolver.mac.data[1], user->resolver.mac.data[2], user->resolver.mac.data[3], user->resolver.mac.data[4], user->resolver.mac.data[5], inet_ntoa(*(in_addr*)&user->resolver.ip), safegamestr); + WARN_LOG(SCENET, "AdhocServer: %s (MAC: %s - IP: %s) attempted to send a text message without joining a %s group first", (char *)user->resolver.name.data, mac2str(&user->resolver.mac).c_str(), ip2str(*(in_addr*)&user->resolver.ip).c_str(), safegamestr); } // Delete User @@ -1995,7 +2005,7 @@ int server_loop(int server) else { // Notify User - WARN_LOG(SCENET, "AdhocServer: Invalid Opcode 0x%02X in Waiting State from %s", user->rx[0], inet_ntoa(*(in_addr*)&user->resolver.ip)); + WARN_LOG(SCENET, "AdhocServer: Invalid Opcode 0x%02X in Waiting State from %s", user->rx[0], ip2str(*(in_addr*)&user->resolver.ip).c_str()); // Logout User logout_user(user); @@ -2078,7 +2088,7 @@ int server_loop(int server) else { // Notify User - WARN_LOG(SCENET, "AdhocServer: Invalid Opcode 0x%02X in Logged-In State from %s (MAC: %02x:%02x:%02x:%02x:%02x:%02x - IP: %s)", user->rx[0], (char *)user->resolver.name.data, user->resolver.mac.data[0], user->resolver.mac.data[1], user->resolver.mac.data[2], user->resolver.mac.data[3], user->resolver.mac.data[4], user->resolver.mac.data[5], inet_ntoa(*(in_addr*)&user->resolver.ip)); + WARN_LOG(SCENET, "AdhocServer: Invalid Opcode 0x%02X in Logged-In State from %s (MAC: %s - IP: %s)", user->rx[0], (char *)user->resolver.name.data, mac2str(&user->resolver.mac).c_str(), ip2str(*(in_addr*)&user->resolver.ip).c_str()); // Logout User logout_user(user); diff --git a/Core/HLE/sceAtrac.cpp b/Core/HLE/sceAtrac.cpp index 8590570a35..8bcf49d003 100644 --- a/Core/HLE/sceAtrac.cpp +++ b/Core/HLE/sceAtrac.cpp @@ -26,7 +26,7 @@ #include "Core/MemMapHelpers.h" #include "Core/Reporting.h" #include "Core/Config.h" -#include "Core/Debugger/Breakpoints.h" +#include "Core/Debugger/MemBlockInfo.h" #include "Core/HW/MediaEngine.h" #include "Core/HW/BufferQueue.h" @@ -556,7 +556,27 @@ struct Atrac { } int got_frame = 0; +#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 48, 101) + if (packet_->size != 0) { + int err = avcodec_send_packet(codecCtx_, packet_); + if (err < 0) { + ERROR_LOG_REPORT(ME, "avcodec_send_packet: Error decoding audio %d / %08x", err, err); + failedDecode_ = true; + return ATDECODE_FAILED; + } + } + + int err = avcodec_receive_frame(codecCtx_, frame_); + int bytes_read = 0; + if (err >= 0) { + bytes_read = frame_->pkt_size; + got_frame = 1; + } else if (err != AVERROR(EAGAIN)) { + bytes_read = err; + } +#else int bytes_read = avcodec_decode_audio4(codecCtx_, frame_, &got_frame, packet_); +#endif #if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 12, 100) av_packet_unref(packet_); #else @@ -636,8 +656,12 @@ void __AtracInit() { atracIDTypes[5] = 0; #ifdef USE_FFMPEG +#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(58, 18, 100) avcodec_register_all(); +#endif +#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(58, 12, 100) av_register_all(); +#endif #endif // USE_FFMPEG } @@ -1222,7 +1246,7 @@ u32 _AtracDecodeData(int atracID, u8 *outbuf, u32 outbufPtr, u32 *SamplesNum, u3 int avret = swr_convert(atrac->swrCtx_, &out, numSamples, inbuf, numSamples); if (outbufPtr != 0) { u32 outBytes = numSamples * atrac->outputChannels_ * sizeof(s16); - CBreakPoints::ExecMemCheck(outbufPtr, true, outBytes, currentMIPS->pc); + NotifyMemInfo(MemBlockFlags::WRITE, outbufPtr, outBytes, "AtracDecode"); } if (avret < 0) { ERROR_LOG(ME, "swr_convert: Error while converting %d", avret); @@ -1245,7 +1269,7 @@ u32 _AtracDecodeData(int atracID, u8 *outbuf, u32 outbufPtr, u32 *SamplesNum, u3 u32 outBytes = numSamples * atrac->outputChannels_ * sizeof(s16); if (outbuf != nullptr) { memset(outbuf, 0, outBytes); - CBreakPoints::ExecMemCheck(outbufPtr, true, outBytes, currentMIPS->pc); + NotifyMemInfo(MemBlockFlags::WRITE, outbufPtr, outBytes, "AtracDecode"); } } } @@ -1570,7 +1594,7 @@ static u32 sceAtracGetNextSample(int atracID, u32 outNAddr) { // Obtains the number of frames remaining in the buffer which can be decoded. // When no more data would be needed, this returns a negative number. static u32 sceAtracGetRemainFrame(int atracID, u32 remainAddr) { - auto remainingFrames = PSPPointer::Create(remainAddr); + auto remainingFrames = PSPPointer::Create(remainAddr); Atrac *atrac = getAtrac(atracID); u32 err = AtracValidateManaged(atrac); @@ -1589,8 +1613,8 @@ static u32 sceAtracGetRemainFrame(int atracID, u32 remainAddr) { } static u32 sceAtracGetSecondBufferInfo(int atracID, u32 fileOffsetAddr, u32 desiredSizeAddr) { - auto fileOffset = PSPPointer::Create(fileOffsetAddr); - auto desiredSize = PSPPointer::Create(desiredSizeAddr); + auto fileOffset = PSPPointer::Create(fileOffsetAddr); + auto desiredSize = PSPPointer::Create(desiredSizeAddr); Atrac *atrac = getAtrac(atracID); u32 err = AtracValidateManaged(atrac); @@ -1625,13 +1649,13 @@ static u32 sceAtracGetSoundSample(int atracID, u32 outEndSampleAddr, u32 outLoop return err; } - auto outEndSample = PSPPointer::Create(outEndSampleAddr); + auto outEndSample = PSPPointer::Create(outEndSampleAddr); if (outEndSample.IsValid()) *outEndSample = atrac->endSample_; - auto outLoopStart = PSPPointer::Create(outLoopStartSampleAddr); + auto outLoopStart = PSPPointer::Create(outLoopStartSampleAddr); if (outLoopStart.IsValid()) *outLoopStart = atrac->loopStartSample_ == -1 ? -1 : atrac->loopStartSample_ - atrac->firstSampleOffset_ - atrac->FirstOffsetExtra(); - auto outLoopEnd = PSPPointer::Create(outLoopEndSampleAddr); + auto outLoopEnd = PSPPointer::Create(outLoopEndSampleAddr); if (outLoopEnd.IsValid()) *outLoopEnd = atrac->loopEndSample_ == -1 ? -1 : atrac->loopEndSample_ - atrac->firstSampleOffset_ - atrac->FirstOffsetExtra(); @@ -2296,8 +2320,8 @@ void _AtracGenerateContext(Atrac *atrac, SceAtracId *context) { context->info.decodePos = atrac->DecodePosBySample(atrac->currentSample_); context->info.streamDataByte = atrac->first_.size - atrac->dataOff_; - u8* buf = (u8*)context; - *(u32*)(buf + 0xfc) = atrac->atracID_; + u8 *buf = (u8 *)context; + *(u32_le *)(buf + 0xfc) = atrac->atracID_; } static u32 _sceAtracGetContextAddress(int atracID) { @@ -2311,7 +2335,7 @@ static u32 _sceAtracGetContextAddress(int atracID) { u32 contextsize = 256; atrac->context_ = kernelMemory.Alloc(contextsize, false, "Atrac Context"); if (atrac->context_.IsValid()) - Memory::Memset(atrac->context_.ptr, 0, 256); + Memory::Memset(atrac->context_.ptr, 0, 256, "AtracContextClear"); WARN_LOG(ME, "%08x=_sceAtracGetContextAddress(%i): allocated new context", atrac->context_.ptr, atracID); } @@ -2419,9 +2443,9 @@ static int sceAtracLowLevelInitDecoder(int atracID, u32 paramsAddr) { static int sceAtracLowLevelDecode(int atracID, u32 sourceAddr, u32 sourceBytesConsumedAddr, u32 samplesAddr, u32 sampleBytesAddr) { auto srcp = PSPPointer::Create(sourceAddr); - auto srcConsumed = PSPPointer::Create(sourceBytesConsumedAddr); + auto srcConsumed = PSPPointer::Create(sourceBytesConsumedAddr); auto outp = PSPPointer::Create(samplesAddr); - auto outWritten = PSPPointer::Create(sampleBytesAddr); + auto outWritten = PSPPointer::Create(sampleBytesAddr); Atrac *atrac = getAtrac(atracID); if (!atrac) { @@ -2448,7 +2472,7 @@ static int sceAtracLowLevelDecode(int atracID, u32 sourceAddr, u32 sourceBytesCo int avret = swr_convert(atrac->swrCtx_, &out, numSamples, (const u8**)atrac->frame_->extended_data, numSamples); u32 outBytes = numSamples * atrac->outputChannels_ * sizeof(s16); - CBreakPoints::ExecMemCheck(samplesAddr, true, outBytes, currentMIPS->pc); + NotifyMemInfo(MemBlockFlags::WRITE, samplesAddr, outBytes, "AtracLowLevelDecode"); if (avret < 0) { ERROR_LOG(ME, "swr_convert: Error while converting %d", avret); } diff --git a/Core/HLE/sceCcc.cpp b/Core/HLE/sceCcc.cpp index 51da548291..b47d805573 100644 --- a/Core/HLE/sceCcc.cpp +++ b/Core/HLE/sceCcc.cpp @@ -21,7 +21,7 @@ #include "Common/Serialize/Serializer.h" #include "Common/Serialize/SerializeFuncs.h" -#include "Core/Debugger/Breakpoints.h" +#include "Core/Debugger/MemBlockInfo.h" #include "Core/MemMap.h" #include "Core/HLE/HLE.h" #include "Core/HLE/FunctionWrappers.h" @@ -118,8 +118,8 @@ static int sceCccUTF8toUTF16(u32 dstAddr, u32 dstSize, u32 srcAddr) if (dst < dstEnd) *dst++ = 0; - CBreakPoints::ExecMemCheck(srcAddr, false, utf.byteIndex(), currentMIPS->pc); - CBreakPoints::ExecMemCheck(dstAddr, true, dst.ptr - dstAddr, currentMIPS->pc); + NotifyMemInfo(MemBlockFlags::READ, srcAddr, utf.byteIndex(), "sceCcc"); + NotifyMemInfo(MemBlockFlags::WRITE, dstAddr, dst.ptr - dstAddr, "sceCcc"); return n; } @@ -154,8 +154,8 @@ static int sceCccUTF8toSJIS(u32 dstAddr, u32 dstSize, u32 srcAddr) if (dst < dstEnd) *dst++ = 0; - CBreakPoints::ExecMemCheck(srcAddr, false, utf.byteIndex(), currentMIPS->pc); - CBreakPoints::ExecMemCheck(dstAddr, true, dst.ptr - dstAddr, currentMIPS->pc); + NotifyMemInfo(MemBlockFlags::READ, srcAddr, utf.byteIndex(), "sceCcc"); + NotifyMemInfo(MemBlockFlags::WRITE, dstAddr, dst.ptr - dstAddr, "sceCcc"); return n; } @@ -185,8 +185,8 @@ static int sceCccUTF16toUTF8(u32 dstAddr, u32 dstSize, u32 srcAddr) if (dst < dstEnd) *dst++ = 0; - CBreakPoints::ExecMemCheck(srcAddr, false, utf.shortIndex() * sizeof(uint16_t), currentMIPS->pc); - CBreakPoints::ExecMemCheck(dstAddr, true, dst.ptr - dstAddr, currentMIPS->pc); + NotifyMemInfo(MemBlockFlags::READ, srcAddr, utf.shortIndex() * sizeof(uint16_t), "sceCcc"); + NotifyMemInfo(MemBlockFlags::WRITE, dstAddr, dst.ptr - dstAddr, "sceCcc"); return n; } @@ -221,8 +221,8 @@ static int sceCccUTF16toSJIS(u32 dstAddr, u32 dstSize, u32 srcAddr) if (dst < dstEnd) *dst++ = 0; - CBreakPoints::ExecMemCheck(srcAddr, false, utf.shortIndex() * sizeof(uint16_t), currentMIPS->pc); - CBreakPoints::ExecMemCheck(dstAddr, true, dst.ptr - dstAddr, currentMIPS->pc); + NotifyMemInfo(MemBlockFlags::READ, srcAddr, utf.shortIndex() * sizeof(uint16_t), "sceCcc"); + NotifyMemInfo(MemBlockFlags::WRITE, dstAddr, dst.ptr - dstAddr, "sceCcc"); return n; } @@ -257,8 +257,8 @@ static int sceCccSJIStoUTF8(u32 dstAddr, u32 dstSize, u32 srcAddr) if (dst < dstEnd) *dst++ = 0; - CBreakPoints::ExecMemCheck(srcAddr, false, sjis.byteIndex(), currentMIPS->pc); - CBreakPoints::ExecMemCheck(dstAddr, true, dst.ptr - dstAddr, currentMIPS->pc); + NotifyMemInfo(MemBlockFlags::READ, srcAddr, sjis.byteIndex(), "sceCcc"); + NotifyMemInfo(MemBlockFlags::WRITE, dstAddr, dst.ptr - dstAddr, "sceCcc"); return n; } @@ -293,8 +293,8 @@ static int sceCccSJIStoUTF16(u32 dstAddr, u32 dstSize, u32 srcAddr) if (dst < dstEnd) *dst++ = 0; - CBreakPoints::ExecMemCheck(srcAddr, false, sjis.byteIndex(), currentMIPS->pc); - CBreakPoints::ExecMemCheck(dstAddr, true, dst.ptr - dstAddr, currentMIPS->pc); + NotifyMemInfo(MemBlockFlags::READ, srcAddr, sjis.byteIndex(), "sceCcc"); + NotifyMemInfo(MemBlockFlags::WRITE, dstAddr, dst.ptr - dstAddr, "sceCcc"); return n; } diff --git a/Core/HLE/sceDeflt.cpp b/Core/HLE/sceDeflt.cpp index 5c1f579071..2f0b6ec469 100644 --- a/Core/HLE/sceDeflt.cpp +++ b/Core/HLE/sceDeflt.cpp @@ -31,7 +31,7 @@ static int sceDeflateDecompress(u32 OutBuffer, int OutBufferLength, u32 InBuffer uLong crc; z_stream stream; u8 *outBufferPtr; - u32 *crc32AddrPtr = 0; + u32_le *crc32AddrPtr = nullptr; if (!Memory::IsValidAddress(OutBuffer) || !Memory::IsValidAddress(InBuffer)) { ERROR_LOG(HLE, "sceZlibDecompress: Bad address %08x %08x", OutBuffer, InBuffer); @@ -42,7 +42,7 @@ static int sceDeflateDecompress(u32 OutBuffer, int OutBufferLength, u32 InBuffer ERROR_LOG(HLE, "sceZlibDecompress: Bad address %08x", Crc32Addr); return 0; } - crc32AddrPtr = (u32 *)Memory::GetPointer(Crc32Addr); + crc32AddrPtr = (u32_le *)Memory::GetPointer(Crc32Addr); } outBufferPtr = Memory::GetPointer(OutBuffer); stream.next_in = (Bytef*)Memory::GetPointer(InBuffer); @@ -77,7 +77,7 @@ static int sceGzipDecompress(u32 OutBuffer, int OutBufferLength, u32 InBuffer, u uLong crc; z_stream stream; u8 *outBufferPtr; - u32 *crc32AddrPtr = 0; + u32_le *crc32AddrPtr = nullptr; if (!Memory::IsValidAddress(OutBuffer) || !Memory::IsValidAddress(InBuffer)) { ERROR_LOG(HLE, "sceZlibDecompress: Bad address %08x %08x", OutBuffer, InBuffer); @@ -88,7 +88,7 @@ static int sceGzipDecompress(u32 OutBuffer, int OutBufferLength, u32 InBuffer, u ERROR_LOG(HLE, "sceZlibDecompress: Bad address %08x", Crc32Addr); return 0; } - crc32AddrPtr = (u32 *)Memory::GetPointer(Crc32Addr); + crc32AddrPtr = (u32_le *)Memory::GetPointer(Crc32Addr); } outBufferPtr = Memory::GetPointer(OutBuffer); stream.next_in = (Bytef*)Memory::GetPointer(InBuffer); @@ -122,7 +122,7 @@ static int sceZlibDecompress(u32 OutBuffer, int OutBufferLength, u32 InBuffer, u uLong crc; z_stream stream; u8 *outBufferPtr; - u32 *crc32AddrPtr = 0; + u32_le *crc32AddrPtr = 0; if (!Memory::IsValidAddress(OutBuffer) || !Memory::IsValidAddress(InBuffer)) { ERROR_LOG(HLE, "sceZlibDecompress: Bad address %08x %08x", OutBuffer, InBuffer); @@ -133,7 +133,7 @@ static int sceZlibDecompress(u32 OutBuffer, int OutBufferLength, u32 InBuffer, u ERROR_LOG(HLE, "sceZlibDecompress: Bad address %08x", Crc32Addr); return 0; } - crc32AddrPtr = (u32 *)Memory::GetPointer(Crc32Addr); + crc32AddrPtr = (u32_le *)Memory::GetPointer(Crc32Addr); } outBufferPtr = Memory::GetPointer(OutBuffer); stream.next_in = (Bytef*)Memory::GetPointer(InBuffer); diff --git a/Core/HLE/sceDisplay.cpp b/Core/HLE/sceDisplay.cpp index e21cd210a3..18527fe3f4 100644 --- a/Core/HLE/sceDisplay.cpp +++ b/Core/HLE/sceDisplay.cpp @@ -157,7 +157,6 @@ static const int frameTimeHistorySize = (int)ARRAY_SIZE(frameTimeHistory); static int frameTimeHistoryPos = 0; static int frameTimeHistoryValid = 0; static double lastFrameTimeHistory = 0.0; -static double monitorFpsUntil = 0.0; static int lastNumFlips = 0; static float flips = 0.0f; static int actualFlips = 0; // taking frameskip into account @@ -299,7 +298,8 @@ void __DisplayDoState(PointerWrap &p) { ScheduleLagSync(); } } else { - lagSyncEvent = CoreTiming::RegisterEvent("LagSync", &hleLagSync); + lagSyncEvent = -1; + CoreTiming::RestoreRegisterEvent(lagSyncEvent, "LagSync", &hleLagSync); ScheduleLagSync(); } @@ -599,7 +599,6 @@ static void DoFrameTiming(bool &throttle, bool &skipFrame, float timestep) { } // Auto-frameskip automatically if speed limit is set differently than the default. - bool useAutoFrameskip = g_Config.bAutoFrameSkip && g_Config.iRenderingMode != FB_NON_BUFFERED_MODE; bool forceFrameskip = fpsLimit > 60 && unthrottleNeedsSkip; int frameSkipNum = CalculateFrameSkip(); if (g_Config.bAutoFrameSkip || forceFrameskip) { diff --git a/Core/HLE/sceDmac.cpp b/Core/HLE/sceDmac.cpp index 4f186facfe..4defe951ca 100644 --- a/Core/HLE/sceDmac.cpp +++ b/Core/HLE/sceDmac.cpp @@ -50,7 +50,8 @@ static int __DmacMemcpy(u32 dst, u32 src, u32 size) { skip = gpu->PerformMemoryCopy(dst, src, size); } if (!skip) { - Memory::Memcpy(dst, Memory::GetPointer(src), size); + // TODO: InvalidateICache src before copy? + Memory::Memcpy(dst, Memory::GetPointer(src), size, "DmacMemcpy"); currentMIPS->InvalidateICache(dst, size); } diff --git a/Core/HLE/sceFont.cpp b/Core/HLE/sceFont.cpp index dbf0bebf34..89d5f79a38 100644 --- a/Core/HLE/sceFont.cpp +++ b/Core/HLE/sceFont.cpp @@ -944,7 +944,7 @@ static u32 sceFontNewLib(u32 paramPtr, u32 errorCodePtr) { __LoadInternalFonts(); auto params = PSPPointer::Create(paramPtr); - auto errorCode = PSPPointer::Create(errorCodePtr); + auto errorCode = PSPPointer::Create(errorCodePtr); if (!params.IsValid() || !errorCode.IsValid()) { ERROR_LOG_REPORT(SCEFONT, "sceFontNewLib(%08x, %08x): invalid addresses", paramPtr, errorCodePtr); @@ -979,7 +979,7 @@ static int sceFontDoneLib(u32 fontLibHandle) { // Open internal font into a FontLib static u32 sceFontOpen(u32 libHandle, u32 index, u32 mode, u32 errorCodePtr) { - auto errorCode = PSPPointer::Create(errorCodePtr); + auto errorCode = PSPPointer::Create(errorCodePtr); if (!errorCode.IsValid()) { // Would crash on the PSP. ERROR_LOG(SCEFONT, "sceFontOpen(%x, %x, %x, %x): invalid pointer", libHandle, index, mode, errorCodePtr); @@ -1009,7 +1009,7 @@ static u32 sceFontOpen(u32 libHandle, u32 index, u32 mode, u32 errorCodePtr) { // Open a user font in RAM into a FontLib static u32 sceFontOpenUserMemory(u32 libHandle, u32 memoryFontAddrPtr, u32 memoryFontLength, u32 errorCodePtr) { - auto errorCode = PSPPointer::Create(errorCodePtr); + auto errorCode = PSPPointer::Create(errorCodePtr); if (!errorCode.IsValid()) { ERROR_LOG_REPORT(SCEFONT, "sceFontOpenUserMemory(%08x, %08x, %08x, %08x): invalid error address", libHandle, memoryFontAddrPtr, memoryFontLength, errorCodePtr); return -1; @@ -1054,7 +1054,7 @@ static u32 sceFontOpenUserMemory(u32 libHandle, u32 memoryFontAddrPtr, u32 memor // Open a user font in a file into a FontLib static u32 sceFontOpenUserFile(u32 libHandle, const char *fileName, u32 mode, u32 errorCodePtr) { - auto errorCode = PSPPointer::Create(errorCodePtr); + auto errorCode = PSPPointer::Create(errorCodePtr); if (!errorCode.IsValid()) { ERROR_LOG_REPORT(SCEFONT, "sceFontOpenUserFile(%08x, %s, %08x, %08x): invalid error address", libHandle, fileName, mode, errorCodePtr); @@ -1114,7 +1114,7 @@ static int sceFontClose(u32 fontHandle) { } static int sceFontFindOptimumFont(u32 libHandle, u32 fontStylePtr, u32 errorCodePtr) { - auto errorCode = PSPPointer::Create(errorCodePtr); + auto errorCode = PSPPointer::Create(errorCodePtr); if (!errorCode.IsValid()) { ERROR_LOG_REPORT(SCEFONT, "sceFontFindOptimumFont(%08x, %08x, %08x): invalid error address", libHandle, fontStylePtr, errorCodePtr); return SCE_KERNEL_ERROR_INVALID_ARGUMENT; @@ -1139,8 +1139,8 @@ static int sceFontFindOptimumFont(u32 libHandle, u32 fontStylePtr, u32 errorCode auto requestedStyle = PSPPointer::Create(fontStylePtr); // Find the first nearest match for H/V, OR the last exact match for others. - float hRes = requestedStyle->fontHRes > 0.0f ? requestedStyle->fontHRes : fontLib->FontHRes(); - float vRes = requestedStyle->fontVRes > 0.0f ? requestedStyle->fontVRes : fontLib->FontVRes(); + float hRes = requestedStyle->fontHRes > 0.0f ? (float)requestedStyle->fontHRes : fontLib->FontHRes(); + float vRes = requestedStyle->fontVRes > 0.0f ? (float)requestedStyle->fontVRes : fontLib->FontVRes(); Font *optimumFont = 0; Font *nearestFont = 0; float nearestDist = std::numeric_limits::infinity(); @@ -1182,7 +1182,7 @@ static int sceFontFindOptimumFont(u32 libHandle, u32 fontStylePtr, u32 errorCode // Returns the font index, not handle static int sceFontFindFont(u32 libHandle, u32 fontStylePtr, u32 errorCodePtr) { - auto errorCode = PSPPointer::Create(errorCodePtr); + auto errorCode = PSPPointer::Create(errorCodePtr); if (!errorCode.IsValid()) { ERROR_LOG_REPORT(SCEFONT, "sceFontFindFont(%x, %x, %x): invalid error address", libHandle, fontStylePtr, errorCodePtr); return SCE_KERNEL_ERROR_INVALID_ARGUMENT; @@ -1206,8 +1206,7 @@ static int sceFontFindFont(u32 libHandle, u32 fontStylePtr, u32 errorCodePtr) { auto requestedStyle = PSPPointer::Create(fontStylePtr); // Find the closest exact match for the fields specified. - float hRes = requestedStyle->fontHRes > 0.0f ? requestedStyle->fontHRes : fontLib->FontHRes(); - float vRes = requestedStyle->fontVRes > 0.0f ? requestedStyle->fontVRes : fontLib->FontVRes(); + float hRes = requestedStyle->fontHRes > 0.0f ? (float)requestedStyle->fontHRes : fontLib->FontHRes(); for (size_t i = 0; i < internalFonts.size(); i++) { if (internalFonts[i]->MatchesStyle(*requestedStyle) != MATCH_NONE) { auto matchStyle = internalFonts[i]->GetFontStyle(); @@ -1462,7 +1461,7 @@ static int sceFontGetFontList(u32 fontLibHandle, u32 fontStylePtr, int numFonts) } static int sceFontGetNumFontList(u32 fontLibHandle, u32 errorCodePtr) { - auto errorCode = PSPPointer::Create(errorCodePtr); + auto errorCode = PSPPointer::Create(errorCodePtr); if (!errorCode.IsValid()) { ERROR_LOG_REPORT(SCEFONT, "sceFontGetNumFontList(%08x, %08x): invalid error address", fontLibHandle, errorCodePtr); return ERROR_FONT_INVALID_PARAMETER; @@ -1494,7 +1493,7 @@ static int sceFontSetResolution(u32 fontLibHandle, float hRes, float vRes) { } static float sceFontPixelToPointH(int fontLibHandle, float fontPixelsH, u32 errorCodePtr) { - auto errorCode = PSPPointer::Create(errorCodePtr); + auto errorCode = PSPPointer::Create(errorCodePtr); if (!errorCode.IsValid()) { ERROR_LOG_REPORT(SCEFONT, "sceFontPixelToPointH(%08x, %f, %08x): invalid error address", fontLibHandle, fontPixelsH, errorCodePtr); return 0.0f; @@ -1511,7 +1510,7 @@ static float sceFontPixelToPointH(int fontLibHandle, float fontPixelsH, u32 erro } static float sceFontPixelToPointV(int fontLibHandle, float fontPixelsV, u32 errorCodePtr) { - auto errorCode = PSPPointer::Create(errorCodePtr); + auto errorCode = PSPPointer::Create(errorCodePtr); if (!errorCode.IsValid()) { ERROR_LOG_REPORT(SCEFONT, "sceFontPixelToPointV(%08x, %f, %08x): invalid error address", fontLibHandle, fontPixelsV, errorCodePtr); return 0.0f; @@ -1528,7 +1527,7 @@ static float sceFontPixelToPointV(int fontLibHandle, float fontPixelsV, u32 erro } static float sceFontPointToPixelH(int fontLibHandle, float fontPointsH, u32 errorCodePtr) { - auto errorCode = PSPPointer::Create(errorCodePtr); + auto errorCode = PSPPointer::Create(errorCodePtr); if (!errorCode.IsValid()) { ERROR_LOG_REPORT(SCEFONT, "sceFontPointToPixelH(%08x, %f, %08x): invalid error address", fontLibHandle, fontPointsH, errorCodePtr); return 0.0f; @@ -1545,7 +1544,7 @@ static float sceFontPointToPixelH(int fontLibHandle, float fontPointsH, u32 erro } static float sceFontPointToPixelV(int fontLibHandle, float fontPointsV, u32 errorCodePtr) { - auto errorCode = PSPPointer::Create(errorCodePtr); + auto errorCode = PSPPointer::Create(errorCodePtr); if (!errorCode.IsValid()) { ERROR_LOG_REPORT(SCEFONT, "sceFontPointToPixelV(%08x, %f, %08x): invalid error address", fontLibHandle, fontPointsV, errorCodePtr); return 0.0f; diff --git a/Core/HLE/sceGe.cpp b/Core/HLE/sceGe.cpp index 8c27ed16c6..04b2e40831 100644 --- a/Core/HLE/sceGe.cpp +++ b/Core/HLE/sceGe.cpp @@ -563,12 +563,11 @@ static int sceGeGetMtx(int type, u32 matrixPtr) { } static u32 sceGeGetCmd(int cmd) { - INFO_LOG(SCEGE, "sceGeGetCmd(%i)", cmd); if (cmd >= 0 && cmd < (int)ARRAY_SIZE(gstate.cmdmem)) { - return gstate.cmdmem[cmd]; // Does not mask away the high bits. - } else { - return SCE_KERNEL_ERROR_INVALID_INDEX; + // Does not mask away the high bits. + return hleLogSuccessInfoX(SCEGE, gstate.cmdmem[cmd]); } + return hleLogError(SCEGE, SCE_KERNEL_ERROR_INVALID_INDEX); } static int sceGeGetStack(int index, u32 stackPtr) { diff --git a/Core/HLE/sceHeap.cpp b/Core/HLE/sceHeap.cpp index e1c802b149..e51f6b1d83 100644 --- a/Core/HLE/sceHeap.cpp +++ b/Core/HLE/sceHeap.cpp @@ -202,7 +202,7 @@ static int sceHeapCreateHeap(const char* name, u32 heapSize, int attr, u32 param heap->address = addr; // Some of the heap is reserved by the implementation (the first 128 bytes, and 8 after each block.) - heap->alloc.Init(heap->address + 128, heap->size - 128); + heap->alloc.Init(heap->address + 128, heap->size - 128, true); heapList[heap->address] = heap; DEBUG_LOG(HLE, "%08x=sceHeapCreateHeap(%s, %08x, %08x, %08x)", heap->address, name, heapSize, attr, paramsPtr); return heap->address; diff --git a/Core/HLE/sceIo.cpp b/Core/HLE/sceIo.cpp index 43e0700b98..55ffcff2fe 100644 --- a/Core/HLE/sceIo.cpp +++ b/Core/HLE/sceIo.cpp @@ -29,7 +29,7 @@ #include "Core/Core.h" #include "Core/Config.h" #include "Core/ConfigValues.h" -#include "Core/Debugger/Breakpoints.h" +#include "Core/Debugger/MemBlockInfo.h" #include "Core/ELF/ParamSFO.h" #include "Core/MemMapHelpers.h" #include "Core/System.h" @@ -637,7 +637,7 @@ void __IoInit() { pspFileSystem.Mount("flash0:", flash0System); if (g_RemasterMode) { - const std::string gameId = g_paramSFO.GetValueString("DISC_ID"); + const std::string gameId = g_paramSFO.GetDiscID(); const std::string exdataPath = g_Config.memStickDirectory + "exdata/" + gameId + "/"; if (File::Exists(exdataPath)) { exdataSystem = new DirectoryFileSystem(&pspFileSystem, exdataPath, FileSystemFlags::SIMULATE_FAT32 | FileSystemFlags::CARD); @@ -1026,11 +1026,12 @@ static bool __IoRead(int &result, int id, u32 data_addr, int size, int &us) { result = SCE_KERNEL_ERROR_ILLEGAL_ADDR; return true; } else if (Memory::IsValidAddress(data_addr)) { - CBreakPoints::ExecMemCheck(data_addr, true, size, currentMIPS->pc); - u8 *data = (u8*) Memory::GetPointer(data_addr); + NotifyMemInfo(MemBlockFlags::WRITE, data_addr, size, "IoRead"); + u8 *data = (u8 *)Memory::GetPointer(data_addr); + u32 validSize = Memory::ValidSize(data_addr, size); if (f->npdrm) { - result = npdrmRead(f, data, size); - currentMIPS->InvalidateICache(data_addr, size); + result = npdrmRead(f, data, validSize); + currentMIPS->InvalidateICache(data_addr, validSize); return true; } @@ -1046,17 +1047,17 @@ static bool __IoRead(int &result, int id, u32 data_addr, int size, int &us) { AsyncIOEvent ev = IO_EVENT_READ; ev.handle = f->handle; ev.buf = data; - ev.bytes = size; + ev.bytes = validSize; ev.invalidateAddr = data_addr; ioManager.ScheduleOperation(ev); return false; } else { if (GetIOTimingMethod() != IOTIMING_REALISTIC) { - result = (int) pspFileSystem.ReadFile(f->handle, data, size); + result = (int)pspFileSystem.ReadFile(f->handle, data, validSize); } else { - result = (int) pspFileSystem.ReadFile(f->handle, data, size, us); + result = (int)pspFileSystem.ReadFile(f->handle, data, validSize, us); } - currentMIPS->InvalidateICache(data_addr, size); + currentMIPS->InvalidateICache(data_addr, validSize); return true; } } else { @@ -1136,12 +1137,13 @@ static bool __IoWrite(int &result, int id, u32 data_addr, int size, int &us) { } const void *data_ptr = Memory::GetPointer(data_addr); + const u32 validSize = Memory::ValidSize(data_addr, size); // Let's handle stdout/stderr specially. if (id == PSP_STDOUT || id == PSP_STDERR) { const char *str = (const char *) data_ptr; - const int str_size = size == 0 ? 0 : (str[size - 1] == '\n' ? size - 1 : size); + const int str_size = size <= 0 ? 0 : (str[validSize - 1] == '\n' ? validSize - 1 : validSize); INFO_LOG(SCEIO, "%s: %.*s", id == 1 ? "stdout" : "stderr", str_size, str); - result = size; + result = validSize; return true; } u32 error; @@ -1160,7 +1162,7 @@ static bool __IoWrite(int &result, int id, u32 data_addr, int size, int &us) { return true; } - CBreakPoints::ExecMemCheck(data_addr, false, size, currentMIPS->pc); + NotifyMemInfo(MemBlockFlags::READ, data_addr, size, "IoWrite"); bool useThread = __KernelIsDispatchEnabled() && ioManagerThreadEnabled && size > IO_THREAD_MIN_DATA_SIZE; if (useThread) { @@ -1174,15 +1176,15 @@ static bool __IoWrite(int &result, int id, u32 data_addr, int size, int &us) { AsyncIOEvent ev = IO_EVENT_WRITE; ev.handle = f->handle; ev.buf = (u8 *) data_ptr; - ev.bytes = size; + ev.bytes = validSize; ev.invalidateAddr = 0; ioManager.ScheduleOperation(ev); return false; } else { if (GetIOTimingMethod() != IOTIMING_REALISTIC) { - result = (int) pspFileSystem.WriteFile(f->handle, (u8 *) data_ptr, size); + result = (int)pspFileSystem.WriteFile(f->handle, (u8 *) data_ptr, validSize); } else { - result = (int) pspFileSystem.WriteFile(f->handle, (u8 *) data_ptr, size, us); + result = (int)pspFileSystem.WriteFile(f->handle, (u8 *) data_ptr, validSize, us); } } return true; @@ -2017,20 +2019,22 @@ static int sceIoChangeAsyncPriority(int id, int priority) { return hleLogSuccessI(SCEIO, 0); } -static int sceIoCloseAsync(int id) -{ +static int sceIoCloseAsync(int id) { u32 error; FileNode *f = __IoGetFd(id, error); - if (f) { - f->closePending = true; - - auto ¶ms = asyncParams[id]; - params.op = IoAsyncOp::CLOSE; - IoStartAsyncThread(id, f); - return hleLogSuccessI(SCEIO, 0); - } else { + if (!f) { return hleLogError(SCEIO, error, "bad file descriptor"); } + if (f->asyncBusy()) { + return hleLogWarning(SCEIO, SCE_KERNEL_ERROR_ASYNC_BUSY, "async busy"); + } + + f->closePending = true; + + auto ¶ms = asyncParams[id]; + params.op = IoAsyncOp::CLOSE; + IoStartAsyncThread(id, f); + return hleLogSuccessI(SCEIO, 0); } static u32 sceIoSetAsyncCallback(int id, u32 clbckId, u32 clbckArg) @@ -2466,9 +2470,9 @@ static int __IoIoctl(u32 id, u32 cmd, u32 indataPtr, u32 inlen, u32 outdataPtr, // Even if the size is 4, it still actually reads a 16 byte struct, it seems. if (Memory::IsValidAddress(indataPtr) && inlen >= 4) { struct SeekInfo { - u64 offset; - u32 unk; - u32 whence; + u64_le offset; + u32_le unk; + u32_le whence; }; const auto seekInfo = PSPPointer::Create(indataPtr); FileMove seek; @@ -2570,9 +2574,9 @@ static int __IoIoctl(u32 id, u32 cmd, u32 indataPtr, u32 inlen, u32 outdataPtr, if (Memory::IsValidAddress(indataPtr) && inlen >= 4) { struct SeekInfo { - u64 offset; - u32 unk; - u32 whence; + u64_le offset; + u32_le unk; + u32_le whence; }; const auto seekInfo = PSPPointer::Create(indataPtr); FileMove seek; diff --git a/Core/HLE/sceJpeg.cpp b/Core/HLE/sceJpeg.cpp index 4230675599..d438a16111 100644 --- a/Core/HLE/sceJpeg.cpp +++ b/Core/HLE/sceJpeg.cpp @@ -84,7 +84,7 @@ static void __JpegCsc(u32 imageAddr, u32 yCbCrAddr, int widthHeight, int bufferW int width = (widthHeight >> 16) & 0xFFF; int lineWidth = std::min(width, bufferWidth); int skipEndOfLine = std::max(0, bufferWidth - lineWidth); - u32 *imageBuffer = (u32*)Memory::GetPointer(imageAddr); + u32_le *imageBuffer = (u32_le *)Memory::GetPointer(imageAddr); int sizeY = width * height; int sizeCb = sizeY >> 2; u8 *Y = (u8*)Memory::GetPointer(yCbCrAddr); @@ -150,7 +150,7 @@ static int __DecodeJpeg(u32 jpegAddr, int jpegSize, u32 imageAddr) { if (actual_components == 3) { u24_be *imageBuffer = (u24_be*)jpegBuf; - u32 *abgr = (u32*)Memory::GetPointer(imageAddr); + u32_le *abgr = (u32_le *)Memory::GetPointer(imageAddr); int pspWidth = 0; for (int w = 2; w <= 4096; w *= 2) { if (w >= width && w >= height) { diff --git a/Core/HLE/sceKernel.cpp b/Core/HLE/sceKernel.cpp index bebf6f62d7..5907848036 100644 --- a/Core/HLE/sceKernel.cpp +++ b/Core/HLE/sceKernel.cpp @@ -896,8 +896,6 @@ const HLEFunction ThreadManForKernel[] = {0X9944F31F, &WrapI_I, "sceKernelSuspendThread", 'i', "i", HLE_KERNEL_SYSCALL }, {0X75156E8F, &WrapI_I, "sceKernelResumeThread", 'i', "i", HLE_KERNEL_SYSCALL }, {0X94416130, &WrapU_UUUU, "sceKernelGetThreadmanIdList", 'x', "xxxx", HLE_KERNEL_SYSCALL }, - {0X28B6489C, &WrapI_I, "sceKernelDeleteSema", 'i', "i", HLE_KERNEL_SYSCALL }, - {0XEF9E4C70, &WrapU_I, "sceKernelDeleteEventFlag", 'x', "i", HLE_KERNEL_SYSCALL }, {0x278c0df5, &WrapI_IU, "sceKernelWaitThreadEnd", 'i', "ix", HLE_KERNEL_SYSCALL }, {0xd6da4ba1, &WrapI_CUIIU, "sceKernelCreateSema", 'i', "sxiix", HLE_KERNEL_SYSCALL }, {0x28b6489c, &WrapI_I, "sceKernelDeleteSema", 'i', "i", HLE_KERNEL_SYSCALL }, diff --git a/Core/HLE/sceKernel.h b/Core/HLE/sceKernel.h index ca9bdfc2d1..08cfb4903d 100644 --- a/Core/HLE/sceKernel.h +++ b/Core/HLE/sceKernel.h @@ -500,7 +500,7 @@ public: } } - int ListIDType(int type, SceUID *uids, int count) const { + int ListIDType(int type, SceUID_le *uids, int count) const { int total = 0; for (int i = 0; i < maxCount; i++) { if (!occupied[i]) { diff --git a/Core/HLE/sceKernelHeap.cpp b/Core/HLE/sceKernelHeap.cpp index af2cbdff1d..f584684cbb 100644 --- a/Core/HLE/sceKernelHeap.cpp +++ b/Core/HLE/sceKernelHeap.cpp @@ -60,7 +60,7 @@ static int sceKernelCreateHeap(int partitionId, int size, int flags, const char heap->name = Name ? Name : ""; // Not sure if this needs validation. heap->size = allocSize; heap->address = addr; - heap->alloc.Init(heap->address + 128, heap->size - 128); + heap->alloc.Init(heap->address + 128, heap->size - 128, true); heap->uid = uid; return hleLogSuccessInfoX(SCEKERNEL, uid); } diff --git a/Core/HLE/sceKernelInterrupt.cpp b/Core/HLE/sceKernelInterrupt.cpp index 8d8e4124db..adaabd362f 100644 --- a/Core/HLE/sceKernelInterrupt.cpp +++ b/Core/HLE/sceKernelInterrupt.cpp @@ -29,7 +29,7 @@ #include "Core/HLE/FunctionWrappers.h" #include "Core/MIPS/MIPS.h" -#include "Core/Debugger/Breakpoints.h" +#include "Core/Debugger/MemBlockInfo.h" #include "Core/HLE/sceKernel.h" #include "Core/HLE/sceKernelThread.h" #include "Core/HLE/sceKernelInterrupt.h" @@ -618,6 +618,7 @@ static u32 sceKernelMemset(u32 addr, u32 fillc, u32 n) Memory::Memset(addr, c, n); } } + NotifyMemInfo(MemBlockFlags::WRITE, addr, n, "KernelMemset"); return addr; } @@ -657,8 +658,8 @@ static u32 sceKernelMemcpy(u32 dst, u32 src, u32 size) } } - CBreakPoints::ExecMemCheck(src, false, size, currentMIPS->pc); - CBreakPoints::ExecMemCheck(dst, true, size, currentMIPS->pc); + NotifyMemInfo(MemBlockFlags::READ, src, size, "KernelMemcpy"); + NotifyMemInfo(MemBlockFlags::WRITE, dst, size, "KernelMemcpy"); return dst; } @@ -689,6 +690,8 @@ static u32 sysclib_memcpy(u32 dst, u32 src, u32 size) { if (Memory::IsValidRange(dst, size) && Memory::IsValidRange(src, size)) { memcpy(Memory::GetPointer(dst), Memory::GetPointer(src), size); } + NotifyMemInfo(MemBlockFlags::READ, src, size, "KernelMemcpy"); + NotifyMemInfo(MemBlockFlags::WRITE, dst, size, "KernelMemcpy"); return dst; } @@ -754,6 +757,7 @@ static u32 sysclib_memset(u32 destAddr, int data, int size) { if (Memory::IsValidRange(destAddr, size)) { memset(Memory::GetPointer(destAddr), data, size); } + NotifyMemInfo(MemBlockFlags::WRITE, destAddr, size, "KernelMemset"); return 0; } @@ -786,6 +790,8 @@ static u32 sysclib_memmove(u32 dst, u32 src, u32 size) { if (Memory::IsValidRange(dst, size) && Memory::IsValidRange(src, size)) { memmove(Memory::GetPointer(dst), Memory::GetPointer(src), size); } + NotifyMemInfo(MemBlockFlags::READ, src, size, "KernelMemmove"); + NotifyMemInfo(MemBlockFlags::WRITE, dst, size, "KernelMemmove"); return 0; } diff --git a/Core/HLE/sceKernelMemory.cpp b/Core/HLE/sceKernelMemory.cpp index 50b0b75fdf..fa8a80b61f 100644 --- a/Core/HLE/sceKernelMemory.cpp +++ b/Core/HLE/sceKernelMemory.cpp @@ -20,16 +20,17 @@ #include #include -#include "Common/Serialize/Serializer.h" -#include "Common/Serialize/SerializeFuncs.h" -#include "Common/Serialize/SerializeMap.h" +#include "Core/CoreTiming.h" +#include "Core/Debugger/MemBlockInfo.h" #include "Core/HLE/HLE.h" #include "Core/HLE/FunctionWrappers.h" #include "Core/System.h" #include "Core/MIPS/MIPS.h" #include "Core/MemMapHelpers.h" -#include "Core/CoreTiming.h" #include "Core/Reporting.h" +#include "Common/Serialize/Serializer.h" +#include "Common/Serialize/SerializeFuncs.h" +#include "Common/Serialize/SerializeMap.h" #include "Core/HLE/sceKernel.h" #include "Core/HLE/sceKernelThread.h" @@ -426,10 +427,11 @@ void __KernelFplEndCallback(SceUID threadID, SceUID prevCallbackId); void __KernelMemoryInit() { - kernelMemory.Init(PSP_GetKernelMemoryBase(), PSP_GetKernelMemoryEnd() - PSP_GetKernelMemoryBase()); - userMemory.Init(PSP_GetUserMemoryBase(), PSP_GetUserMemoryEnd() - PSP_GetUserMemoryBase()); - Memory::Memset(PSP_GetKernelMemoryBase(), 0, PSP_GetKernelMemoryEnd() - PSP_GetKernelMemoryBase()); - Memory::Memset(PSP_GetUserMemoryBase(), 0, PSP_GetUserMemoryEnd() - PSP_GetUserMemoryBase()); + MemBlockInfoInit(); + kernelMemory.Init(PSP_GetKernelMemoryBase(), PSP_GetKernelMemoryEnd() - PSP_GetKernelMemoryBase(), false); + userMemory.Init(PSP_GetUserMemoryBase(), PSP_GetUserMemoryEnd() - PSP_GetUserMemoryBase(), false); + Memory::Memset(PSP_GetKernelMemoryBase(), 0, PSP_GetKernelMemoryEnd() - PSP_GetKernelMemoryBase(), "MemInit"); + Memory::Memset(PSP_GetUserMemoryBase(), 0, PSP_GetUserMemoryEnd() - PSP_GetUserMemoryBase(), "MemInit"); INFO_LOG(SCEKERNEL, "Kernel and user memory pools initialized"); vplWaitTimer = CoreTiming::RegisterEvent("VplTimeout", __KernelVplTimeout); @@ -471,6 +473,8 @@ void __KernelMemoryDoState(PointerWrap &p) if (s >= 2) { Do(p, tlsplThreadEndChecks); } + + MemBlockInfoDoState(p); } void __KernelMemoryShutdown() @@ -486,6 +490,7 @@ void __KernelMemoryShutdown() #endif kernelMemory.Shutdown(); tlsplThreadEndChecks.clear(); + MemBlockInfoShutdown(); } enum SceKernelFplAttr @@ -510,6 +515,7 @@ static bool __KernelUnlockFplForThread(FPL *fpl, FplWaitingThread &threadInfo, u { u32 blockPtr = fpl->address + fpl->alignedSize * blockNum; Memory::Write_U32(blockPtr, threadInfo.addrPtr); + NotifyMemInfo(MemBlockFlags::SUB_ALLOC, blockPtr, fpl->alignedSize, "FplAllocate"); } else return false; @@ -722,6 +728,7 @@ int sceKernelAllocateFpl(SceUID uid, u32 blockPtrAddr, u32 timeoutPtr) if (blockNum >= 0) { u32 blockPtr = fpl->address + fpl->alignedSize * blockNum; Memory::Write_U32(blockPtr, blockPtrAddr); + NotifyMemInfo(MemBlockFlags::SUB_ALLOC, blockPtr, fpl->alignedSize, "FplAllocate"); } else { SceUID threadID = __KernelGetCurThread(); HLEKernel::RemoveWaitingThread(fpl->waitingThreads, threadID); @@ -753,6 +760,7 @@ int sceKernelAllocateFplCB(SceUID uid, u32 blockPtrAddr, u32 timeoutPtr) if (blockNum >= 0) { u32 blockPtr = fpl->address + fpl->alignedSize * blockNum; Memory::Write_U32(blockPtr, blockPtrAddr); + NotifyMemInfo(MemBlockFlags::SUB_ALLOC, blockPtr, fpl->alignedSize, "FplAllocate"); } else { SceUID threadID = __KernelGetCurThread(); HLEKernel::RemoveWaitingThread(fpl->waitingThreads, threadID); @@ -784,6 +792,7 @@ int sceKernelTryAllocateFpl(SceUID uid, u32 blockPtrAddr) if (blockNum >= 0) { u32 blockPtr = fpl->address + fpl->alignedSize * blockNum; Memory::Write_U32(blockPtr, blockPtrAddr); + NotifyMemInfo(MemBlockFlags::SUB_ALLOC, blockPtr, fpl->alignedSize, "FplAllocate"); return 0; } else { return SCE_KERNEL_ERROR_NO_MEMORY; @@ -812,6 +821,9 @@ int sceKernelFreeFpl(SceUID uid, u32 blockPtr) return SCE_KERNEL_ERROR_ILLEGAL_MEMBLOCK; } else { if (fpl->freeBlock(blockNum)) { + u32 blockPtr = fpl->address + fpl->alignedSize * blockNum; + NotifyMemInfo(MemBlockFlags::SUB_FREE, blockPtr, fpl->alignedSize, "FplFree"); + DEBUG_LOG(SCEKERNEL, "sceKernelFreeFpl(%i, %08x)", uid, blockPtr); __KernelSortFplThreads(fpl); @@ -1503,7 +1515,7 @@ SceUID sceKernelCreateVpl(const char *name, int partition, u32 attr, u32 vplSize // A vpl normally has accounting stuff in the first 32 bytes. vpl->address = memBlockPtr + 0x20; - vpl->alloc.Init(vpl->address, vpl->nv.poolSize); + vpl->alloc.Init(vpl->address, vpl->nv.poolSize, true); vpl->header = PSPPointer::Create(memBlockPtr); vpl->header->Init(memBlockPtr, vplSize); @@ -1572,7 +1584,7 @@ static bool __KernelAllocateVpl(SceUID uid, u32 size, u32 addrPtr, u32 &error, b } else { // Padding (normally used to track the allocation.) u32 allocSize = size + 8; - addr = vpl->alloc.Alloc(allocSize, true); + addr = vpl->alloc.Alloc(allocSize, true, "VplAllocate"); } if (addr != (u32) -1) { Memory::Write_U32(addr, addrPtr); @@ -1953,9 +1965,10 @@ int __KernelFreeTls(TLSPL *tls, SceUID threadID) u32 alignedSize = (tls->ntls.blockSize + tls->alignment - 1) & ~(tls->alignment - 1); u32 freedAddress = tls->address + freeBlock * alignedSize; + NotifyMemInfo(MemBlockFlags::SUB_ALLOC, freedAddress, tls->ntls.blockSize, "TlsFree"); // Whenever freeing a block, clear it (even if it's not going to wake anyone.) - Memory::Memset(freedAddress, 0, tls->ntls.blockSize); + Memory::Memset(freedAddress, 0, tls->ntls.blockSize, "TlsFree"); // First, let's remove the end check for the freeing thread. auto freeingLocked = tlsplThreadEndChecks.equal_range(threadID); @@ -2227,10 +2240,12 @@ int sceKernelGetTlsAddr(SceUID uid) u32 alignedSize = (tls->ntls.blockSize + tls->alignment - 1) & ~(tls->alignment - 1); u32 allocAddress = tls->address + allocBlock * alignedSize; + NotifyMemInfo(MemBlockFlags::SUB_ALLOC, allocAddress, tls->ntls.blockSize, "TlsAddr"); // We clear the blocks upon first allocation (and also when they are freed, both are necessary.) - if (needsClear) - Memory::Memset(allocAddress, 0, tls->ntls.blockSize); + if (needsClear) { + Memory::Memset(allocAddress, 0, tls->ntls.blockSize, "TlsAddr"); + } return allocAddress; } diff --git a/Core/HLE/sceKernelModule.cpp b/Core/HLE/sceKernelModule.cpp index a60b07e71f..cd98ce0b00 100644 --- a/Core/HLE/sceKernelModule.cpp +++ b/Core/HLE/sceKernelModule.cpp @@ -21,6 +21,7 @@ #include "zlib.h" +#include "Common/Data/Convert/SmallDataConvert.h" #include "Common/Serialize/Serializer.h" #include "Common/Serialize/SerializeFuncs.h" #include "Common/Serialize/SerializeSet.h" @@ -95,6 +96,11 @@ static const char * const lieAboutSuccessModules[] = { "flash0:/kd/audiocodec_260.prx", "flash0:/kd/libatrac3plus.prx", "disc0:/PSP_GAME/SYSDIR/UPDATE/EBOOT.BIN", + "flash0:/kd/ifhandle.prx", + "flash0:/kd/pspnet.prx", + "flash0:/kd/pspnet_inet.prx", + "flash0:/kd/pspnet_apctl.prx", + "flash0:/kd/pspnet_resolver.prx", }; // Modules to not load. TODO: Look into loosening this a little (say sceFont). @@ -113,9 +119,14 @@ static const char * const blacklistedModules[] = { "sceNetInet_Library", "sceNetResolver_Library", "sceNet_Library", + "sceNetAdhoc_Library", + "sceNetAdhocAuth_Service", + "sceNetAdhocctl_Library", + "sceNetIfhandle_Service", "sceSsl_Module", "sceDEFLATE_Library", "sceMD5_Library", + "sceMemab", }; struct WriteVarSymbolState; @@ -625,7 +636,7 @@ static void WriteVarSymbol(WriteVarSymbolState &state, u32 exportAddress, u32 re case R_MIPS_LO16: { // Sign extend the existing low value (e.g. from addiu.) - const u32 offsetLo = (s32)(s16)(u16)(relocData & 0xFFFF); + const u32 offsetLo = SignExtend16ToU32(relocData); u32 full = exportAddress; // This is only used in the error case (no hi/wrong hi.) if (!reverse) { @@ -855,7 +866,7 @@ void PSPModule::Cleanup() { for (u32 i = 0; i < (u32)(nm.text_size + 3); i += 4) { Memory::Write_U32(MIPS_MAKE_BREAK(1), nm.text_addr + i); } - Memory::Memset(nm.text_addr + nm.text_size, -1, nm.data_size + nm.bss_size); + Memory::Memset(nm.text_addr + nm.text_size, -1, nm.data_size + nm.bss_size, "ModuleClear"); // Let's also invalidate, just to make sure it's cleared out for any future data. currentMIPS->InvalidateICache(memoryBlockAddr, memoryBlockSize); @@ -873,7 +884,7 @@ static void __SaveDecryptedEbootToStorageMedia(const u8 *decryptedEbootDataPtr, return; } - const std::string filenameToDumpTo = g_paramSFO.GetValueString("DISC_ID") + ".BIN"; + const std::string filenameToDumpTo = g_paramSFO.GetDiscID() + ".BIN"; const std::string dumpDirectory = GetSysDirectory(DIRECTORY_DUMP); const std::string fullPath = dumpDirectory + filenameToDumpTo; @@ -1006,8 +1017,12 @@ static bool KernelImportModuleFuncs(PSPModule *module, u32 *firstImportStubAddr, } } + // Prevent infinite spin on bad data. + if (entry->size == 0) + break; + // If nidData is 0, only variables are being imported. - if (entry->nidData != 0) { + if (entry->numFuncs > 0 && entry->nidData != 0) { if (!Memory::IsValidAddress(entry->nidData)) { ERROR_LOG_REPORT(LOADER, "Crazy nidData address %08x, skipping entire module", entry->nidData); needReport = true; @@ -1036,7 +1051,7 @@ static bool KernelImportModuleFuncs(PSPModule *module, u32 *firstImportStubAddr, // We skip vars when reimporting, since we might double-offset. // We only reimport funcs, which can't be double-offset. - if (entry->varData != 0 && !reimporting) { + if (entry->numVars > 0 && entry->varData != 0 && !reimporting) { if (!Memory::IsValidAddress(entry->varData)) { ERROR_LOG_REPORT(LOADER, "Crazy varData address %08x, skipping rest of module", entry->varData); needReport = true; @@ -1088,7 +1103,7 @@ static bool KernelImportModuleFuncs(PSPModule *module, u32 *firstImportStubAddr, } snprintf(temp, sizeof(temp), "%s ver=%04x, flags=%04x, size=%d, numVars=%d, numFuncs=%d, nidData=%08x, firstSym=%08x, varData=%08x, extra=%08x\n", - modulename, entry->version, entry->flags, entry->size, entry->numVars, entry->numFuncs, entry->nidData, entry->firstSymAddr, entry->size >= 6 ? entry->varData : 0, entry->size >= 7 ? entry->extra : 0); + modulename, entry->version, entry->flags, entry->size, entry->numVars, entry->numFuncs, entry->nidData, entry->firstSymAddr, entry->size >= 6 ? (u32)entry->varData : 0, entry->size >= 7 ? (u32)entry->extra : 0); debugInfo += temp; } @@ -1336,7 +1351,7 @@ static PSPModule *__KernelLoadELFFromPtr(const u8 *ptr, size_t elfSize, u32 load if (textSection == -1) { module->textStart = reader.GetVaddr(); - module->textEnd = firstImportStubAddr - 4; + module->textEnd = firstImportStubAddr == 0 ? reader.GetVaddr() : firstImportStubAddr - 4; // Reference Jpcsp. if (reader.GetFirstSegmentAlign() > 0) module->textStart &= ~(reader.GetFirstSegmentAlign() - 1); @@ -1737,13 +1752,13 @@ bool __KernelLoadExec(const char *filename, u32 paramPtr, std::string *error_str if (param.args > 0) { u32 argpAddr = param.argp; param_argp = new u8[param.args]; - Memory::Memcpy(param_argp, argpAddr, param.args); + Memory::Memcpy(param_argp, argpAddr, param.args, "KernelLoadParam"); } if (param.keyp != 0) { u32 keyAddr = param.keyp; size_t keylen = strlen(Memory::GetCharPointer(keyAddr))+1; param_key = new u8[keylen]; - Memory::Memcpy(param_key, keyAddr, (u32)keylen); + Memory::Memcpy(param_key, keyAddr, (u32)keylen, "KernelLoadParam"); } __KernelLoadReset(); @@ -2443,12 +2458,18 @@ u32 sceKernelFindModuleByName(const char *name) PSPModule *module = kernelObjects.Get(moduleId, error); if (!module) continue; - if (!module->isFake && strcmp(name, module->nm.name) == 0) { - INFO_LOG(SCEMODULE, "%d = sceKernelFindModuleByName(%s)", module->modulePtr, name); - return module->modulePtr; + if (strcmp(name, module->nm.name) == 0) { + if (!module->isFake) { + INFO_LOG(SCEMODULE, "%d = sceKernelFindModuleByName(%s)", module->modulePtr, name); + return module->modulePtr; + } + else { + WARN_LOG(SCEMODULE, "0 = sceKernelFindModuleByName(%s): Module Fake", name); + return hleDelayResult(0, "Module Fake", 1000 * 1000); + } } } - WARN_LOG(SCEMODULE, "0 = sceKernelFindModuleByName(%s): Module Not Found or Fake", name); + WARN_LOG(SCEMODULE, "0 = sceKernelFindModuleByName(%s): Module Not Found", name); return 0; } diff --git a/Core/HLE/sceKernelMsgPipe.cpp b/Core/HLE/sceKernelMsgPipe.cpp index 2df1f2d105..24d538609e 100644 --- a/Core/HLE/sceKernelMsgPipe.cpp +++ b/Core/HLE/sceKernelMsgPipe.cpp @@ -105,7 +105,7 @@ struct MsgPipeWaitingThread void ReadBuffer(u32 destPtr, u32 len) { - Memory::Memcpy(destPtr, bufAddr + bufSize - freeSize, len); + Memory::Memcpy(destPtr, bufAddr + bufSize - freeSize, len, "MsgPipeReadBuffer"); freeSize -= len; if (transferredBytes.IsValid()) *transferredBytes += len; @@ -113,7 +113,7 @@ struct MsgPipeWaitingThread void WriteBuffer(u32 srcPtr, u32 len) { - Memory::Memcpy(bufAddr + (bufSize - freeSize), srcPtr, len); + Memory::Memcpy(bufAddr + (bufSize - freeSize), srcPtr, len, "MsgPipeWriteBuffer"); freeSize -= len; if (transferredBytes.IsValid()) *transferredBytes += len; @@ -399,7 +399,7 @@ static int __KernelSendMsgPipe(MsgPipe *m, u32 sendBufAddr, u32 sendSize, int wa if (bytesToSend != 0) { - Memory::Memcpy(m->buffer + (m->nmp.bufSize - m->nmp.freeSize), sendBufAddr, bytesToSend); + Memory::Memcpy(m->buffer + (m->nmp.bufSize - m->nmp.freeSize), sendBufAddr, bytesToSend, "MsgPipeSend"); m->nmp.freeSize -= bytesToSend; curSendAddr += bytesToSend; sendSize -= bytesToSend; @@ -492,7 +492,7 @@ static int __KernelReceiveMsgPipe(MsgPipe *m, u32 receiveBufAddr, u32 receiveSiz u32 bytesToReceive = std::min(receiveSize, m->GetUsedSize()); if (bytesToReceive != 0) { - Memory::Memcpy(curReceiveAddr, m->buffer, bytesToReceive); + Memory::Memcpy(curReceiveAddr, m->buffer, bytesToReceive, "MsgPipeReceive"); m->nmp.freeSize += bytesToReceive; memmove(Memory::GetPointer(m->buffer), Memory::GetPointer(m->buffer) + bytesToReceive, m->GetUsedSize()); curReceiveAddr += bytesToReceive; @@ -610,7 +610,6 @@ static bool __KernelCheckResumeMsgPipeReceive(MsgPipe *m, MsgPipeWaitingThread & static void __KernelMsgPipeEndCallback(SceUID threadID, SceUID prevCallbackId) { u32 error; u32 waitValue = __KernelGetWaitValue(threadID, error); - u32 timeoutPtr = __KernelGetWaitTimeoutPtr(threadID, error); SceUID uid = __KernelGetWaitID(threadID, WAITTYPE_MSGPIPE, error); MsgPipe *ko = uid == 0 ? NULL : kernelObjects.Get(uid, error); diff --git a/Core/HLE/sceKernelMutex.cpp b/Core/HLE/sceKernelMutex.cpp index 1b6b314764..ea767c197d 100644 --- a/Core/HLE/sceKernelMutex.cpp +++ b/Core/HLE/sceKernelMutex.cpp @@ -1089,7 +1089,7 @@ static int __KernelReferLwMutexStatus(SceUID uid, u32 infoPtr) // Refresh and write m->nm.currentCount = workarea->lockLevel; - m->nm.lockThread = workarea->lockThread == 0 ? -1 : workarea->lockThread; + m->nm.lockThread = workarea->lockThread == 0 ? SceUID_le(-1) : workarea->lockThread; m->nm.numWaitThreads = (int) m->waitingThreads.size(); Memory::WriteStruct(infoPtr, &m->nm); } diff --git a/Core/HLE/sceKernelThread.cpp b/Core/HLE/sceKernelThread.cpp index 8f4bf88ef0..c8732fd74e 100644 --- a/Core/HLE/sceKernelThread.cpp +++ b/Core/HLE/sceKernelThread.cpp @@ -51,11 +51,10 @@ #include "Core/HLE/KernelWaitHelpers.h" #include "Core/HLE/ThreadQueueList.h" -typedef struct -{ +struct WaitTypeNames { WaitType type; const char *name; -} WaitTypeNames; +}; const WaitTypeNames waitTypeNames[] = { { WAITTYPE_NONE, "None" }, @@ -84,18 +83,13 @@ const WaitTypeNames waitTypeNames[] = { { WAITTYPE_ASYNCIO, "AsyncIO" }, { WAITTYPE_MICINPUT, "Microphone input"}, { WAITTYPE_NET, "Network"}, + { WAITTYPE_USB, "USB" }, }; -const char *getWaitTypeName(WaitType type) -{ - int waitTypeNamesAmount = sizeof(waitTypeNames)/sizeof(WaitTypeNames); - - for (int i = 0; i < waitTypeNamesAmount; i++) - { - if (waitTypeNames[i].type == type) - { - return waitTypeNames[i].name; - } +const char *getWaitTypeName(WaitType type) { + for (WaitTypeNames info : waitTypeNames) { + if (info.type == type) + return info.name; } return "Unknown"; @@ -433,7 +427,7 @@ public: bool FillStack() { // Fill the stack. if ((nt.attr & PSP_THREAD_ATTR_NO_FILLSTACK) == 0) { - Memory::Memset(currentStack.start, 0xFF, nt.stackSize); + Memory::Memset(currentStack.start, 0xFF, nt.stackSize, "ThreadFillStack"); } context.r[MIPS_REG_SP] = currentStack.start + nt.stackSize; currentStack.end = context.r[MIPS_REG_SP]; @@ -441,7 +435,7 @@ public: context.r[MIPS_REG_SP] -= 256; context.r[MIPS_REG_K0] = context.r[MIPS_REG_SP]; u32 k0 = context.r[MIPS_REG_K0]; - Memory::Memset(k0, 0, 0x100); + Memory::Memset(k0, 0, 0x100, "ThreadK0"); Memory::Write_U32(GetUID(), k0 + 0xc0); Memory::Write_U32(nt.initialStack, k0 + 0xc8); Memory::Write_U32(0xffffffff, k0 + 0xf8); @@ -457,7 +451,7 @@ public: DEBUG_LOG(SCEKERNEL, "Freeing thread stack %s", nt.name); if ((nt.attr & PSP_THREAD_ATTR_CLEAR_STACK) != 0 && nt.initialStack != 0) { - Memory::Memset(nt.initialStack, 0, nt.stackSize); + Memory::Memset(nt.initialStack, 0, nt.stackSize, "ThreadFreeStack"); } if (nt.attr & PSP_THREAD_ATTR_KERNEL) { @@ -482,7 +476,7 @@ public: nt.stackSize = currentStack.end - currentStack.start; // We still drop the threadID at the bottom and fill it, but there's no k0. - Memory::Memset(currentStack.start, 0xFF, nt.stackSize); + Memory::Memset(currentStack.start, 0xFF, nt.stackSize, "ThreadExtendStack"); Memory::Write_U32(GetUID(), nt.initialStack); return true; } @@ -758,8 +752,6 @@ u32 __KernelInterruptReturnAddress() { } static void __KernelDelayBeginCallback(SceUID threadID, SceUID prevCallbackId) { - SceUID pauseKey = prevCallbackId == 0 ? threadID : prevCallbackId; - u32 error; SceUID waitID = __KernelGetWaitID(threadID, WAITTYPE_DELAY, error); if (waitID == threadID) { @@ -943,7 +935,7 @@ void __KernelThreadingInit() lastSwitchCycles = 0; idleThreadHackAddr = kernelMemory.Alloc(blockSize, false, "threadrethack"); - Memory::Memcpy(idleThreadHackAddr, idleThreadCode, sizeof(idleThreadCode)); + Memory::Memcpy(idleThreadHackAddr, idleThreadCode, sizeof(idleThreadCode), "ThreadMIPS"); u32 pos = idleThreadHackAddr + sizeof(idleThreadCode); for (size_t i = 0; i < ARRAY_SIZE(threadHacks); ++i) { @@ -1196,7 +1188,7 @@ void __KernelThreadingShutdown() { std::string __KernelThreadingSummary() { PSPThread *t = __GetCurrentThread(); - return StringFromFormat("Cur thread: %s (attr %08x)", t ? t->GetName() : "(null)", t ? t->nt.attr : 0); + return StringFromFormat("Cur thread: %s (attr %08x)", t ? t->GetName() : "(null)", t ? (u32)t->nt.attr : 0); } const char *__KernelGetThreadName(SceUID threadID) @@ -1213,7 +1205,15 @@ bool KernelIsThreadDormant(SceUID threadID) { PSPThread *t = kernelObjects.Get(threadID, error); if (t) return (t->nt.status & (THREADSTATUS_DEAD | THREADSTATUS_DORMANT)) != 0; - return 0; + return false; +} + +bool KernelIsThreadWaiting(SceUID threadID) { + u32 error; + PSPThread *t = kernelObjects.Get(threadID, error); + if (t) + return (t->nt.status & (THREADSTATUS_WAITSUSPEND)) != 0; + return false; } u32 __KernelGetWaitValue(SceUID threadID, u32 &error) { @@ -1283,15 +1283,15 @@ u32 sceKernelReferThreadStatus(u32 threadID, u32 statusPtr) t->nt.nativeSize = THREADINFO_SIZE_AFTER_260; if (wantedSize != 0) - Memory::Memcpy(statusPtr, &t->nt, std::min(wantedSize, (u32)sizeof(t->nt))); + Memory::Memcpy(statusPtr, &t->nt, std::min(wantedSize, (u32)sizeof(t->nt)), "ThreadStatus"); // TODO: What is this value? Basic tests show 0... if (wantedSize > sizeof(t->nt)) - Memory::Memset(statusPtr + sizeof(t->nt), 0, wantedSize - sizeof(t->nt)); + Memory::Memset(statusPtr + sizeof(t->nt), 0, wantedSize - sizeof(t->nt), "ThreadStatus"); } else { t->nt.nativeSize = THREADINFO_SIZE; u32 sz = std::min(THREADINFO_SIZE, wantedSize); if (sz != 0) - Memory::Memcpy(statusPtr, &t->nt, sz); + Memory::Memcpy(statusPtr, &t->nt, sz, "ThreadStatus"); } hleEatCycles(1400); @@ -1401,7 +1401,7 @@ u32 sceKernelGetThreadmanIdList(u32 type, u32 readBufPtr, u32 readBufSize, u32 i } u32 total = 0; - auto uids = PSPPointer::Create(readBufPtr); + auto uids = PSPPointer::Create(readBufPtr); u32 error; if (type > 0 && type <= SCE_KERNEL_TMID_Tlspl) { DEBUG_LOG(SCEKERNEL, "sceKernelGetThreadmanIdList(%i, %08x, %i, %08x)", type, readBufPtr, readBufSize, idCountPtr); @@ -1521,16 +1521,16 @@ u32 __KernelResumeThreadFromWait(SceUID threadID, u64 retval) } // makes the current thread wait for an event -void __KernelWaitCurThread(WaitType type, SceUID waitID, u32 waitValue, u32 timeoutPtr, bool processCallbacks, const char *reason) -{ - if (!dispatchEnabled) - { +void __KernelWaitCurThread(WaitType type, SceUID waitID, u32 waitValue, u32 timeoutPtr, bool processCallbacks, const char *reason) { + if (!dispatchEnabled) { WARN_LOG_REPORT(SCEKERNEL, "Ignoring wait, dispatching disabled... right thing to do?"); return; } PSPThread *thread = __GetCurrentThread(); _assert_(thread != nullptr); + if ((thread->nt.status & THREADSTATUS_WAIT) != 0) + WARN_LOG_REPORT(SCEKERNEL, "Waiting thread for %d that was already waiting for %d", type, thread->nt.waitType); thread->nt.waitID = waitID; thread->nt.waitType = type; __KernelChangeThreadState(thread, ThreadStatus(THREADSTATUS_WAIT | (thread->nt.status & THREADSTATUS_SUSPEND))); @@ -1538,22 +1538,21 @@ void __KernelWaitCurThread(WaitType type, SceUID waitID, u32 waitValue, u32 time thread->waitInfo.waitValue = waitValue; thread->waitInfo.timeoutPtr = timeoutPtr; - // TODO: time waster if (!reason) reason = "started wait"; hleReSchedule(processCallbacks, reason); } -void __KernelWaitCallbacksCurThread(WaitType type, SceUID waitID, u32 waitValue, u32 timeoutPtr) -{ - if (!dispatchEnabled) - { +void __KernelWaitCallbacksCurThread(WaitType type, SceUID waitID, u32 waitValue, u32 timeoutPtr) { + if (!dispatchEnabled) { WARN_LOG_REPORT(SCEKERNEL, "Ignoring wait, dispatching disabled... right thing to do?"); return; } PSPThread *thread = __GetCurrentThread(); + if ((thread->nt.status & THREADSTATUS_WAIT) != 0) + WARN_LOG_REPORT(SCEKERNEL, "Waiting thread for %d that was already waiting for %d", type, thread->nt.waitType); thread->nt.waitID = waitID; thread->nt.waitType = type; __KernelChangeThreadState(thread, ThreadStatus(THREADSTATUS_WAIT | (thread->nt.status & THREADSTATUS_SUSPEND))); @@ -1935,7 +1934,7 @@ SceUID __KernelSetupRootThread(SceUID moduleID, int args, const char *argp, int u32 location = currentMIPS->r[MIPS_REG_SP]; currentMIPS->r[MIPS_REG_A1] = location; if (argp) - Memory::Memcpy(location, argp, args); + Memory::Memcpy(location, argp, args, "ThreadParam"); // Let's assume same as starting a new thread, 64 bytes for safety/kernel. currentMIPS->r[MIPS_REG_SP] -= 64; @@ -2022,26 +2021,29 @@ int __KernelStartThread(SceUID threadToStartID, int argSize, u32 argBlockPtr, bo return error; PSPThread *cur = __GetCurrentThread(); - __KernelResetThread(startThread, cur ? cur->nt.currentPriority : 0); + __KernelResetThread(startThread, cur ? (s32)cur->nt.currentPriority : 0); u32 &sp = startThread->context.r[MIPS_REG_SP]; // Force args means just use those as a0/a1 without any special treatment. // This is a hack to avoid allocating memory for helper threads which take args. - if ((argBlockPtr && argSize > 0) || forceArgs) { + if (forceArgs) { + startThread->context.r[MIPS_REG_A0] = argSize; + startThread->context.r[MIPS_REG_A1] = argBlockPtr; + } else if (argBlockPtr && argSize > 0) { // Make room for the arguments, always 0x10 aligned. - if (!forceArgs) - sp -= (argSize + 0xf) & ~0xf; + sp -= (argSize + 0xf) & ~0xf; startThread->context.r[MIPS_REG_A0] = argSize; startThread->context.r[MIPS_REG_A1] = sp; + + // Now copy argument to stack. + if (Memory::IsValidAddress(argBlockPtr)) { + Memory::Memcpy(sp, argBlockPtr, argSize, "ThreadStartArgs"); + } } else { startThread->context.r[MIPS_REG_A0] = 0; startThread->context.r[MIPS_REG_A1] = 0; } - // Now copy argument to stack. - if (!forceArgs && Memory::IsValidAddress(argBlockPtr)) - Memory::Memcpy(sp, argBlockPtr, argSize); - // On the PSP, there's an extra 64 bytes of stack eaten after the args. // This could be stack overflow safety, or just stack eaten by the kernel entry func. sp -= 64; diff --git a/Core/HLE/sceKernelThread.h b/Core/HLE/sceKernelThread.h index 3df1d70fc0..b7e77c95b9 100644 --- a/Core/HLE/sceKernelThread.h +++ b/Core/HLE/sceKernelThread.h @@ -110,6 +110,7 @@ enum WaitType : int WAITTYPE_ASYNCIO = 23, WAITTYPE_MICINPUT = 24, // fake WAITTYPE_NET = 25, // fake + WAITTYPE_USB = 26, // fake NUM_WAITTYPES }; @@ -173,6 +174,7 @@ u32 __KernelGetCurThreadStack(); u32 __KernelGetCurThreadStackStart(); const char *__KernelGetThreadName(SceUID threadID); bool KernelIsThreadDormant(SceUID threadID); +bool KernelIsThreadWaiting(SceUID threadID); void __KernelSaveContext(PSPThreadContext *ctx, bool vfpuEnabled); void __KernelLoadContext(PSPThreadContext *ctx, bool vfpuEnabled); diff --git a/Core/HLE/sceKernelVTimer.cpp b/Core/HLE/sceKernelVTimer.cpp index 245db8a395..63efc53eda 100644 --- a/Core/HLE/sceKernelVTimer.cpp +++ b/Core/HLE/sceKernelVTimer.cpp @@ -514,7 +514,7 @@ u32 sceKernelReferVTimerStatus(SceUID uid, u32 statusAddr) { NativeVTimer status = vt->nvt; u32 size = Memory::Read_U32(statusAddr); status.current = __getVTimerCurrentTime(vt); - Memory::Memcpy(statusAddr, &status, std::min(size, (u32)sizeof(status))); + Memory::Memcpy(statusAddr, &status, std::min(size, (u32)sizeof(status)), "VTimerStatus"); } return 0; diff --git a/Core/HLE/sceMp3.cpp b/Core/HLE/sceMp3.cpp index b3bdcb9b18..de9c3fd8ae 100644 --- a/Core/HLE/sceMp3.cpp +++ b/Core/HLE/sceMp3.cpp @@ -18,21 +18,24 @@ #include #include -#include "Common/Serialize/SerializeFuncs.h" -#include "Common/Serialize/SerializeMap.h" #include "Core/Config.h" +#include "Core/Debugger/MemBlockInfo.h" #include "Core/HLE/HLE.h" #include "Core/HLE/FunctionWrappers.h" +#include "Core/HLE/sceKernelMemory.h" #include "Core/HLE/sceMp3.h" #include "Core/HW/MediaEngine.h" +#include "Core/HW/SimpleAudioDec.h" #include "Core/MemMap.h" #include "Core/Reporting.h" -#include "Core/HW/SimpleAudioDec.h" +#include "Common/Serialize/SerializeFuncs.h" +#include "Common/Serialize/SerializeMap.h" static const u32 ERROR_MP3_INVALID_HANDLE = 0x80671001; static const u32 ERROR_MP3_UNRESERVED_HANDLE = 0x80671102; static const u32 ERROR_MP3_NOT_YET_INIT_HANDLE = 0x80671103; static const u32 ERROR_MP3_NO_RESOURCE_AVAIL = 0x80671201; +static const u32 ERROR_MP3_BAD_SAMPLE_RATE = 0x80671302; static const u32 ERROR_MP3_BAD_RESET_FRAME = 0x80671501; static const u32 ERROR_MP3_BAD_ADDR = 0x80671002; static const u32 ERROR_MP3_BAD_SIZE = 0x80671003; @@ -157,19 +160,21 @@ void __Mp3DoState(PointerWrap &p) { } static int sceMp3Decode(u32 mp3, u32 outPcmPtr) { - DEBUG_LOG(ME, "sceMp3Decode(%08x,%08x)", mp3, outPcmPtr); - AuCtx *ctx = getMp3Ctx(mp3); if (!ctx) { - ERROR_LOG(ME, "%s: bad mp3 handle %08x", __FUNCTION__, mp3); - return -1; + if (mp3 >= MP3_MAX_HANDLES) + return hleLogError(ME, ERROR_MP3_INVALID_HANDLE, "invalid handle"); + return hleLogError(ME, ERROR_MP3_NOT_YET_INIT_HANDLE, "unreserved handle"); + } else if (ctx->Version < 0 || ctx->AuBuf == 0) { + return hleLogError(ME, ERROR_MP3_NOT_YET_INIT_HANDLE, "not yet init"); } int pcmBytes = ctx->AuDecode(outPcmPtr); if (pcmBytes > 0) { // decode data successfully, delay thread - return hleDelayResult(pcmBytes, "mp3 decode", mp3DecodeDelay); + return hleDelayResult(hleLogSuccessI(ME, pcmBytes), "mp3 decode", mp3DecodeDelay); } + // Should already have logged. return pcmBytes; } @@ -377,6 +382,7 @@ static int FindMp3Header(AuCtx *ctx, int &header, int end) { } static int sceMp3Init(u32 mp3) { + int sdkver = sceKernelGetCompiledSdkVersion(); AuCtx *ctx = getMp3Ctx(mp3); if (!ctx) { if (mp3 >= MP3_MAX_HANDLES) @@ -397,30 +403,41 @@ static int sceMp3Init(u32 mp3) { // Parse the Mp3 header int layerBits = (header >> 17) & 0x3; int versionBits = (header >> 19) & 0x3; - ctx->SamplingRate = __CalculateMp3SampleRates((header >> 10) & 0x3, versionBits); - ctx->Channels = __CalculateMp3Channels((header >> 6) & 0x3); - ctx->BitRate = __CalculateMp3Bitrates((header >> 12) & 0xF, versionBits, layerBits); - ctx->MaxOutputSample = CalculateMp3SamplesPerFrame(versionBits, layerBits); - ctx->freq = ctx->SamplingRate; + int bitrate = __CalculateMp3Bitrates((header >> 12) & 0xF, versionBits, layerBits); + int samplerate = __CalculateMp3SampleRates((header >> 10) & 0x3, versionBits);; + int channels = __CalculateMp3Channels((header >> 6) & 0x3); - DEBUG_LOG(ME, "sceMp3Init(): channels=%i, samplerate=%iHz, bitrate=%ikbps", ctx->Channels, ctx->SamplingRate, ctx->BitRate); + DEBUG_LOG(ME, "sceMp3Init(): channels=%i, samplerate=%iHz, bitrate=%ikbps, layerBits=%d ,versionBits=%d,HEADER: %08x", channels, samplerate, bitrate, layerBits, versionBits, header); if (layerBits != 1) { // TODO: Should return ERROR_AVCODEC_INVALID_DATA. WARN_LOG_REPORT(ME, "sceMp3Init: invalid data: not layer 3"); } + if (bitrate == 0 || bitrate == -1) { + return hleDelayResult(hleReportError(ME, ERROR_AVCODEC_INVALID_DATA, "invalid bitrate v%d l%d rate %04x", versionBits, layerBits, (header >> 12) & 0xF), "mp3 init", PARSE_DELAY_MS); + } + if (samplerate == -1) { + return hleDelayResult(hleReportError(ME, ERROR_AVCODEC_INVALID_DATA, "invalid sample rate v%d l%d rate %02x", versionBits, layerBits, (header >> 10) & 0x3), "mp3 init", PARSE_DELAY_MS); + } + + // Before we allow init, newer SDK versions next require at least 156 bytes. + // That happens to be the size of the first frame header for VBR. + if (sdkver >= 0x06000000 && ctx->readPos < 156) { + return hleDelayResult(hleLogError(ME, SCE_KERNEL_ERROR_INVALID_VALUE, "insufficient mp3 data for init"), "mp3 init", PARSE_DELAY_MS); + } + + ctx->SamplingRate = samplerate; + ctx->Channels = channels; + ctx->BitRate = bitrate; + ctx->MaxOutputSample = CalculateMp3SamplesPerFrame(versionBits, layerBits); + ctx->freq = ctx->SamplingRate; + if (versionBits != 3) { // TODO: Should return 0x80671301 (unsupported version?) WARN_LOG_REPORT(ME, "sceMp3Init: invalid data: not MPEG v1"); } - if (ctx->BitRate == 0 || ctx->BitRate == -1) { - return hleDelayResult(hleReportError(ME, ERROR_AVCODEC_INVALID_DATA, "invalid bitrate v%d l%d rate %04x", versionBits, layerBits, (header >> 12) & 0xF), "mp3 init", PARSE_DELAY_MS); - } - if (ctx->SamplingRate == -1) { - return hleDelayResult(hleReportError(ME, ERROR_AVCODEC_INVALID_DATA, "invalid sample rate v%d l%d rate %02x", versionBits, layerBits, (header >> 10) & 0x3), "mp3 init", PARSE_DELAY_MS); - } else if (ctx->SamplingRate != 44100) { - // TODO: Should return 0x80671302 (unsupported sample rate?) - WARN_LOG_REPORT(ME, "sceMp3Init: invalid data: not 44.1kHz"); + if (samplerate != 44100 && sdkver < 3090500) { + return hleDelayResult(hleLogError(ME, ERROR_MP3_BAD_SAMPLE_RATE, "invalid data: not 44.1kHz"), "mp3 init", PARSE_DELAY_MS); } // Based on bitrate, we can calculate the frame size in bytes. @@ -432,11 +449,8 @@ static int sceMp3Init(u32 mp3) { ctx->Version = versionBits; - // for mp3, if required freq is 48000, reset resampling Frequency to 48000 seems get better sound quality (e.g. Miku Custom BGM) - // TODO: Isn't this backwards? Woudln't we want to read as 48kHz and resample to 44.1kHz? - if (ctx->freq == 48000) { - ctx->decoder->SetResampleFrequency(ctx->freq); - } + // This tells us to resample to the same frequency it decodes to. + ctx->decoder->SetResampleFrequency(ctx->freq); return hleDelayResult(hleLogSuccessI(ME, 0), "mp3 init", PARSE_DELAY_MS); } @@ -685,6 +699,7 @@ static u32 sceMp3LowLevelDecode(u32 mp3, u32 sourceAddr, u32 sourceBytesConsumed int outpcmbytes = 0; ctx->decoder->Decode((void*)inbuff, 4096, outbuff, &outpcmbytes); + NotifyMemInfo(MemBlockFlags::WRITE, samplesAddr, outpcmbytes, "Mp3LowLevelDecode"); Memory::Write_U32(ctx->decoder->GetSourcePos(), sourceBytesConsumedAddr); Memory::Write_U32(outpcmbytes, sampleBytesAddr); @@ -707,7 +722,7 @@ const HLEFunction sceMp3[] = { {0X8AB81558, &WrapU_V, "sceMp3StartEntry", 'x', "" }, {0X8F450998, &WrapI_U, "sceMp3GetSamplingRate", 'i', "x" }, {0XA703FE0F, &WrapI_UUUU, "sceMp3GetInfoToAddStreamData", 'i', "xppp" }, - {0XD021C0FB, &WrapI_UU, "sceMp3Decode", 'i', "xx" }, + {0XD021C0FB, &WrapI_UU, "sceMp3Decode", 'i', "xp" }, {0XD0A56296, &WrapI_U, "sceMp3CheckStreamDataNeeded", 'i', "x" }, {0XD8F54A51, &WrapI_U, "sceMp3GetLoopNum", 'i', "x" }, {0XF5478233, &WrapI_U, "sceMp3ReleaseMp3Handle", 'i', "x" }, diff --git a/Core/HLE/sceMpeg.cpp b/Core/HLE/sceMpeg.cpp index 298f2423d9..8d1fbd206d 100644 --- a/Core/HLE/sceMpeg.cpp +++ b/Core/HLE/sceMpeg.cpp @@ -34,6 +34,8 @@ #include "Core/Reporting.h" #include "GPU/GPUInterface.h" #include "GPU/GPUState.h" +#include "Core/HLE/sceKernelMemory.h" +#include "Core/Core.h" // MPEG AVC elementary stream. static const int MPEG_AVC_ES_SIZE = 2048; // MPEG packet size. @@ -391,9 +393,13 @@ void __MpegInit() { actionPostPut = __KernelRegisterActionType(PostPutAction::Create); #ifdef USE_FFMPEG +#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(58, 18, 100) avcodec_register_all(); +#endif +#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(58, 12, 100) av_register_all(); #endif +#endif } void __MpegDoState(PointerWrap &p) { @@ -522,8 +528,8 @@ static u32 sceMpegCreate(u32 mpegAddr, u32 dataPtr, u32 size, u32 ringbufferAddr Memory::Write_U32(mpegHandle, mpegAddr); // Initialize fake mpeg struct. - Memory::Memcpy(mpegHandle, "LIBMPEG\0", 8); - Memory::Memcpy(mpegHandle + 8, "001\0", 4); + Memory::Memcpy(mpegHandle, "LIBMPEG\0", 8, "Mpeg"); + Memory::Memcpy(mpegHandle + 8, "001\0", 4, "Mpeg"); Memory::Write_U32(-1, mpegHandle + 12); if (ringbuffer.IsValid()) { Memory::Write_U32(ringbufferAddr, mpegHandle + 16); @@ -799,6 +805,8 @@ static bool InitPmp(MpegContext * ctx){ return false; } + pmp_CodecCtx->flags |= AV_CODEC_FLAG_OUTPUT_CORRUPT | AV_CODEC_FLAG_LOW_DELAY; + // each pmp video context is corresponding to one pmp video codec mediaengine->m_pCodecCtxs[0] = pmp_CodecCtx; @@ -987,7 +995,22 @@ static bool decodePmpVideo(PSPPointer ringbuffer, u32 pmpctxA // decode video frame +#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 48, 101) + if (packet.size != 0) + avcodec_send_packet(pCodecCtx, &packet); + int len = avcodec_receive_frame(pCodecCtx, pFrame); + if (len == 0) { + len = pFrame->pkt_size; + got_picture = 1; + } else if (len == AVERROR(EAGAIN)) { + len = 0; + got_picture = 0; + } else { + got_picture = 0; + } +#else int len = avcodec_decode_video2(pCodecCtx, pFrame, &got_picture, &packet); +#endif DEBUG_LOG(ME, "got_picture %d", got_picture); if (got_picture){ SwsContext *img_convert_ctx = NULL; @@ -1017,10 +1040,17 @@ static bool decodePmpVideo(PSPPointer ringbuffer, u32 pmpctxA sws_freeContext(img_convert_ctx); // update timestamp - if (av_frame_get_best_effort_timestamp(mediaengine->m_pFrame) != AV_NOPTS_VALUE) - mediaengine->m_videopts = av_frame_get_best_effort_timestamp(mediaengine->m_pFrame) + av_frame_get_pkt_duration(mediaengine->m_pFrame) - mediaengine->m_firstTimeStamp; +#if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(55, 58, 100) + int64_t bestPts = mediaengine->m_pFrame->best_effort_timestamp; + int64_t ptsDuration = mediaengine->m_pFrame->pkt_duration; +#else + int64_t bestPts = av_frame_get_best_effort_timestamp(mediaengine->m_pFrame); + int64_t ptsDuration = av_frame_get_pkt_duration(mediaengine->m_pFrame); +#endif + if (bestPts != AV_NOPTS_VALUE) + mediaengine->m_videopts = bestPts + ptsDuration - mediaengine->m_firstTimeStamp; else - mediaengine->m_videopts += av_frame_get_pkt_duration(mediaengine->m_pFrame); + mediaengine->m_videopts += ptsDuration; // push the decoded frame into pmp_queue pmp_queue.push_back(pFrameRGB); @@ -1584,11 +1614,13 @@ static int sceMpegGetAvcAu(u32 mpeg, u32 streamId, u32 auAddr, u32 attrAddr) ERROR_LOG_REPORT(ME, "sceMpegGetAvcAu(%08x, %08x, %08x, %08x): invalid ringbuffer address", mpeg, streamId, auAddr, attrAddr); return -1; } - - if (ctx->mpegwarmUp < MPEG_WARMUP_FRAMES) { - DEBUG_LOG(ME, "sceMpegGetAvcAu(%08x, %08x, %08x, %08x): warming up", mpeg, streamId, auAddr, attrAddr); - ctx->mpegwarmUp++; - return ERROR_MPEG_NO_DATA; + + if (PSP_CoreParameter().compat.flags().MpegAvcWarmUp) { + if (ctx->mpegwarmUp == 0) { + DEBUG_LOG(ME, "sceMpegGetAvcAu(%08x, %08x, %08x, %08x): warming up", mpeg, streamId, auAddr, attrAddr); + ctx->mpegwarmUp++; + return ERROR_MPEG_NO_DATA; + } } SceMpegAu avcAu; @@ -1686,12 +1718,6 @@ static int sceMpegGetAtracAu(u32 mpeg, u32 streamId, u32 auAddr, u32 attrAddr) return -1; } - if (ctx->mpegwarmUp < MPEG_WARMUP_FRAMES) { - DEBUG_LOG(ME, "sceMpegGetAtracAu(%08x, %08x, %08x, %08x): warming up", mpeg, streamId, auAddr, attrAddr); - ctx->mpegwarmUp++; - return ERROR_MPEG_NO_DATA; - } - SceMpegAu atracAu; atracAu.read(auAddr); @@ -1962,7 +1988,7 @@ static u32 sceMpegAtracDecode(u32 mpeg, u32 auAddr, u32 bufferAddr, int init) // We kept track of the stream number here in sceMpegGetAtracAu(). ctx->mediaengine->setAudioStream(atracAu.esBuffer); - Memory::Memset(bufferAddr, 0, MPEG_ATRAC_ES_OUTPUT_SIZE); + Memory::Memset(bufferAddr, 0, MPEG_ATRAC_ES_OUTPUT_SIZE, "MpegAtracClear"); ctx->mediaengine->getAudioSamples(bufferAddr); atracAu.pts = ctx->mediaengine->getAudioTimeStamp() + ctx->mpegFirstTimestamp; diff --git a/Core/HLE/sceNet.cpp b/Core/HLE/sceNet.cpp index 448b0ba66e..193a3e04c9 100644 --- a/Core/HLE/sceNet.cpp +++ b/Core/HLE/sceNet.cpp @@ -48,6 +48,11 @@ #include "Core/Reporting.h" #include "Core/Instance.h" +#if PPSSPP_PLATFORM(SWITCH) && !defined(INADDR_NONE) +// Missing toolchain define +#define INADDR_NONE 0xFFFFFFFF +#endif + static bool netInited; bool netInetInited; @@ -153,9 +158,6 @@ static void __ApctlState(u64 userdata, int cyclesLate) { int ScheduleApctlState(int event, int newState, int usec, const char* reason) { int uid = event + 1; - if (apctlStateEvent < 0) - apctlStateEvent = CoreTiming::RegisterEvent("__ApctlState", __ApctlState); - u64 param = ((u64)__KernelGetCurThread()) << 32 | uid; CoreTiming::ScheduleEvent(usToCycles(usec), apctlStateEvent, param); __KernelWaitCurThread(WAITTYPE_NET, uid, newState, 0, false, reason); @@ -203,11 +205,12 @@ void __NetInit() { g_adhocServerIP.in.sin_port = htons(SERVER_PORT); //27312 // Maybe read this from config too g_adhocServerIP.in.sin_addr.s_addr = INADDR_NONE; + dummyPeekBuf64k = (char*)malloc(dummyPeekBuf64kSize); InitLocalhostIP(); SceNetEtherAddr mac; getLocalMac(&mac); - NOTICE_LOG(SCENET, "LocalHost IP will be %s [%s]", inet_ntoa(g_localhostIP.in.sin_addr), mac2str(&mac).c_str()); + NOTICE_LOG(SCENET, "LocalHost IP will be %s [%s]", ip2str(g_localhostIP.in.sin_addr).c_str(), mac2str(&mac).c_str()); // TODO: May be we should initialize & cleanup somewhere else than here for PortManager to be used as general purpose for whatever port forwarding PPSSPP needed __UPnPInit(); @@ -235,6 +238,8 @@ void __NetShutdown() { // Since PortManager supposed to be general purpose for whatever port forwarding PPSSPP needed, may be we shouldn't clear & restore ports in here? it will be cleared and restored by PortManager's destructor when exiting PPSSPP anyway __UPnPShutdown(); + + free(dummyPeekBuf64k); } static void __UpdateApctlHandlers(u32 oldState, u32 newState, u32 flag, u32 error) { @@ -304,14 +309,11 @@ void __NetDoState(PointerWrap &p) { } if (s >= 5) { Do(p, apctlStateEvent); - if (apctlStateEvent != -1) { - CoreTiming::RestoreRegisterEvent(apctlStateEvent, "__ApctlState", __ApctlState); - } - } - else { + } else { apctlStateEvent = -1; } - + CoreTiming::RestoreRegisterEvent(apctlStateEvent, "__ApctlState", __ApctlState); + if (p.mode == p.MODE_READ) { // Let's not change "Inited" value when Loading SaveState in the middle of multiplayer to prevent memory & port leaks netApctlInited = cur_netApctlInited; @@ -591,7 +593,7 @@ u32 Net_Term() { } static u32 sceNetTerm() { - WARN_LOG(SCENET, "sceNetTerm()"); + WARN_LOG(SCENET, "sceNetTerm() at %08x", currentMIPS->pc); int retval = Net_Term(); // Give time to make sure everything are cleaned up @@ -681,15 +683,14 @@ static u32 sceWlanGetEtherAddr(u32 addrAddr) { Memory::Memset(addrAddr, PPSSPP_ID, 6); // Making sure the 1st 2-bits on the 1st byte of OUI are zero to prevent issue with some games (ie. Gran Turismo) addr[0] &= 0xfc; - } - else - // Read MAC Address from config - if (!ParseMacAddress(g_Config.sMACAddress.c_str(), addr)) { - ERROR_LOG(SCENET, "Error parsing mac address %s", g_Config.sMACAddress.c_str()); - Memory::Memset(addrAddr, 0, 6); } else { - CBreakPoints::ExecMemCheck(addrAddr, true, 6, currentMIPS->pc); + // Read MAC Address from config + if (!ParseMacAddress(g_Config.sMACAddress.c_str(), addr)) { + ERROR_LOG(SCENET, "Error parsing mac address %s", g_Config.sMACAddress.c_str()); + Memory::Memset(addrAddr, 0, 6); + } } + NotifyMemInfo(MemBlockFlags::WRITE, addrAddr, 6, "WlanEtherAddr"); return hleLogSuccessI(SCENET, hleDelayResult(0, "get ether mac", 200)); } diff --git a/Core/HLE/sceNetAdhoc.cpp b/Core/HLE/sceNetAdhoc.cpp index 0b6e97b472..c224b8f7be 100644 --- a/Core/HLE/sceNetAdhoc.cpp +++ b/Core/HLE/sceNetAdhoc.cpp @@ -16,6 +16,7 @@ // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. #if defined(_WIN32) +#include #include "Common/CommonWindows.h" #endif @@ -109,6 +110,7 @@ int PollAdhocSocket(SceNetAdhocPollSd* sds, int count, int timeout, int nonblock int FlushPtpSocket(int socketId); int NetAdhocGameMode_DeleteMaster(); int NetAdhocctl_ExitGameMode(); +int NetAdhocPtp_Connect(int id, int timeout, int flag, bool allowForcedConnect = true); static int sceNetAdhocPdpSend(int id, const char* mac, u32 port, void* data, int len, int timeout, int flag); static int sceNetAdhocPdpRecv(int id, void* addr, void* port, void* buf, void* dataLength, u32 timeout, int flag); @@ -180,8 +182,13 @@ static void __GameModeNotify(u64 userdata, int cyclesLate) { int recvd = 0; for (auto& gma : replicaGameModeAreas) { // Either replicas new data has been received or that player has been disconnected - if (gma.dataUpdated || gma.updateTimestamp == 0) + if (gma.dataUpdated || gma.updateTimestamp == 0) { recvd++; + // Since we're able to receive data, now we're certain that remote player is listening and ready to receive data, so we send initial data one more time in case they're not listening yet on previous attempt (ie. Pocket Pool) + if (gma.dataUpdated) { + sceNetAdhocPdpSend(gameModeSocket, (const char*)&gma.mac, ADHOC_GAMEMODE_PORT, masterGameModeArea.data, masterGameModeArea.size, 0, ADHOC_F_NONBLOCK); + } + } } // Resume blocked thread u64 now = CoreTiming::GetGlobalTimeUsScaled(); @@ -353,9 +360,6 @@ int WaitBlockingAdhocctlSocket(AdhocctlRequest request, int usec, const char* re return ERROR_NET_ADHOCCTL_BUSY; } - if (adhocctlNotifyEvent < 0) - adhocctlNotifyEvent = CoreTiming::RegisterEvent("__AdhocctlNotify", __AdhocctlNotify); - u64 param = ((u64)__KernelGetCurThread()) << 32 | uid; adhocctlStartTime = (u64)(time_now_d() * 1000000.0); adhocctlRequests[uid] = request; @@ -370,9 +374,6 @@ int WaitBlockingAdhocctlSocket(AdhocctlRequest request, int usec, const char* re int ScheduleAdhocctlState(int event, int newState, int usec, const char* reason) { int uid = event + 1; - if (adhocctlStateEvent < 0) - adhocctlStateEvent = CoreTiming::RegisterEvent("__AdhocctlState", __AdhocctlState); - u64 param = ((u64)__KernelGetCurThread()) << 32 | uid; CoreTiming::ScheduleEvent(usToCycles(usec), adhocctlStateEvent, param); __KernelWaitCurThread(WAITTYPE_NET, uid, newState, 0, false, reason); @@ -384,9 +385,6 @@ int StartGameModeScheduler(int bufSize) { if (gameModeSocket < 0) return -1; - if (gameModeNotifyEvent < 0) - gameModeNotifyEvent = CoreTiming::RegisterEvent("__GameModeNotify", __GameModeNotify); - INFO_LOG(SCENET, "GameMode Scheduler (%d, %d) has started", gameModeSocket, bufSize); u64 param = ((u64)__KernelGetCurThread()) << 32 | bufSize; CoreTiming::ScheduleEvent(usToCycles(GAMEMODE_INIT_DELAY), gameModeNotifyEvent, param); @@ -402,20 +400,25 @@ int DoBlockingPdpRecv(int uid, AdhocSocketRequest& req, s64& result) { return 0; } - sockaddr_in sin; - memset(&sin, 0, sizeof(sin)); + struct sockaddr_in sin; socklen_t sinlen = sizeof(sin); + memset(&sin, 0, sinlen); - int ret = recvfrom(uid, (char*)req.buffer, *req.length, MSG_PEEK | MSG_NOSIGNAL | MSG_TRUNC, (sockaddr*)&sin, &sinlen); + // On Windows: MSG_TRUNC are not supported on recvfrom (socket error WSAEOPNOTSUPP), so we use dummy buffer as an alternative + int ret = recvfrom(uid, dummyPeekBuf64k, dummyPeekBuf64kSize, MSG_PEEK | MSG_NOSIGNAL, (struct sockaddr*)&sin, &sinlen); int sockerr = errno; + if (ret > 0 && *req.length > 0) + memcpy(req.buffer, dummyPeekBuf64k, std::min(ret, *req.length)); // Note: UDP must not be received partially, otherwise leftover data in socket's buffer will be discarded if (ret >= 0 && ret <= *req.length) { - ret = recvfrom(uid, (char*)req.buffer, *req.length, MSG_NOSIGNAL, (sockaddr*)&sin, &sinlen); + sinlen = sizeof(sin); + memset(&sin, 0, sinlen); + ret = recvfrom(uid, (char*)req.buffer, *req.length, MSG_NOSIGNAL, (struct sockaddr*)&sin, &sinlen); // UDP can also receives 0 data, while on TCP receiving 0 data = connection gracefully closed, but not sure whether PDP can send/recv 0 data or not tho *req.length = 0; if (ret >= 0) { - DEBUG_LOG(SCENET, "sceNetAdhocPdpRecv[%i:%u]: Received %u bytes from %s:%u\n", req.id, getLocalPort(uid), ret, inet_ntoa(sin.sin_addr), ntohs(sin.sin_port)); + DEBUG_LOG(SCENET, "sceNetAdhocPdpRecv[%i:%u]: Received %u bytes from %s:%u\n", req.id, getLocalPort(uid), ret, ip2str(sin.sin_addr).c_str(), ntohs(sin.sin_port)); // Peer MAC SceNetEtherAddr mac; @@ -440,7 +443,7 @@ int DoBlockingPdpRecv(int uid, AdhocSocketRequest& req, s64& result) { *req.length = ret; *req.remotePort = ntohs(sin.sin_port) - portOffset; - WARN_LOG(SCENET, "sceNetAdhocPdpRecv[%i:%u]: Received %i bytes from Unknown Peer %s:%u", req.id, getLocalPort(uid), ret, inet_ntoa(sin.sin_addr), ntohs(sin.sin_port)); + WARN_LOG(SCENET, "sceNetAdhocPdpRecv[%i:%u]: Received %i bytes from Unknown Peer %s:%u", req.id, getLocalPort(uid), ret, ip2str(sin.sin_addr).c_str(), ntohs(sin.sin_port)); } } result = 0; @@ -457,7 +460,7 @@ int DoBlockingPdpRecv(int uid, AdhocSocketRequest& req, s64& result) { } // Returning required buffer size when available data in recv buffer is larger than provided buffer size else if (ret > *req.length) { - WARN_LOG(SCENET, "sceNetAdhocPdpRecv[%i:%u]: Peeked %u/%u bytes from %s:%u\n", req.id, getLocalPort(uid), ret, *req.length, inet_ntoa(sin.sin_addr), ntohs(sin.sin_port)); + WARN_LOG(SCENET, "sceNetAdhocPdpRecv[%i:%u]: Peeked %u/%u bytes from %s:%u\n", req.id, getLocalPort(uid), ret, *req.length, ip2str(sin.sin_addr).c_str(), ntohs(sin.sin_port)); *req.length = ret; // Peer MAC @@ -477,6 +480,7 @@ int DoBlockingPdpRecv(int uid, AdhocSocketRequest& req, s64& result) { } result = ERROR_NET_ADHOC_NOT_ENOUGH_SPACE; } + // FIXME: Blocking operation with infinite timeout(0) should never get a TIMEOUT error, right? May be we should return INVALID_ARG instead if it was infinite timeout (0)? else result = ERROR_NET_ADHOC_TIMEOUT; // ERROR_NET_ADHOC_INVALID_ARG; // ERROR_NET_ADHOC_DISCONNECTED @@ -499,16 +503,16 @@ int DoBlockingPdpSend(int uid, AdhocSocketRequest& req, s64& result, AdhocSendTa bool retry = false; for (auto peer = targetPeers.peers.begin(); peer != targetPeers.peers.end(); ) { // Fill in Target Structure - sockaddr_in target; + struct sockaddr_in target; target.sin_family = AF_INET; target.sin_addr.s_addr = peer->ip; target.sin_port = htons(peer->port + ((isOriPort && !isPrivateIP(peer->ip)) ? 0 : portOffset)); - int ret = sendto(pdpsocket.id, (const char*)req.buffer, targetPeers.length, MSG_NOSIGNAL, (sockaddr*)&target, sizeof(target)); + int ret = sendto(pdpsocket.id, (const char*)req.buffer, targetPeers.length, MSG_NOSIGNAL, (struct sockaddr*)&target, sizeof(target)); int sockerr = errno; if (ret >= 0) { - DEBUG_LOG(SCENET, "sceNetAdhocPdpSend[%i:%u](B): Sent %u bytes to %s:%u\n", uid, getLocalPort(pdpsocket.id), ret, inet_ntoa(target.sin_addr), ntohs(target.sin_port)); + DEBUG_LOG(SCENET, "sceNetAdhocPdpSend[%i:%u](B): Sent %u bytes to %s:%u\n", uid, getLocalPort(pdpsocket.id), ret, ip2str(target.sin_addr).c_str(), ntohs(target.sin_port)); // Remove successfully sent to peer to prevent sending the same data again during a retry peer = targetPeers.peers.erase(peer); } @@ -555,10 +559,14 @@ int DoBlockingPtpSend(int uid, AdhocSocketRequest& req, s64& result) { DEBUG_LOG(SCENET, "sceNetAdhocPtpSend[%i:%u]: Sent %u bytes to %s:%u\n", req.id, ptpsocket.lport, ret, mac2str(&ptpsocket.paddr).c_str(), ptpsocket.pport); + // Set to Established on successful Send when an attempt to Connect was initiated + if (ptpsocket.state == ADHOC_PTP_STATE_SYN_SENT) + ptpsocket.state = ADHOC_PTP_STATE_ESTABLISHED; + // Return Success result = 0; } - else if (ret == SOCKET_ERROR && (sockerr == EAGAIN || sockerr == EWOULDBLOCK)) { + else if (ret == SOCKET_ERROR && (sockerr == EAGAIN || sockerr == EWOULDBLOCK || (ptpsocket.state == ADHOC_PTP_STATE_SYN_SENT && (sockerr == ENOTCONN || connectInProgress(sockerr))))) { u64 now = (u64)(time_now_d() * 1000000.0); if (req.timeout == 0 || now - req.startTime <= req.timeout) { return -1; @@ -604,9 +612,13 @@ int DoBlockingPtpRecv(int uid, AdhocSocketRequest& req, s64& result) { if (peer != NULL) peer->last_recv = CoreTiming::GetGlobalTimeUsScaled(); peerlock.unlock(); + // Set to Established on successful Recv when an attempt to Connect was initiated + if (ptpsocket.state == ADHOC_PTP_STATE_SYN_SENT) + ptpsocket.state = ADHOC_PTP_STATE_ESTABLISHED; + result = 0; } - else if (ret == SOCKET_ERROR && (sockerr == EAGAIN || sockerr == EWOULDBLOCK)) { + else if (ret == SOCKET_ERROR && (sockerr == EAGAIN || sockerr == EWOULDBLOCK || (ptpsocket.state == ADHOC_PTP_STATE_SYN_SENT && (sockerr == ENOTCONN || connectInProgress(sockerr))))) { u64 now = (u64)(time_now_d() * 1000000.0); if (req.timeout == 0 || now - req.startTime <= req.timeout) { return -1; @@ -637,7 +649,7 @@ int DoBlockingPtpAccept(int uid, AdhocSocketRequest& req, s64& result) { return 0; } - sockaddr_in sin; + struct sockaddr_in sin; memset(&sin, 0, sizeof(sin)); socklen_t sinlen = sizeof(sin); int ret, sockerr; @@ -646,7 +658,7 @@ int DoBlockingPtpAccept(int uid, AdhocSocketRequest& req, s64& result) { ret = IsSocketReady(uid, true, false, &sockerr); if (ret > 0) { // Accept Connection - ret = accept(uid, (sockaddr*)&sin, &sinlen); + ret = accept(uid, (struct sockaddr*)&sin, &sinlen); sockerr = errno; } @@ -689,15 +701,15 @@ int DoBlockingPtpConnect(int uid, AdhocSocketRequest& req, s64& result) { // Connection is ready if (ret > 0) { - sockaddr_in sin; + struct sockaddr_in sin; memset(&sin, 0, sizeof(sin)); socklen_t sinlen = sizeof(sin); - getpeername(uid, (sockaddr*)&sin, &sinlen); + getpeername(uid, (struct sockaddr*)&sin, &sinlen); // Set Connected State ptpsocket.state = ADHOC_PTP_STATE_ESTABLISHED; - INFO_LOG(SCENET, "sceNetAdhocPtpConnect[%i:%u]: Established (%s:%u)", req.id, ptpsocket.lport, inet_ntoa(sin.sin_addr), ptpsocket.pport); + INFO_LOG(SCENET, "sceNetAdhocPtpConnect[%i:%u]: Established (%s:%u)", req.id, ptpsocket.lport, ip2str(sin.sin_addr).c_str(), ptpsocket.pport); // Success result = 0; @@ -904,9 +916,6 @@ int WaitBlockingAdhocSocket(u64 threadSocketId, int type, int pspSocketId, void* return ERROR_NET_ADHOC_BUSY; // ERROR_NET_ADHOC_TIMEOUT } - if (adhocSocketNotifyEvent < 0) - adhocSocketNotifyEvent = CoreTiming::RegisterEvent("__AdhocSocketNotify", __AdhocSocketNotify); - //changeBlockingMode(socketId, 1); u32 tmout = timeoutUS; @@ -1002,38 +1011,27 @@ void __NetAdhocDoState(PointerWrap &p) { Do(p, adhocConnectionType); Do(p, adhocctlState); Do(p, adhocctlNotifyEvent); - if (adhocctlNotifyEvent != -1) { - CoreTiming::RestoreRegisterEvent(adhocctlNotifyEvent, "__AdhocctlNotify", __AdhocctlNotify); - } Do(p, adhocSocketNotifyEvent); - if (adhocSocketNotifyEvent != -1) { - CoreTiming::RestoreRegisterEvent(adhocSocketNotifyEvent, "__AdhocSocketNotify", __AdhocSocketNotify); - } - } - else { + } else { adhocConnectionType = ADHOC_CONNECT; adhocctlState = ADHOCCTL_STATE_DISCONNECTED; adhocctlNotifyEvent = -1; adhocSocketNotifyEvent = -1; } + CoreTiming::RestoreRegisterEvent(adhocctlNotifyEvent, "__AdhocctlNotify", __AdhocctlNotify); + CoreTiming::RestoreRegisterEvent(adhocSocketNotifyEvent, "__AdhocSocketNotify", __AdhocSocketNotify); if (s >= 6) { Do(p, gameModeNotifyEvent); - if (gameModeNotifyEvent != -1) { - CoreTiming::RestoreRegisterEvent(gameModeNotifyEvent, "__GameModeNotify", __GameModeNotify); - } - } - else { + } else { gameModeNotifyEvent = -1; } + CoreTiming::RestoreRegisterEvent(gameModeNotifyEvent, "__GameModeNotify", __GameModeNotify); if (s >= 7) { Do(p, adhocctlStateEvent); - if (adhocctlStateEvent != -1) { - CoreTiming::RestoreRegisterEvent(adhocctlStateEvent, "__AdhocctlState", __AdhocctlState); - } - } - else { + } else { adhocctlStateEvent = -1; } + CoreTiming::RestoreRegisterEvent(adhocctlStateEvent, "__AdhocctlState", __AdhocctlState); if (s >= 8) { Do(p, isAdhocctlBusy); Do(p, netAdhocGameModeEntered); @@ -1246,23 +1244,29 @@ static int sceNetAdhocPdpCreate(const char *mac, int port, int bufferSize, u32 f setUDPConnReset(usocket, false); // Binding Information for local Port - sockaddr_in addr; + struct sockaddr_in addr; addr.sin_family = AF_INET; addr.sin_addr.s_addr = INADDR_ANY; if (isLocalServer) { getLocalIp(&addr); } - addr.sin_port = htons(port + portOffset); + addr.sin_port = htons(static_cast(port + static_cast(portOffset))); // The port might be under 1024 (ie. GTA:VCS use port 1, Ford Street Racing use port 0 (UNUSED_PORT), etc) and already used by other application/host OS, should we add 1024 to the port whenever it tried to use an already used port? // Bound Socket to local Port - int iResult = bind(usocket, (sockaddr *)&addr, sizeof(addr)); + int iResult = bind(usocket, (struct sockaddr*)&addr, sizeof(addr)); if (iResult == 0) { + // Workaround: Send a dummy 0 size message to AdhocServer IP to make sure the socket actually bound to an address when binded with INADDR_ANY before using getsockname, seems to fix sending from incorrect port issue on MGS:PW on Android + addr.sin_addr.s_addr = g_adhocServerIP.in.sin_addr.s_addr; + addr.sin_port = 0; + sendto(usocket, dummyPeekBuf64k, 0, MSG_NOSIGNAL, (struct sockaddr*)&addr, sizeof(addr)); // Update sport with the port assigned internal->lport = ntohs(local.sin_port) socklen_t len = sizeof(addr); - if (getsockname(usocket, (sockaddr*)&addr, &len) == 0) { + if (getsockname(usocket, (struct sockaddr*)&addr, &len) == 0) { + if (port + static_cast(portOffset) > 65535 || static_cast(ntohs(addr.sin_port)) - static_cast(portOffset) <= 0) + WARN_LOG(SCENET, "sceNetAdhocPdpCreate - Shifting to Negative Port: %d -> %d -> %d", port, port + portOffset, ntohs(addr.sin_port) - portOffset); port = ntohs(addr.sin_port) - portOffset; } @@ -1431,7 +1435,7 @@ static int sceNetAdhocPdpSend(int id, const char *mac, u32 port, void *data, int // Single Target if (!isBroadcastMAC(daddr)) { // Fill in Target Structure - sockaddr_in target; + struct sockaddr_in target; target.sin_family = AF_INET; target.sin_port = htons(dport + portOffset); @@ -1444,7 +1448,7 @@ static int sceNetAdhocPdpSend(int id, const char *mac, u32 port, void *data, int //_acquireNetworkLock(); // Send Data. UDP are guaranteed to be sent as a whole or nothing(failed if len > SO_MAX_MSG_SIZE), and never be partially sent/recv - int sent = sendto(pdpsocket.id, (const char *)data, len, MSG_NOSIGNAL, (sockaddr *)&target, sizeof(target)); + int sent = sendto(pdpsocket.id, (const char *)data, len, MSG_NOSIGNAL, (struct sockaddr*)&target, sizeof(target)); int error = errno; if (sent == SOCKET_ERROR) { @@ -1472,7 +1476,7 @@ static int sceNetAdhocPdpSend(int id, const char *mac, u32 port, void *data, int hleEatMicro(1000); // Can be longer than 1ms tho // Sent Data if (sent >= 0) { - DEBUG_LOG(SCENET, "sceNetAdhocPdpSend[%i:%u]: Sent %u bytes to %s:%u\n", id, getLocalPort(pdpsocket.id), sent, inet_ntoa(target.sin_addr), ntohs(target.sin_port)); + DEBUG_LOG(SCENET, "sceNetAdhocPdpSend[%i:%u]: Sent %u bytes to %s:%u\n", id, getLocalPort(pdpsocket.id), sent, ip2str(target.sin_addr).c_str(), ntohs(target.sin_port)); // Success return 0; // sent; // MotorStorm will try to resend if return value is not 0 @@ -1539,19 +1543,19 @@ static int sceNetAdhocPdpSend(int id, const char *mac, u32 port, void *data, int // Iterate Peers for (auto peer : dest.peers) { // Fill in Target Structure - sockaddr_in target; + struct sockaddr_in target; target.sin_family = AF_INET; target.sin_addr.s_addr = peer.ip; target.sin_port = htons(dport + ((isOriPort && !isPrivateIP(peer.ip)) ? 0 : portOffset)); - int sent = sendto(pdpsocket.id, (const char*)data, len, MSG_NOSIGNAL, (sockaddr*)&target, sizeof(target)); + int sent = sendto(pdpsocket.id, (const char*)data, len, MSG_NOSIGNAL, (struct sockaddr*)&target, sizeof(target)); int error = errno; if (sent == SOCKET_ERROR) { DEBUG_LOG(SCENET, "Socket Error (%i) on sceNetAdhocPdpSend[%i:%u->%u](BC) [size=%i]", error, id, getLocalPort(pdpsocket.id), ntohs(target.sin_port), len); } if (sent >= 0) { - DEBUG_LOG(SCENET, "sceNetAdhocPdpSend[%i:%u](BC): Sent %u bytes to %s:%u\n", id, getLocalPort(pdpsocket.id), sent, inet_ntoa(target.sin_addr), ntohs(target.sin_port)); + DEBUG_LOG(SCENET, "sceNetAdhocPdpSend[%i:%u](BC): Sent %u bytes to %s:%u\n", id, getLocalPort(pdpsocket.id), sent, ip2str(target.sin_addr).c_str(), ntohs(target.sin_port)); } } } @@ -1660,10 +1664,11 @@ static int sceNetAdhocPdpRecv(int id, void *addr, void * port, void *buf, void * } // Sender Address - sockaddr_in sin; + struct sockaddr_in sin; // Set Address Length (so we get the sender ip) socklen_t sinlen = sizeof(sin); + memset(&sin, 0, sinlen); //sin.sin_len = (uint8_t)sinlen; // Acquire Network Lock @@ -1675,9 +1680,13 @@ static int sceNetAdhocPdpRecv(int id, void *addr, void * port, void *buf, void * // Receive Data. PDP always sent in full size or nothing(failed), recvfrom will always receive in full size as requested (blocking) or failed (non-blocking). If available UDP data is larger than buffer, excess data is lost. // Should peek first for the available data size if it's more than len return ERROR_NET_ADHOC_NOT_ENOUGH_SPACE along with required size in len to prevent losing excess data - received = recvfrom(pdpsocket.id, (char*)buf, *len, MSG_PEEK | MSG_NOSIGNAL | MSG_TRUNC, (sockaddr*)&sin, &sinlen); + // On Windows: MSG_TRUNC are not supported on recvfrom (socket error WSAEOPNOTSUPP), so we use dummy buffer as an alternative + received = recvfrom(pdpsocket.id, dummyPeekBuf64k, dummyPeekBuf64kSize, MSG_PEEK | MSG_NOSIGNAL, (struct sockaddr*)&sin, &sinlen); + if (received > 0 && *len > 0) + memcpy(buf, dummyPeekBuf64k, std::min(received, *len)); + if (received != SOCKET_ERROR && *len < received) { - WARN_LOG(SCENET, "sceNetAdhocPdpRecv[%i:%u]: Peeked %u/%u bytes from %s:%u\n", id, getLocalPort(pdpsocket.id), received, *len, inet_ntoa(sin.sin_addr), ntohs(sin.sin_port)); + WARN_LOG(SCENET, "sceNetAdhocPdpRecv[%i:%u]: Peeked %u/%u bytes from %s:%u\n", id, getLocalPort(pdpsocket.id), received, *len, ip2str(sin.sin_addr).c_str(), ntohs(sin.sin_port)); *len = received; // Peer MAC @@ -1701,7 +1710,9 @@ static int sceNetAdhocPdpRecv(int id, void *addr, void * port, void *buf, void * return hleLogSuccessVerboseI(SCENET, ERROR_NET_ADHOC_NOT_ENOUGH_SPACE, "not enough space"); //received; } - received = recvfrom(pdpsocket.id, (char*)buf, *len, MSG_NOSIGNAL, (sockaddr*)&sin, &sinlen); + sinlen = sizeof(sin); + memset(&sin, 0, sinlen); + received = recvfrom(pdpsocket.id, (char*)buf, *len, MSG_NOSIGNAL, (struct sockaddr*)&sin, &sinlen); error = errno; // On Windows: recvfrom on UDP can get error WSAECONNRESET when previous sendto's destination is unreachable (or destination port is not bound), may need to disable SIO_UDP_CONNRESET @@ -1718,7 +1729,7 @@ static int sceNetAdhocPdpRecv(int id, void *addr, void * port, void *buf, void * hleEatMicro(1000); // Received Data. UDP can also receives 0 data, while on TCP 0 data = connection gracefully closed, but not sure about PDP tho if (received >= 0) { - DEBUG_LOG(SCENET, "sceNetAdhocPdpRecv[%i:%u]: Received %u bytes from %s:%u\n", id, getLocalPort(pdpsocket.id), received, inet_ntoa(sin.sin_addr), ntohs(sin.sin_port)); + DEBUG_LOG(SCENET, "sceNetAdhocPdpRecv[%i:%u]: Received %u bytes from %s:%u\n", id, getLocalPort(pdpsocket.id), received, ip2str(sin.sin_addr).c_str(), ntohs(sin.sin_port)); // Peer MAC SceNetEtherAddr mac; @@ -1747,7 +1758,7 @@ static int sceNetAdhocPdpRecv(int id, void *addr, void * port, void *buf, void * // Unknown Peer, let's not give any data else { //*len = 0; // received; // Is this going to be okay since saddr may not be valid? - WARN_LOG(SCENET, "sceNetAdhocPdpRecv[%i:%u]: Received %i bytes from Unknown Peer %s:%u", id, getLocalPort(pdpsocket.id), received, inet_ntoa(sin.sin_addr), ntohs(sin.sin_port)); + WARN_LOG(SCENET, "sceNetAdhocPdpRecv[%i:%u]: Received %i bytes from Unknown Peer %s:%u", id, getLocalPort(pdpsocket.id), received, ip2str(sin.sin_addr).c_str(), ntohs(sin.sin_port)); } // Free Network Lock //_freeNetworkLock(); @@ -1794,14 +1805,16 @@ int NetAdhoc_SetSocketAlert(int id, s32_le flag) { adhocSockets[id - 1]->flags = flg; adhocSockets[id - 1]->alerted_flags = 0; - return hleDelayResult(0, "set socket alert delay", 1000); + return 0; } // Flags seems to be bitmasks of ADHOC_F_ALERT... (need more games to test this) int sceNetAdhocSetSocketAlert(int id, int flag) { WARN_LOG_REPORT_ONCE(sceNetAdhocSetSocketAlert, SCENET, "UNTESTED sceNetAdhocSetSocketAlert(%d, %08x) at %08x", id, flag, currentMIPS->pc); - return hleLogDebug(SCENET, NetAdhoc_SetSocketAlert(id, flag), ""); + int retval = NetAdhoc_SetSocketAlert(id, flag); + hleDelayResult(retval, "set socket alert delay", 1000); + return hleLogDebug(SCENET, retval, ""); } int PollAdhocSocket(SceNetAdhocPollSd* sds, int count, int timeout, int nonblock) { @@ -2347,7 +2360,7 @@ u32 NetAdhocctl_Disconnect() { } // Return Success, some games might ignore returned value and always treat it as success, otherwise repeatedly calling this function - return hleDelayResult(0, "disconnect delay", us); + return 0; } // Library uninitialized @@ -2370,7 +2383,7 @@ static u32 sceNetAdhocctlDelHandler(u32 handlerID) { if (adhocctlHandlers.find(handlerID) != adhocctlHandlers.end()) { adhocctlHandlers.erase(handlerID); - INFO_LOG(SCENET, "sceNetAdhocctlDelHandler(%d)", handlerID); + INFO_LOG(SCENET, "sceNetAdhocctlDelHandler(%d) at %08x", handlerID, currentMIPS->pc); } else { WARN_LOG(SCENET, "sceNetAdhocctlDelHandler(%d): Invalid Handler ID", handlerID); } @@ -2381,6 +2394,7 @@ static u32 sceNetAdhocctlDelHandler(u32 handlerID) { int NetAdhocctl_Term() { if (netAdhocctlInited) { if (adhocctlState != ADHOCCTL_STATE_DISCONNECTED) { + // Note: This might block current thread if the first attempt to send OPCODE_DISCONNECT to AdhocServer failed with EAGAIN error if (netAdhocGameModeEntered) NetAdhocctl_ExitGameMode(); else @@ -2393,6 +2407,8 @@ int NetAdhocctl_Term() { friendFinderThread.join(); } + // TODO: May need to block current thread to make sure all Adhocctl callbacks have been fully executed before terminating Adhoc PSPThread (ie. threadAdhocID). + // Clear GameMode resources NetAdhocGameMode_DeleteMaster(); deleteAllGMB(); @@ -2427,7 +2443,7 @@ int NetAdhocctl_Term() { int sceNetAdhocctlTerm() { // WLAN might be disabled in the middle of successfull multiplayer, but we still need to cleanup right? - INFO_LOG(SCENET, "sceNetAdhocctlTerm()"); + INFO_LOG(SCENET, "sceNetAdhocctlTerm() at %08x", currentMIPS->pc); //if (netAdhocMatchingInited) NetAdhocMatching_Term(); int retval = NetAdhocctl_Term(); @@ -2645,7 +2661,7 @@ int NetAdhocctl_Create(const char* groupName) { hleEatMicro(us); // Return Success // FIXME: When tested using JPCSP + official prx files it seems sceNetAdhocctlCreate switching to a different thread for at least 100ms after returning success and before executing the next line. - return hleDelayResult(0, "create/connect/join delay", adhocEventPollDelay); + return 0; } // Connected State @@ -2928,8 +2944,10 @@ static int sceNetAdhocGetPdpStat(u32 structSize, u32 structAddr) { // Valid Socket Entry auto sock = adhocSockets[j]; if (sock != NULL && sock->type == SOCK_PDP) { - // Set available bytes to be received. With FIOREAD There might be ghosting 1 byte in recv buffer when remote peer's socket got closed (ie. Warriors Orochi 2) Attempting to recv this ghost 1 byte will result to socket error 10054 (may need to disable SIO_UDP_CONNRESET error) - sock->data.pdp.rcv_sb_cc = getAvailToRecv(sock->data.pdp.id); + // Set available bytes to be received. With FIONREAD There might be ghosting 1 byte in recv buffer when remote peer's socket got closed (ie. Warriors Orochi 2) Attempting to recv this ghost 1 byte will result to socket error 10054 (may need to disable SIO_UDP_CONNRESET error) + // It seems real PSP respecting the socket buffer size arg, so we may need to cap the value up to the buffer size arg since we use larger buffer, for PDP/UDP the total size must not contains partial/truncated message to avoid data loss. + // TODO: We may need to manage PDP messages ourself by reading each msg 1-by-1 and moving it to our internal buffer(msg array) in order to calculate the correct messages size that can fit into buffer size when there are more than 1 messages in the recv buffer (simulate FIONREAD) + sock->data.pdp.rcv_sb_cc = getAvailToRecv(sock->data.pdp.id, sock->buffer_size); // Copy Socket Data from Internal Memory memcpy(&buf[i], &sock->data.pdp, sizeof(SceNetAdhocPdpStat)); @@ -3022,6 +3040,8 @@ static int sceNetAdhocGetPtpStat(u32 structSize, u32 structAddr) { // Set available bytes to be received sock->data.ptp.rcv_sb_cc = getAvailToRecv(sock->data.ptp.id); + // It seems real PSP respecting the socket buffer size arg, so we may need to cap the value to the buffer size arg since we use larger buffer + sock->data.ptp.rcv_sb_cc = std::min(sock->data.ptp.rcv_sb_cc, (u32_le)sock->buffer_size); // Copy Socket Data from internal Memory memcpy(&buf[i], &sock->data.ptp, sizeof(SceNetAdhocPtpStat)); @@ -3089,7 +3109,7 @@ static int sceNetAdhocPtpOpen(const char *srcmac, int sport, const char *dstmac, // Valid Addresses. FIXME: MAC only valid after successful attempt to Create/Connect/Join a Group? (ie. adhocctlCurrentMode != ADHOCCTL_MODE_NONE) if ((adhocctlCurrentMode != ADHOCCTL_MODE_NONE) && saddr != NULL && isLocalMAC(saddr) && daddr != NULL && !isBroadcastMAC(daddr) && !isZeroMAC(daddr)) { // Dissidia 012 will try to reOpen the port without Closing the old one first when PtpConnect failed to try again. - if (isPTPPortInUse(sport, false)) { + if (isPTPPortInUse(sport, false, daddr, dport)) { // FIXME: When PORT_IN_USE error occured it seems the index to the socket id also increased, which means it tries to create & bind the socket first and then closes it due to failed to bind return hleLogDebug(SCENET, ERROR_NET_ADHOC_PORT_IN_USE, "port in use"); } @@ -3128,24 +3148,25 @@ static int sceNetAdhocPtpOpen(const char *srcmac, int sport, const char *dstmac, setSockTimeout(tcpsocket, SO_SNDTIMEO, rexmt_int); // Disable Nagle Algo to send immediately. Or may be we shouldn't disable Nagle since there is PtpFlush function? - if (g_Config.bTCPNoDelay) - setSockNoDelay(tcpsocket, 1); + setSockNoDelay(tcpsocket, 1); // Binding Information for local Port - sockaddr_in addr; + struct sockaddr_in addr; // addr.sin_len = sizeof(addr); addr.sin_family = AF_INET; addr.sin_addr.s_addr = INADDR_ANY; if (isLocalServer) { getLocalIp(&addr); } - addr.sin_port = htons(sport + portOffset); + addr.sin_port = htons(static_cast(sport + static_cast(portOffset))); // Bound Socket to local Port - if (bind(tcpsocket, (sockaddr*)&addr, sizeof(addr)) == 0) { + if (bind(tcpsocket, (struct sockaddr*)&addr, sizeof(addr)) == 0) { // Update sport with the port assigned internal->lport = ntohs(local.sin_port) socklen_t len = sizeof(addr); - if (getsockname(tcpsocket, (sockaddr*)&addr, &len) == 0) { + if (getsockname(tcpsocket, (struct sockaddr*)&addr, &len) == 0) { + if (sport + static_cast(portOffset) > 65535 || static_cast(ntohs(addr.sin_port)) - static_cast(portOffset) <= 0) + WARN_LOG(SCENET, "sceNetAdhocPtpOpen - Shifting to Negative Port: %d -> %d -> %d", sport, sport + portOffset, ntohs(addr.sin_port) - portOffset); sport = ntohs(addr.sin_port) - portOffset; } @@ -3191,6 +3212,13 @@ static int sceNetAdhocPtpOpen(const char *srcmac, int sport, const char *dstmac, // Switch to non-blocking for futher usage changeBlockingMode(tcpsocket, 1); + // Initiate PtpConnect (ie. The Warrior seems to try to PtpSend right after PtpOpen without trying to PtpConnect first) + NetAdhocPtp_Connect(i + 1, rexmt_int, 1, false); + + // Workaround to give some time to get connected before returning from PtpOpen over high latency + if (g_Config.bForcedFirstConnect && internal->attemptCount == 1) + hleDelayResult(i + 1, "delayed ptpopen", rexmt_int); + // Return PTP Socket Pointer return hleLogDebug(SCENET, i + 1, "success"); } @@ -3238,16 +3266,15 @@ int AcceptPtpSocket(int ptpId, int newsocket, sockaddr_in& peeraddr, SceNetEther setSockReuseAddrPort(newsocket); // Disable Nagle Algo to send immediately. Or may be we shouldn't disable Nagle since there is PtpFlush function? - if (g_Config.bTCPNoDelay) - setSockNoDelay(newsocket, 1); + setSockNoDelay(newsocket, 1); // Local Address Information - sockaddr_in local; + struct sockaddr_in local; memset(&local, 0, sizeof(local)); socklen_t locallen = sizeof(local); // Grab Local Address - if (getsockname(newsocket, (sockaddr*)&local, &locallen) == 0) { + if (getsockname(newsocket, (struct sockaddr*)&local, &locallen) == 0) { // Peer MAC SceNetEtherAddr mac; @@ -3314,7 +3341,7 @@ int AcceptPtpSocket(int ptpId, int newsocket, sockaddr_in& peeraddr, SceNetEther // Switch to non-blocking for futher usage changeBlockingMode(newsocket, 1); - INFO_LOG(SCENET, "sceNetAdhocPtpAccept[%i->%i:%u]: Established (%s:%u) - state: %d", ptpId, i + 1, internal->data.ptp.lport, inet_ntoa(peeraddr.sin_addr), internal->data.ptp.pport, internal->data.ptp.state); + INFO_LOG(SCENET, "sceNetAdhocPtpAccept[%i->%i:%u]: Established (%s:%u) - state: %d", ptpId, i + 1, internal->data.ptp.lport, ip2str(peeraddr.sin_addr).c_str(), internal->data.ptp.pport, internal->data.ptp.state); // Return Socket return i + 1; @@ -3382,7 +3409,7 @@ static int sceNetAdhocPtpAccept(int id, u32 peerMacAddrPtr, u32 peerPortPtr, int if (ptpsocket.state == ADHOC_PTP_STATE_LISTEN) { hleEatMicro(500); // Address Information - sockaddr_in peeraddr; + struct sockaddr_in peeraddr; memset(&peeraddr, 0, sizeof(peeraddr)); socklen_t peeraddrlen = sizeof(peeraddr); int error; @@ -3391,7 +3418,7 @@ static int sceNetAdhocPtpAccept(int id, u32 peerMacAddrPtr, u32 peerPortPtr, int int newsocket = IsSocketReady(ptpsocket.id, true, false, &error); if (newsocket > 0) { // Accept Connection - newsocket = accept(ptpsocket.id, (sockaddr*)&peeraddr, &peeraddrlen); + newsocket = accept(ptpsocket.id, (struct sockaddr*)&peeraddr, &peeraddrlen); error = errno; } @@ -3438,19 +3465,7 @@ static int sceNetAdhocPtpAccept(int id, u32 peerMacAddrPtr, u32 peerPortPtr, int return hleLogSuccessVerboseI(SCENET, ERROR_NET_ADHOC_NOT_INITIALIZED, "not initialized"); } -/** - * Adhoc Emulator PTP Connection Opener - * @param id Socket File Descriptor - * @param timeout Connect Timeout (in Microseconds) - * @param flag Nonblocking Flag - * @return 0 on success or... ADHOC_NOT_INITIALIZED, ADHOC_INVALID_ARG, ADHOC_INVALID_SOCKET_ID, ADHOC_SOCKET_DELETED, ADHOC_CONNECTION_REFUSED, ADHOC_SOCKET_ALERTED, ADHOC_WOULD_BLOCK, ADHOC_TIMEOUT, ADHOC_NOT_OPENED, ADHOC_THREAD_ABORTED, NET_INTERNAL - */ -static int sceNetAdhocPtpConnect(int id, int timeout, int flag) { - INFO_LOG(SCENET, "sceNetAdhocPtpConnect(%i, %i, %i) at %08x", id, timeout, flag, currentMIPS->pc); - if (!g_Config.bEnableWlan) { - return -1; - } - +int NetAdhocPtp_Connect(int id, int timeout, int flag, bool allowForcedConnect) { // Library is initialized if (netAdhocInited) { @@ -3475,31 +3490,31 @@ static int sceNetAdhocPtpConnect(int id, int timeout, int flag) { if (ptpsocket.state == ADHOC_PTP_STATE_CLOSED || ptpsocket.state == ADHOC_PTP_STATE_SYN_SENT) { hleEatMicro(500); // Target Address - sockaddr_in sin; + struct sockaddr_in sin; memset(&sin, 0, sizeof(sin)); - + // Setup Target Address // sin.sin_len = sizeof(sin); sin.sin_family = AF_INET; sin.sin_port = htons(ptpsocket.pport + portOffset); - + // Grab Peer IP - if (resolveMAC(&ptpsocket.paddr, (uint32_t *)&sin.sin_addr.s_addr)) { + if (resolveMAC(&ptpsocket.paddr, (uint32_t*)&sin.sin_addr.s_addr)) { // Some games (ie. PSP2) might try to talk to it's self, not sure if they talked through WAN or LAN when using public Adhoc Server tho sin.sin_port = htons(ptpsocket.pport + ((isOriPort && !isPrivateIP(sin.sin_addr.s_addr)) ? 0 : portOffset)); // Connect Socket to Peer // NOTE: Based on what i read at stackoverflow, The First Non-blocking POSIX connect will always returns EAGAIN/EWOULDBLOCK because it returns without waiting for ACK/handshake, But GvG Next Plus is treating non-blocking PtpConnect just like blocking connect, May be on a real PSP the first non-blocking sceNetAdhocPtpConnect can be successfull? - int connectresult = connect(ptpsocket.id, (sockaddr *)&sin, sizeof(sin)); - + int connectresult = connect(ptpsocket.id, (struct sockaddr*)&sin, sizeof(sin)); + // Grab Error Code int errorcode = errno; if (connectresult == SOCKET_ERROR) { if (errorcode == EAGAIN || errorcode == EWOULDBLOCK || errorcode == EALREADY || errorcode == EISCONN) - DEBUG_LOG(SCENET, "sceNetAdhocPtpConnect[%i]: Socket Error (%i) to %s:%u", id, errorcode, inet_ntoa(sin.sin_addr), ptpsocket.pport); + DEBUG_LOG(SCENET, "sceNetAdhocPtpConnect[%i]: Socket Error (%i) to %s:%u", id, errorcode, ip2str(sin.sin_addr).c_str(), ptpsocket.pport); else - ERROR_LOG(SCENET, "sceNetAdhocPtpConnect[%i]: Socket Error (%i) to %s:%u", id, errorcode, inet_ntoa(sin.sin_addr), ptpsocket.pport); + ERROR_LOG(SCENET, "sceNetAdhocPtpConnect[%i]: Socket Error (%i) to %s:%u", id, errorcode, ip2str(sin.sin_addr).c_str(), ptpsocket.pport); } // Instant Connection (Lucky!) @@ -3508,12 +3523,12 @@ static int sceNetAdhocPtpConnect(int id, int timeout, int flag) { socket->lastAttempt = CoreTiming::GetGlobalTimeUsScaled(); // Set Connected State ptpsocket.state = ADHOC_PTP_STATE_ESTABLISHED; - - INFO_LOG(SCENET, "sceNetAdhocPtpConnect[%i:%u]: Already Connected to %s:%u", id, ptpsocket.lport, inet_ntoa(sin.sin_addr), ptpsocket.pport); + + INFO_LOG(SCENET, "sceNetAdhocPtpConnect[%i:%u]: Already Connected to %s:%u", id, ptpsocket.lport, ip2str(sin.sin_addr).c_str(), ptpsocket.pport); // Success return 0; } - + // Error handling else if (connectresult == SOCKET_ERROR) { // Connection in Progress @@ -3523,7 +3538,7 @@ static int sceNetAdhocPtpConnect(int id, int timeout, int flag) { socket->lastAttempt = CoreTiming::GetGlobalTimeUsScaled(); // Blocking Mode // Workaround: Forcing first attempt to be blocking to prevent issue related to lobby or high latency networks. (can be useful for GvG Next Plus, Dissidia 012, and Fate Unlimited Codes) - if (!flag || (g_Config.bForcedFirstConnect && socket->attemptCount == 1)) { + if (!flag || (allowForcedConnect && g_Config.bForcedFirstConnect && socket->attemptCount == 1)) { // Simulate blocking behaviour with non-blocking socket u64 threadSocketId = ((u64)__KernelGetCurThread()) << 32 | ptpsocket.id; return WaitBlockingAdhocSocket(threadSocketId, PTP_CONNECT, id, nullptr, nullptr, (flag) ? std::max((int)socket->retry_interval, timeout) : timeout, nullptr, nullptr, "ptp connect"); @@ -3539,23 +3554,39 @@ static int sceNetAdhocPtpConnect(int id, int timeout, int flag) { } } } - + // Peer not found return hleLogDebug(SCENET, ERROR_NET_ADHOC_INVALID_ADDR, "invalid address"); // ERROR_NET_ADHOC_WOULD_BLOCK / ERROR_NET_ADHOC_TIMEOUT } - + // Not a valid Client Socket return hleLogDebug(SCENET, ERROR_NET_ADHOC_NOT_OPENED, "not opened"); } - + // Invalid Socket return hleLogDebug(SCENET, ERROR_NET_ADHOC_INVALID_SOCKET_ID, "invalid socket id"); } - + // Library is uninitialized return hleLogDebug(SCENET, ERROR_NET_ADHOC_NOT_INITIALIZED, "not initialized"); } +/** + * Adhoc Emulator PTP Connection Opener + * @param id Socket File Descriptor + * @param timeout Connect Timeout (in Microseconds) + * @param flag Nonblocking Flag + * @return 0 on success or... ADHOC_NOT_INITIALIZED, ADHOC_INVALID_ARG, ADHOC_INVALID_SOCKET_ID, ADHOC_SOCKET_DELETED, ADHOC_CONNECTION_REFUSED, ADHOC_SOCKET_ALERTED, ADHOC_WOULD_BLOCK, ADHOC_TIMEOUT, ADHOC_NOT_OPENED, ADHOC_THREAD_ABORTED, NET_INTERNAL + */ +static int sceNetAdhocPtpConnect(int id, int timeout, int flag) { + INFO_LOG(SCENET, "sceNetAdhocPtpConnect(%i, %i, %i) at %08x", id, timeout, flag, currentMIPS->pc); + if (!g_Config.bEnableWlan) { + return -1; + } + + return NetAdhocPtp_Connect(id, timeout, flag); +} + int NetAdhocPtp_Close(int id, int unknown) { // Library is initialized if (netAdhocInited) { @@ -3680,24 +3711,25 @@ static int sceNetAdhocPtpListen(const char *srcmac, int sport, int bufsize, int setSockTimeout(tcpsocket, SO_RCVTIMEO, rexmt_int); // Disable Nagle Algo to send immediately. Or may be we shouldn't disable Nagle since there is PtpFlush function? - if (g_Config.bTCPNoDelay) - setSockNoDelay(tcpsocket, 1); + setSockNoDelay(tcpsocket, 1); // Binding Information for local Port - sockaddr_in addr; + struct sockaddr_in addr; addr.sin_family = AF_INET; addr.sin_addr.s_addr = INADDR_ANY; if (isLocalServer) { getLocalIp(&addr); } - addr.sin_port = htons(sport + portOffset); + addr.sin_port = htons(static_cast(sport + static_cast(portOffset))); int iResult = 0; // Bound Socket to local Port - if ((iResult = bind(tcpsocket, (sockaddr*)&addr, sizeof(addr))) == 0) { + if ((iResult = bind(tcpsocket, (struct sockaddr*)&addr, sizeof(addr))) == 0) { // Update sport with the port assigned internal->lport = ntohs(local.sin_port) socklen_t len = sizeof(addr); - if (getsockname(tcpsocket, (sockaddr*)&addr, &len) == 0) { + if (getsockname(tcpsocket, (struct sockaddr*)&addr, &len) == 0) { + if (sport + static_cast(portOffset) > 65535 || static_cast(ntohs(addr.sin_port)) - static_cast(portOffset) <= 0) + WARN_LOG(SCENET, "sceNetAdhocPtpListen - Shifting to Negative Port: %d -> %d -> %d", sport, sport + portOffset, ntohs(addr.sin_port) - portOffset); sport = ntohs(addr.sin_port) - portOffset; } // Switch into Listening Mode @@ -3810,7 +3842,7 @@ static int sceNetAdhocPtpSend(int id, u32 dataAddr, u32 dataSizeAddr, int timeou socket->nonblocking = flag; // Connected Socket - if (ptpsocket.state == ADHOC_PTP_STATE_ESTABLISHED) { + if (ptpsocket.state == ADHOC_PTP_STATE_ESTABLISHED || ptpsocket.state == ADHOC_PTP_STATE_SYN_SENT) { // Valid Arguments if (data != NULL && len != NULL && *len > 0) { // Schedule Timeout Removal @@ -3844,12 +3876,16 @@ static int sceNetAdhocPtpSend(int id, u32 dataAddr, u32 dataSizeAddr, int timeou DEBUG_LOG(SCENET, "sceNetAdhocPtpSend[%i:%u]: Sent %u bytes to %s:%u\n", id, ptpsocket.lport, sent, mac2str(&ptpsocket.paddr).c_str(), ptpsocket.pport); + // Set to Established on successful Send when an attempt to Connect was initiated + if (ptpsocket.state == ADHOC_PTP_STATE_SYN_SENT) + ptpsocket.state = ADHOC_PTP_STATE_ESTABLISHED; + // Return Success return 0; } // Non-Critical Error - else if (sent == SOCKET_ERROR && (error == EAGAIN || error == EWOULDBLOCK)) { + else if (sent == SOCKET_ERROR && (error == EAGAIN || error == EWOULDBLOCK || (ptpsocket.state == ADHOC_PTP_STATE_SYN_SENT && (error == ENOTCONN || connectInProgress(error))))) { // Non-Blocking if (flag) return hleLogSuccessVerboseI(SCENET, ERROR_NET_ADHOC_WOULD_BLOCK, "would block"); @@ -3864,16 +3900,16 @@ static int sceNetAdhocPtpSend(int id, u32 dataAddr, u32 dataSizeAddr, int timeou // Change Socket State ptpsocket.state = ADHOC_PTP_STATE_CLOSED; - // Disconnected - return hleLogError(SCENET, ERROR_NET_ADHOC_DISCONNECTED, "disconnected"); + // Disconnected or Not connected? + return hleLogError(SCENET, ERROR_NET_ADHOC_DISCONNECTED, "disconnected?"); } // Invalid Arguments return hleLogError(SCENET, ERROR_NET_ADHOC_INVALID_ARG, "invalid arg"); } - // Not connected - return hleLogError(SCENET, ERROR_NET_ADHOC_NOT_CONNECTED, "not connected"); + // Disconnected + return hleLogError(SCENET, ERROR_NET_ADHOC_DISCONNECTED, "disconnected"); } // Invalid Socket @@ -3909,7 +3945,7 @@ static int sceNetAdhocPtpRecv(int id, u32 dataAddr, u32 dataSizeAddr, int timeou auto& ptpsocket = socket->data.ptp; socket->nonblocking = flag; - if (ptpsocket.state == ADHOC_PTP_STATE_ESTABLISHED) { + if (ptpsocket.state == ADHOC_PTP_STATE_ESTABLISHED || ptpsocket.state == ADHOC_PTP_STATE_SYN_SENT) { // Schedule Timeout Removal //if (flag) timeout = 0; @@ -3934,7 +3970,7 @@ static int sceNetAdhocPtpRecv(int id, u32 dataAddr, u32 dataSizeAddr, int timeou received = recv(ptpsocket.id, (char*)buf, *len, MSG_NOSIGNAL); error = errno; - if (received == SOCKET_ERROR && (error == EAGAIN || error == EWOULDBLOCK)) { + if (received == SOCKET_ERROR && (error == EAGAIN || error == EWOULDBLOCK || (ptpsocket.state == ADHOC_PTP_STATE_SYN_SENT && (error == ENOTCONN || connectInProgress(error))))) { if (flag == 0) { // Simulate blocking behaviour with non-blocking socket u64 threadSocketId = ((u64)__KernelGetCurThread()) << 32 | ptpsocket.id; @@ -3962,6 +3998,10 @@ static int sceNetAdhocPtpRecv(int id, u32 dataAddr, u32 dataSizeAddr, int timeou DEBUG_LOG(SCENET, "sceNetAdhocPtpRecv[%i:%u]: Received %u bytes from %s:%u\n", id, ptpsocket.lport, received, mac2str(&ptpsocket.paddr).c_str(), ptpsocket.pport); + // Set to Established on successful Recv when an attempt to Connect was initiated + if (ptpsocket.state == ADHOC_PTP_STATE_SYN_SENT) + ptpsocket.state = ADHOC_PTP_STATE_ESTABLISHED; + // Return Success return 0; } @@ -3974,11 +4014,11 @@ static int sceNetAdhocPtpRecv(int id, u32 dataAddr, u32 dataSizeAddr, int timeou // Change Socket State ptpsocket.state = ADHOC_PTP_STATE_CLOSED; - // Disconnected - return hleLogError(SCENET, ERROR_NET_ADHOC_DISCONNECTED, "disconnected"); + // Disconnected or Not connected? + return hleLogError(SCENET, ERROR_NET_ADHOC_DISCONNECTED, "disconnected?"); } - return hleLogError(SCENET, ERROR_NET_ADHOC_NOT_CONNECTED, "not connected"); + return hleLogError(SCENET, ERROR_NET_ADHOC_DISCONNECTED, "disconnected"); } // Invalid Socket @@ -4627,8 +4667,10 @@ int NetAdhocMatching_Start(int matchingId, int evthPri, int evthPartitionId, int // Create PDP Socket int sock = sceNetAdhocPdpCreate((const char*)&item->mac, static_cast(item->port), item->rxbuflen, 0); item->socket = sock; - if (sock < 1) + if (sock < 1) { + peerlock.unlock(); return hleLogError(SCENET, ERROR_NET_ADHOC_MATCHING_PORT_IN_USE, "adhoc matching port in use"); + } // Create & Start the Fake PSP Thread ("matching_ev%d" and "matching_io%d") netAdhocValidateLoopMemory(); @@ -4658,8 +4700,7 @@ int NetAdhocMatching_Start(int matchingId, int evthPri, int evthPartitionId, int // Multithreading Unlock peerlock.unlock(); - // Give a little time to make sure matching Threads are ready before the game use the next sceNet functions, should've checked for status instead of guessing the time? - return hleDelayResult(0, "give some time", adhocMatchingEventDelay); + return 0; } #define KERNEL_PARTITION_ID 1 @@ -4671,7 +4712,9 @@ static int sceNetAdhocMatchingStart(int matchingId, int evthPri, int evthStack, if (!g_Config.bEnableWlan) return -1; - return NetAdhocMatching_Start(matchingId, evthPri, USER_PARTITION_ID, evthStack, inthPri, USER_PARTITION_ID, inthStack, optLen, optDataAddr); + int retval = NetAdhocMatching_Start(matchingId, evthPri, USER_PARTITION_ID, evthStack, inthPri, USER_PARTITION_ID, inthStack, optLen, optDataAddr); + // Give a little time to make sure matching Threads are ready before the game use the next sceNet functions, should've checked for status instead of guessing the time? + return hleDelayResult(retval, "give some time", adhocMatchingEventDelay); } // With params for Partition ID for the event & input handler stack @@ -4680,7 +4723,9 @@ static int sceNetAdhocMatchingStart2(int matchingId, int evthPri, int evthPartit if (!g_Config.bEnableWlan) return -1; - return NetAdhocMatching_Start(matchingId, evthPri, evthPartitionId, evthStack, inthPri, inthPartitionId, inthStack, optLen, optDataAddr); + int retval = NetAdhocMatching_Start(matchingId, evthPri, evthPartitionId, evthStack, inthPri, inthPartitionId, inthStack, optLen, optDataAddr); + // Give a little time to make sure matching Threads are ready before the game use the next sceNet functions, should've checked for status instead of guessing the time? + return hleDelayResult(retval, "give some time", adhocMatchingEventDelay); } @@ -5503,6 +5548,7 @@ void __NetTriggerCallbacks() void __NetMatchingCallbacks() //(int matchingId) { std::lock_guard adhocGuard(adhocEvtMtx); + hleSkipDeadbeef(); int delayus = adhocDefaultDelay; auto params = matchingEvents.begin(); @@ -5514,7 +5560,7 @@ void __NetMatchingCallbacks() //(int matchingId) if (actionAfterMatchingMipsCall < 0) { actionAfterMatchingMipsCall = __KernelRegisterActionType(AfterMatchingMipsCall::Create); } - + DEBUG_LOG(SCENET, "AdhocMatching - Remaining Events: %d", (int)matchingEvents.size()); DEBUG_LOG(SCENET, "AdhocMatchingCallback: [ID=%i][EVENT=%i][%s]", args[0], args[1], mac2str((SceNetEtherAddr*)Memory::GetPointer(args[2])).c_str()); AfterMatchingMipsCall* after = (AfterMatchingMipsCall*)__KernelCreateAction(actionAfterMatchingMipsCall); after->SetData(args[0], args[1], args[2]); @@ -5525,7 +5571,6 @@ void __NetMatchingCallbacks() //(int matchingId) // Must be delayed long enough whenever there is a pending callback. Should it be 10-100ms for Matching Events? or Not Less than the delays on sceNetAdhocMatching HLE? sceKernelDelayThread(delayus); - hleSkipDeadbeef(); } const HLEFunction sceNetAdhoc[] = { @@ -5684,7 +5729,7 @@ static int sceNetAdhocctlGetPeerList(u32 sizeAddr, u32 bufAddr) { discovered++; u32_le ipaddr = peer->ip_addr; - DEBUG_LOG(SCENET, "Peer [%s][%s][%s][%llu]", mac2str(&peer->mac_addr).c_str(), inet_ntoa(*(in_addr*)&ipaddr), (const char*)&peer->nickname.data, peer->last_recv); + DEBUG_LOG(SCENET, "Peer [%s][%s][%s][%llu]", mac2str(&peer->mac_addr).c_str(), ip2str(*(in_addr*)&ipaddr).c_str(), (const char*)&peer->nickname.data, peer->last_recv); } } @@ -5776,7 +5821,7 @@ static int sceNetAdhocctlGetAddrByName(const char *nickName, u32 sizeAddr, u32 b u64 lastrecv = std::max(0LL, static_cast(CoreTiming::GetGlobalTimeUsScaled() - defaultLastRecvDelta)); buf[discovered++].last_recv = lastrecv; - DEBUG_LOG(SCENET, "Peer [%s][%s][%s][%llu]", mac2str(&mac).c_str(), inet_ntoa(addr.sin_addr), nickName, lastrecv); + DEBUG_LOG(SCENET, "Peer [%s][%s][%s][%llu]", mac2str(&mac).c_str(), ip2str(addr.sin_addr).c_str(), nickName, lastrecv); } // Peer Reference @@ -5799,7 +5844,7 @@ static int sceNetAdhocctlGetAddrByName(const char *nickName, u32 sizeAddr, u32 b buf[discovered++].last_recv = peer->last_recv; u32_le ipaddr = peer->ip_addr; - DEBUG_LOG(SCENET, "Peer [%s][%s][%s][%llu]", mac2str(&peer->mac_addr).c_str(), inet_ntoa(*(in_addr*)&ipaddr), (const char*)&peer->nickname.data, peer->last_recv); + DEBUG_LOG(SCENET, "Peer [%s][%s][%s][%llu]", mac2str(&peer->mac_addr).c_str(), ip2str(*(in_addr*)&ipaddr).c_str(), (const char*)&peer->nickname.data, peer->last_recv); } } @@ -7033,6 +7078,7 @@ int matchingEventThread(int matchingId) int matchingInputThread(int matchingId) // TODO: The MatchingInput thread is using sceNetAdhocPdpRecv & sceNetAdhocPdpSend functions so it might be better to run this on PSP thread instead of real thread { setCurrentThreadName("MatchingInput"); + auto n = GetI18NCategory("Networking"); // Multithreading Lock peerlock.lock(); // Cast Context @@ -7208,6 +7254,10 @@ int matchingInputThread(int matchingId) // TODO: The MatchingInput thread is usi // Ignore Incoming Trash Data } + else if (recvresult == 0 && rxbuflen > 0) { + WARN_LOG(SCENET, "InputLoop[%d]: Unknown Port[%s:%u] (Recved=%i, Length=%i)", matchingId, mac2str(&sendermac).c_str(), senderport, recvresult, rxbuflen); + host->NotifyUserMessage(std::string(n->T("Data from incorrect Port")) + std::string(" [") + mac2str(&sendermac) + std::string("]:") + std::to_string(senderport) + std::string(" -> ") + std::to_string(context->port), 1.0, 0x0080ff); + } // Handle Peer Timeouts handleTimeout(context); diff --git a/Core/HLE/sceOpenPSID.cpp b/Core/HLE/sceOpenPSID.cpp index 86e35bc4fe..2fd31a12da 100644 --- a/Core/HLE/sceOpenPSID.cpp +++ b/Core/HLE/sceOpenPSID.cpp @@ -17,9 +17,9 @@ #include "Core/HLE/HLE.h" #include "Core/HLE/FunctionWrappers.h" +#include "Core/HLE/proAdhoc.h" #include "Core/HLE/sceOpenPSID.h" #include "Core/MemMapHelpers.h" -#include SceOpenPSID dummyOpenPSID = { 0x10, 0x02, 0xA3, 0x44, 0x13, 0xF5, 0x93, 0xB0, 0xCC, 0x6E, 0xD1, 0x32, 0x27, 0x85, 0x0F, 0x9D }; diff --git a/Core/HLE/sceP3da.cpp b/Core/HLE/sceP3da.cpp index 1595dc139e..de5ce004dc 100644 --- a/Core/HLE/sceP3da.cpp +++ b/Core/HLE/sceP3da.cpp @@ -48,11 +48,11 @@ static u32 sceP3daBridgeCore(u32 p3daCoreAddr, u32 channelsNum, u32 samplesNum, DEBUG_LOG(SCEAUDIO, "sceP3daBridgeCore(%08x, %08x, %08x, %08x, %08x)", p3daCoreAddr, channelsNum, samplesNum, inputAddr, outputAddr); if (Memory::IsValidAddress(inputAddr) && Memory::IsValidAddress(outputAddr)) { int scaleval = getScaleValue(channelsNum); - s16* outbuf = (s16*)Memory::GetPointer(outputAddr); + s16_le *outbuf = (s16_le *)Memory::GetPointer(outputAddr); memset(outbuf, 0, samplesNum * sizeof(s16) * 2); for (u32 k = 0; k < channelsNum; k++) { u32 inaddr = Memory::Read_U32(inputAddr + k * 4); - s16 *inbuf = (s16*)Memory::GetPointer(inaddr); + const s16 *inbuf = (const s16 *)Memory::GetPointer(inaddr); if (!inbuf) continue; for (u32 i = 0; i < samplesNum; i++) { diff --git a/Core/HLE/scePower.cpp b/Core/HLE/scePower.cpp index caa8e1d8fe..1fea3b6c51 100644 --- a/Core/HLE/scePower.cpp +++ b/Core/HLE/scePower.cpp @@ -284,7 +284,7 @@ static int sceKernelPowerTick(int flag) { return 0; } -static int __KernelVolatileMemLock(int type, u32 paddr, u32 psize) { +int KernelVolatileMemLock(int type, u32 paddr, u32 psize) { if (type != 0) { return SCE_KERNEL_ERROR_INVALID_MODE; } @@ -307,7 +307,7 @@ static int __KernelVolatileMemLock(int type, u32 paddr, u32 psize) { } static int sceKernelVolatileMemTryLock(int type, u32 paddr, u32 psize) { - u32 error = __KernelVolatileMemLock(type, paddr, psize); + u32 error = KernelVolatileMemLock(type, paddr, psize); switch (error) { case 0: @@ -331,43 +331,52 @@ static int sceKernelVolatileMemTryLock(int type, u32 paddr, u32 psize) { return error; } -static int sceKernelVolatileMemUnlock(int type) { +int KernelVolatileMemUnlock(int type) { if (type != 0) { - ERROR_LOG_REPORT(HLE, "sceKernelVolatileMemUnlock(%i) - invalid mode", type); return SCE_KERNEL_ERROR_INVALID_MODE; } - if (volatileMemLocked) { - volatileMemLocked = false; - - // Wake someone, always fifo. - bool wokeThreads = false; - u32 error; - while (!volatileWaitingThreads.empty() && !volatileMemLocked) { - VolatileWaitingThread waitInfo = volatileWaitingThreads.front(); - volatileWaitingThreads.erase(volatileWaitingThreads.begin()); - - int waitID = __KernelGetWaitID(waitInfo.threadID, WAITTYPE_VMEM, error); - // If they were force-released, just skip. - if (waitID == 1 && __KernelVolatileMemLock(0, waitInfo.addrPtr, waitInfo.sizePtr) == 0) { - __KernelResumeThreadFromWait(waitInfo.threadID, 0); - wokeThreads = true; - } - } - - if (wokeThreads) { - INFO_LOG(HLE, "sceKernelVolatileMemUnlock(%i) handed over to another thread", type); - hleReSchedule("volatile mem unlocked"); - } else { - DEBUG_LOG(HLE, "sceKernelVolatileMemUnlock(%i)", type); - } - } else { - ERROR_LOG_REPORT(HLE, "sceKernelVolatileMemUnlock(%i) FAILED - not locked", type); + if (!volatileMemLocked) { // I guess it must use a sema. return SCE_KERNEL_ERROR_SEMA_OVF; } + + volatileMemLocked = false; + + // Wake someone, always fifo. + bool wokeThreads = false; + u32 error; + while (!volatileWaitingThreads.empty() && !volatileMemLocked) { + VolatileWaitingThread waitInfo = volatileWaitingThreads.front(); + volatileWaitingThreads.erase(volatileWaitingThreads.begin()); + + int waitID = __KernelGetWaitID(waitInfo.threadID, WAITTYPE_VMEM, error); + // If they were force-released, just skip. + if (waitID == 1 && KernelVolatileMemLock(0, waitInfo.addrPtr, waitInfo.sizePtr) == 0) { + __KernelResumeThreadFromWait(waitInfo.threadID, 0); + wokeThreads = true; + } + } + + if (wokeThreads) { + INFO_LOG(HLE, "KernelVolatileMemUnlock(%i) handed over to another thread", type); + hleReSchedule("volatile mem unlocked"); + } return 0; } +static int sceKernelVolatileMemUnlock(int type) { + int error = KernelVolatileMemUnlock(type); + if (error == SCE_KERNEL_ERROR_INVALID_MODE) { + ERROR_LOG_REPORT(HLE, "sceKernelVolatileMemUnlock(%i) - invalid mode", type); + return error; + } else if (error == SCE_KERNEL_ERROR_SEMA_OVF) { + ERROR_LOG_REPORT(HLE, "sceKernelVolatileMemUnlock(%i) FAILED - not locked", type); + return error; + } + + return hleLogSuccessI(HLE, 0); +} + static int sceKernelVolatileMemLock(int type, u32 paddr, u32 psize) { u32 error = 0; @@ -378,7 +387,7 @@ static int sceKernelVolatileMemLock(int type, u32 paddr, u32 psize) { } else if (__IsInInterrupt()) { error = SCE_KERNEL_ERROR_ILLEGAL_CONTEXT; } else { - error = __KernelVolatileMemLock(type, paddr, psize); + error = KernelVolatileMemLock(type, paddr, psize); } switch (error) { diff --git a/Core/HLE/scePower.h b/Core/HLE/scePower.h index 78ac4a1c80..62a1f9084e 100644 --- a/Core/HLE/scePower.h +++ b/Core/HLE/scePower.h @@ -24,3 +24,6 @@ void __PowerDoState(PointerWrap &p); void Register_scePower(); void Register_sceSuspendForUser(); + +int KernelVolatileMemLock(int type, u32 paddr, u32 psize); +int KernelVolatileMemUnlock(int type); diff --git a/Core/HLE/scePsmf.cpp b/Core/HLE/scePsmf.cpp index 847d8b0035..71d6a8b1cc 100644 --- a/Core/HLE/scePsmf.cpp +++ b/Core/HLE/scePsmf.cpp @@ -110,8 +110,8 @@ struct PsmfData { struct PsmfPlayerCreateData { PSPPointer buffer; - u32 bufferSize; - int threadPriority; + u32_le bufferSize; + s32_le threadPriority; }; struct PsmfPlayerData { @@ -212,7 +212,7 @@ public: class PsmfPlayer { public: // For savestates only. - PsmfPlayer() : filehandle(0), finishThread(nullptr), videoWidth(480), videoHeight(272) { + PsmfPlayer() : videoWidth(480), videoHeight(272) { mediaengine = new MediaEngine(); } PsmfPlayer(const PsmfPlayerCreateData *data); @@ -243,7 +243,7 @@ public: return mediaengine->IsVideoEnd() && (mediaengine->IsNoAudioData() || !mediaengine->IsActuallyPlayingAudio()); } - int filehandle; + int filehandle = 0; u32 fileoffset; int readSize; int streamSize; @@ -275,7 +275,7 @@ public: PsmfPlayerStatus status; MediaEngine *mediaengine; - HLEHelperThread *finishThread; + HLEHelperThread *finishThread = nullptr; }; class PsmfStream { @@ -710,10 +710,8 @@ void __PsmfPlayerDoState(PointerWrap &p) { eventPsmfPlayerStatusChange = -1; } else { Do(p, eventPsmfPlayerStatusChange); - if (eventPsmfPlayerStatusChange != -1) { - CoreTiming::RestoreRegisterEvent(eventPsmfPlayerStatusChange, "PsmfPlayerStatusChangeEvent", &__PsmfPlayerStatusChange); - } } + CoreTiming::RestoreRegisterEvent(eventPsmfPlayerStatusChange, "PsmfPlayerStatusChangeEvent", &__PsmfPlayerStatusChange); if (s < 2) { // Assume the latest, which is what we were emulating before. psmfPlayerLibVersion = 0x06060010; @@ -732,9 +730,6 @@ void __PsmfShutdown() { } static void DelayPsmfStateChange(u32 psmfPlayer, u32 newState, s64 delayUs) { - if (eventPsmfPlayerStatusChange == -1) { - eventPsmfPlayerStatusChange = CoreTiming::RegisterEvent("PsmfPlayerStatusChange", &__PsmfPlayerStatusChange); - } CoreTiming::ScheduleEvent(usToCycles(delayUs), eventPsmfPlayerStatusChange, (u64)psmfPlayer << 32 | newState); } @@ -795,7 +790,6 @@ static u32 scePsmfGetNumberOfSpecificStreams(u32 psmfStruct, int streamType) { int streamNum = 0; for (auto it : psmf->streamMap) { - bool match = false; if (it.second->matchesType(streamType)) { streamNum++; } @@ -971,7 +965,7 @@ static u32 scePsmfVerifyPsmf(u32 psmfAddr) } // Kurohyou 2 (at least the demo) uses an uninitialized value that happens to be zero on the PSP. // It appears to be written by scePsmfVerifyPsmf(), so we write some bytes into the stack here. - Memory::Memset(currentMIPS->r[MIPS_REG_SP] - 0x20, 0, 0x20); + Memory::Memset(currentMIPS->r[MIPS_REG_SP] - 0x20, 0, 0x20, "PsmfStack"); DEBUG_LOG(ME, "scePsmfVerifyPsmf(%08x)", psmfAddr); return 0; } @@ -1112,7 +1106,7 @@ static u32 scePsmfGetEPidWithTimestamp(u32 psmfStruct, u32 ts) } static int scePsmfPlayerCreate(u32 psmfPlayer, u32 dataPtr) { - auto player = PSPPointer::Create(psmfPlayer); + auto player = PSPPointer::Create(psmfPlayer); const auto data = PSPPointer::Create(dataPtr); if (!player.IsValid() || !data.IsValid()) { @@ -1662,7 +1656,7 @@ static int scePsmfPlayerGetAudioData(u32 psmfPlayer, u32 audioDataAddr) if (psmfplayer->mediaengine->getAudioSamples(audioDataAddr) == 0) { if (psmfplayer->totalAudioStreams > 0 && (s64)psmfplayer->psmfPlayerAvcAu.pts < (s64)psmfplayer->totalDurationTimestamp - VIDEO_FRAME_DURATION_TS) { // Write zeros for any missing trailing frames so it syncs with the video. - Memory::Memset(audioDataAddr, 0, audioSamplesBytes); + Memory::Memset(audioDataAddr, 0, audioSamplesBytes, "PsmfAudioClear"); } else { ret = (int)ERROR_PSMFPLAYER_NO_MORE_DATA; } diff --git a/Core/HLE/sceSas.cpp b/Core/HLE/sceSas.cpp index 25cb02f392..1c1014252c 100644 --- a/Core/HLE/sceSas.cpp +++ b/Core/HLE/sceSas.cpp @@ -109,10 +109,9 @@ int __SasThread() { if (sasThreadState == SasThreadState::QUEUED) { sas->Mix(sasThreadParams.outAddr, sasThreadParams.inAddr, sasThreadParams.leftVol, sasThreadParams.rightVol); - sasDoneMutex.lock(); + std::lock_guard doneGuard(sasDoneMutex); sasThreadState = SasThreadState::READY; sasDone.notify_one(); - sasDoneMutex.unlock(); } } return 0; @@ -213,9 +212,7 @@ void __SasDoState(PointerWrap &p) { __SasDisableThread(); } - if (sasMixEvent != -1) { - CoreTiming::RestoreRegisterEvent(sasMixEvent, "SasMix", sasMixFinish); - } + CoreTiming::RestoreRegisterEvent(sasMixEvent, "SasMix", sasMixFinish); } void __SasShutdown() { diff --git a/Core/HLE/sceUmd.cpp b/Core/HLE/sceUmd.cpp index 51c44b72ae..8f61204230 100644 --- a/Core/HLE/sceUmd.cpp +++ b/Core/HLE/sceUmd.cpp @@ -105,11 +105,12 @@ void __UmdDoState(PointerWrap &p) } if (s > 2) { Do(p, umdInsertChangeEvent); - CoreTiming::RestoreRegisterEvent(umdInsertChangeEvent, "UmdInsertChange", __UmdInsertChange); Do(p, UMDInserted); - } - else + } else { + umdInsertChangeEvent = -1; UMDInserted = true; + } + CoreTiming::RestoreRegisterEvent(umdInsertChangeEvent, "UmdInsertChange", __UmdInsertChange); } static u8 __KernelUmdGetState() diff --git a/Core/HLE/sceUsb.cpp b/Core/HLE/sceUsb.cpp index 520b057826..14c017f8fa 100644 --- a/Core/HLE/sceUsb.cpp +++ b/Core/HLE/sceUsb.cpp @@ -17,18 +17,26 @@ #include "Common/Serialize/Serializer.h" #include "Common/Serialize/SerializeFuncs.h" +#include "Core/CoreTiming.h" #include "Core/HLE/HLE.h" #include "Core/HLE/FunctionWrappers.h" -#include "Core/MIPS/MIPS.h" -#include "Core/CoreTiming.h" +#include "Core/HLE/KernelWaitHelpers.h" +#include "Core/HLE/sceKernelThread.h" #include "Core/HLE/sceUsb.h" +#include "Core/MIPS/MIPS.h" +#include "Core/Reporting.h" + +static constexpr uint32_t ERROR_USB_WAIT_TIMEOUT = 0x80243008; // TODO: Map by driver name -bool usbStarted = false; +static bool usbStarted = false; // TODO: Check actual status -bool usbConnected = true; +static bool usbConnected = true; // TODO: Activation by product id -bool usbActivated = false; +static bool usbActivated = false; + +static int usbWaitTimer = -1; +static std::vector waitingThreads; enum UsbStatus { USB_STATUS_STOPPED = 0x001, @@ -39,16 +47,85 @@ enum UsbStatus { USB_STATUS_ACTIVATED = 0x200, }; -void __UsbInit() -{ +static int UsbCurrentState() { + int state = 0; + if (usbStarted) { + state = USB_STATUS_STARTED + | (usbConnected ? USB_STATUS_CONNECTED : USB_STATUS_DISCONNECTED) + | (usbActivated ? USB_STATUS_ACTIVATED : USB_STATUS_DEACTIVATED); + } + return state; +} + +static bool UsbMatchState(int state, u32 mode) { + int match = state & UsbCurrentState(); + if (mode == 0) { + return match == state; + } + return match != 0; +} + +static void UsbSetTimeout(PSPPointer timeout) { + if (!timeout.IsValid() || usbWaitTimer == -1) + return; + + // This should call __UsbWaitTimeout() later, unless we cancel it. + CoreTiming::ScheduleEvent(usToCycles(*timeout), usbWaitTimer, __KernelGetCurThread()); +} + +static void UsbWaitExecTimeout(u64 userdata, int cycleslate) { + u32 error; + SceUID threadID = (SceUID)userdata; + + PSPPointer timeout = PSPPointer::Create(__KernelGetWaitTimeoutPtr(threadID, error)); + if (timeout.IsValid()) + *timeout = 0; + + HLEKernel::RemoveWaitingThread(waitingThreads, threadID); + __KernelResumeThreadFromWait(threadID, ERROR_USB_WAIT_TIMEOUT); + __KernelReSchedule("wait timed out"); +} + +static void UsbUpdateState() { + u32 error; + bool wokeThreads = false; + for (size_t i = 0; i < waitingThreads.size(); ++i) { + SceUID threadID = waitingThreads[i]; + int state = __KernelGetWaitID(threadID, WAITTYPE_USB, error); + if (error != 0) + continue; + + u32 mode = __KernelGetWaitValue(threadID, error); + if (UsbMatchState(state, mode)) { + waitingThreads.erase(waitingThreads.begin() + i); + --i; + + PSPPointer timeout = PSPPointer::Create(__KernelGetWaitTimeoutPtr(threadID, error)); + if (timeout.IsValid() && usbWaitTimer != -1) { + // Remove any event for this thread. + s64 cyclesLeft = CoreTiming::UnscheduleEvent(usbWaitTimer, threadID); + *timeout = (int)cyclesToUs(cyclesLeft); + } + + __KernelResumeThreadFromWait(threadID, UsbCurrentState()); + } + } + + if (wokeThreads) + hleReSchedule("usb state change"); +} + +void __UsbInit() { usbStarted = false; usbConnected = true; usbActivated = false; + waitingThreads.clear(); + + usbWaitTimer = CoreTiming::RegisterEvent("UsbWaitTimeout", UsbWaitExecTimeout); } -void __UsbDoState(PointerWrap &p) -{ - auto s = p.Section("sceUsb", 1, 2); +void __UsbDoState(PointerWrap &p) { + auto s = p.Section("sceUsb", 1, 3); if (!s) return; @@ -60,17 +137,27 @@ void __UsbDoState(PointerWrap &p) usbConnected = true; } Do(p, usbActivated); + if (s >= 3) { + Do(p, waitingThreads); + Do(p, usbWaitTimer); + } else { + waitingThreads.clear(); + usbWaitTimer = -1; + } + CoreTiming::RestoreRegisterEvent(usbWaitTimer, "UsbWaitTimeout", UsbWaitExecTimeout); } static int sceUsbStart(const char* driverName, u32 argsSize, u32 argsPtr) { INFO_LOG(HLE, "sceUsbStart(%s, %i, %08x)", driverName, argsSize, argsPtr); usbStarted = true; + UsbUpdateState(); return 0; } static int sceUsbStop(const char* driverName, u32 argsSize, u32 argsPtr) { INFO_LOG(HLE, "sceUsbStop(%s, %i, %08x)", driverName, argsSize, argsPtr); usbStarted = false; + UsbUpdateState(); return 0; } @@ -79,9 +166,7 @@ static int sceUsbGetState() { if (!usbStarted) { state = 0x80243007; } else { - state = USB_STATUS_STARTED - | (usbConnected ? USB_STATUS_CONNECTED : USB_STATUS_DISCONNECTED) - | (usbActivated ? USB_STATUS_ACTIVATED : USB_STATUS_DEACTIVATED); + state = UsbCurrentState(); } DEBUG_LOG(HLE, "sceUsbGetState: 0x%x", state); return state; @@ -90,22 +175,40 @@ static int sceUsbGetState() { static int sceUsbActivate(u32 pid) { INFO_LOG(HLE, "sceUsbActivate(%i)", pid); usbActivated = true; + UsbUpdateState(); return 0; } static int sceUsbDeactivate(u32 pid) { INFO_LOG(HLE, "sceUsbDeactivate(%i)", pid); usbActivated = false; + UsbUpdateState(); return 0; } -static int sceUsbWaitState(int state, int waitMode, u32 timeoutAddr) { - ERROR_LOG(HLE, "UNIMPL sceUsbWaitStat(%i, %i, %08x)", state, waitMode, timeoutAddr); - return sceUsbGetState(); +static int sceUsbWaitState(int state, u32 waitMode, u32 timeoutPtr) { + hleEatCycles(10000); + + if (waitMode >= 2) + return hleLogError(HLE, SCE_KERNEL_ERROR_ILLEGAL_MODE, "invalid mode"); + if (state == 0) + return hleLogError(HLE, SCE_KERNEL_ERROR_EVF_ILPAT, "bad state"); + + if (UsbMatchState(state, waitMode)) { + return hleLogSuccessX(HLE, UsbCurrentState()); + } + + // We'll have to wait as long as it takes. Cleanup first, just in case. + HLEKernel::RemoveWaitingThread(waitingThreads, __KernelGetCurThread()); + waitingThreads.push_back(__KernelGetCurThread()); + + UsbSetTimeout(PSPPointer::Create(timeoutPtr)); + __KernelWaitCurThread(WAITTYPE_USB, state, waitMode, timeoutPtr, false, "usb state waited"); + return hleLogSuccessI(HLE, 0, "waiting"); } -static int sceUsbWaitStateCB(int state, int waitMode, u32 timeoutAddr) { - ERROR_LOG(HLE, "UNIMPL sceUsbWaitStateCB(%i, %i, %08x)", state, waitMode, timeoutAddr); +static int sceUsbWaitStateCB(int state, u32 waitMode, u32 timeoutPtr) { + ERROR_LOG_REPORT(HLE, "UNIMPL sceUsbWaitStateCB(%i, %i, %08x)", state, waitMode, timeoutPtr); return 0; } @@ -118,8 +221,8 @@ const HLEFunction sceUsb[] = {0X112CC951, nullptr, "sceUsbGetDrvState", '?', "" }, {0X586DB82C, &WrapI_U, "sceUsbActivate", 'i', "x" }, {0XC572A9C8, &WrapI_U, "sceUsbDeactivate", 'i', "x" }, - {0X5BE0E002, &WrapI_IIU, "sceUsbWaitState", '?', "xxx"}, - {0X616F2B61, &WrapI_IIU, "sceUsbWaitStateCB", '?', "xxx"}, + {0X5BE0E002, &WrapI_IUU, "sceUsbWaitState", 'x', "xip"}, + {0X616F2B61, &WrapI_IUU, "sceUsbWaitStateCB", 'x', "xip"}, {0X1C360735, nullptr, "sceUsbWaitCancel", '?', "" }, }; diff --git a/Core/HLE/sceUsbCam.cpp b/Core/HLE/sceUsbCam.cpp index 3882e140fb..18417bb9c2 100644 --- a/Core/HLE/sceUsbCam.cpp +++ b/Core/HLE/sceUsbCam.cpp @@ -34,9 +34,7 @@ #endif #ifdef HAVE_WIN32_CAMERA -#ifndef NOMINMAX -#define NOMINMAX -#endif +#include "Common/CommonWindows.h" #include "Windows/CaptureDevice.h" #endif diff --git a/Core/HLE/sceUsbGps.cpp b/Core/HLE/sceUsbGps.cpp index 456d09b487..2c9f320044 100644 --- a/Core/HLE/sceUsbGps.cpp +++ b/Core/HLE/sceUsbGps.cpp @@ -15,6 +15,9 @@ // Official git repository and contact information can be found at // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. +#ifdef __MINGW32__ +#include +#endif #include #include "Common/System/System.h" diff --git a/Core/HLE/sceUsbMic.cpp b/Core/HLE/sceUsbMic.cpp index 086b968f45..49e2aec844 100644 --- a/Core/HLE/sceUsbMic.cpp +++ b/Core/HLE/sceUsbMic.cpp @@ -15,6 +15,7 @@ // Official git repository and contact information can be found at // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. +#include "ppsspp_config.h" #include #include @@ -33,7 +34,7 @@ #endif #ifdef HAVE_WIN32_MICROPHONE -#define NOMINMAX +#include "Common/CommonWindows.h" #include "Windows/CaptureDevice.h" #endif @@ -71,8 +72,6 @@ static void __MicBlockingResume(u64 userdata, int cyclesLate) { waitingThreads.erase(waitingThreads.begin() + count); } else { u64 waitTimeus = (waitingThread.needSize - Microphone::getReadMicDataLength()) * 1000000 / 2 / waitingThread.sampleRate; - if(eventMicBlockingResume == -1) - eventMicBlockingResume = CoreTiming::RegisterEvent("MicBlockingResume", &__MicBlockingResume); CoreTiming::ScheduleEvent(usToCycles(waitTimeus), eventMicBlockingResume, userdata); } } else { @@ -119,6 +118,10 @@ void __UsbMicShutdown() { void __UsbMicDoState(PointerWrap &p) { auto s = p.Section("sceUsbMic", 0, 3); if (!s) { + // Still need to restore the event. + eventMicBlockingResume = -1; + CoreTiming::RestoreRegisterEvent(eventMicBlockingResume, "MicBlockingResume", &__MicBlockingResume); + waitingThreads.clear(); return; } bool isMicStartedNow = Microphone::isMicStarted(); @@ -130,12 +133,11 @@ void __UsbMicDoState(PointerWrap &p) { Do(p, micState); if (s > 1) { Do(p, eventMicBlockingResume); - if (eventMicBlockingResume != -1) { - CoreTiming::RestoreRegisterEvent(eventMicBlockingResume, "MicBlockingResume", &__MicBlockingResume); - } } else { eventMicBlockingResume = -1; } + CoreTiming::RestoreRegisterEvent(eventMicBlockingResume, "MicBlockingResume", &__MicBlockingResume); + if (s > 2) { Do(p, curTargetAddr); Do(p, readMicDataLength); @@ -452,8 +454,6 @@ u32 __MicInput(u32 maxSamples, u32 sampleRate, u32 bufAddr, MICTYPE type, bool b } u64 waitTimeus = (size - Microphone::availableAudioBufSize()) * 1000000 / 2 / sampleRate; - if (eventMicBlockingResume == -1) - eventMicBlockingResume = CoreTiming::RegisterEvent("MicBlockingResume", &__MicBlockingResume); CoreTiming::ScheduleEvent(usToCycles(waitTimeus), eventMicBlockingResume, __KernelGetCurThread()); MicWaitInfo waitInfo = { __KernelGetCurThread(), bufAddr, size, sampleRate }; waitingThreads.push_back(waitInfo); diff --git a/Core/HLE/sceUtility.cpp b/Core/HLE/sceUtility.cpp index 8e2c27ac09..e0aa43ec83 100644 --- a/Core/HLE/sceUtility.cpp +++ b/Core/HLE/sceUtility.cpp @@ -24,11 +24,14 @@ #include "Common/Serialize/SerializeFuncs.h" #include "Common/Serialize/SerializeMap.h" #include "Common/Serialize/SerializeSet.h" +#include "Core/Config.h" +#include "Core/CoreTiming.h" #include "Core/HLE/HLE.h" +#include "Core/HLE/HLEHelperThread.h" #include "Core/HLE/FunctionWrappers.h" #include "Core/MIPS/MIPS.h" +#include "Core/MIPS/MIPSCodeUtils.h" #include "Core/Reporting.h" -#include "Core/Config.h" #include "Core/System.h" #include "Core/HLE/sceKernel.h" @@ -36,8 +39,6 @@ #include "Core/HLE/sceKernelThread.h" #include "Core/HLE/sceUtility.h" -#include "Core/HLE/sceCtrl.h" -#include "Core/Util/PPGeDraw.h" #include "Core/Dialog/PSPSaveDialog.h" #include "Core/Dialog/PSPMsgDialog.h" #include "Core/Dialog/PSPPlaceholderDialog.h" @@ -65,7 +66,6 @@ const int SCE_ERROR_MODULE_BAD_ID = 0x80111101; const int SCE_ERROR_MODULE_ALREADY_LOADED = 0x80111102; const int SCE_ERROR_MODULE_NOT_LOADED = 0x80111103; const int SCE_ERROR_AV_MODULE_BAD_ID = 0x80110F01; -int oldStatus = 100; //random value static const int noDeps[] = {0}; static const int httpModuleDeps[] = {0x0102, 0x0103, 0x0104, 0}; @@ -120,55 +120,63 @@ static const ModuleLoadInfo moduleLoadInfo[] = { ModuleLoadInfo(0x0601, 0x00000000), }; -enum UtilityDialogType { - UTILITY_DIALOG_NONE, - UTILITY_DIALOG_SAVEDATA, - UTILITY_DIALOG_MSG, - UTILITY_DIALOG_OSK, - UTILITY_DIALOG_NET, - UTILITY_DIALOG_SCREENSHOT, - UTILITY_DIALOG_GAMESHARING, - UTILITY_DIALOG_GAMEDATAINSTALL, -}; - // Only a single dialog is allowed at a time. static UtilityDialogType currentDialogType; bool currentDialogActive; -static PSPSaveDialog saveDialog; -static PSPMsgDialog msgDialog; -static PSPOskDialog oskDialog; -static PSPNetconfDialog netDialog; -static PSPScreenshotDialog screenshotDialog; -static PSPGamedataInstallDialog gamedataInstallDialog; +static PSPSaveDialog saveDialog(UtilityDialogType::SAVEDATA); +static PSPMsgDialog msgDialog(UtilityDialogType::MSG); +static PSPOskDialog oskDialog(UtilityDialogType::OSK); +static PSPNetconfDialog netDialog(UtilityDialogType::NET); +static PSPScreenshotDialog screenshotDialog(UtilityDialogType::SCREENSHOT); +static PSPGamedataInstallDialog gamedataInstallDialog(UtilityDialogType::GAMEDATAINSTALL); +static int oldStatus = 100; //random value static std::map currentlyLoadedModules; +static int volatileUnlockEvent = -1; +static HLEHelperThread *accessThread = nullptr; + +static void CleanupDialogThreads() { + if (accessThread && accessThread->Stopped()) { + delete accessThread; + accessThread = nullptr; + } +} static void ActivateDialog(UtilityDialogType type) { if (!currentDialogActive) { - // TODO: Lock volatile RAM (see https://github.com/hrydgard/ppsspp/issues/8288) - // We don't have a virtual dialog thread unlike JPSCP. Not sure if it's OK to - // just do it on the current thread - it does seem dangerous to assume so... currentDialogType = type; currentDialogActive = true; } + CleanupDialogThreads(); } static void DeactivateDialog() { if (currentDialogActive) { - // TODO: Unlock and zero volatile RAM. currentDialogActive = false; } + CleanupDialogThreads(); +} + +static void UtilityVolatileUnlock(u64 userdata, int cyclesLate) { + // There can only be one active, so just try each of them. + saveDialog.FinishVolatile(); + msgDialog.FinishVolatile(); + oskDialog.FinishVolatile(); + netDialog.FinishVolatile(); + screenshotDialog.FinishVolatile(); + gamedataInstallDialog.FinishVolatile(); } void __UtilityInit() { - currentDialogType = UTILITY_DIALOG_NONE; + currentDialogType = UtilityDialogType::NONE; DeactivateDialog(); SavedataParam::Init(); currentlyLoadedModules.clear(); + volatileUnlockEvent = CoreTiming::RegisterEvent("UtilityVolatileUnlock", UtilityVolatileUnlock); } void __UtilityDoState(PointerWrap &p) { - auto s = p.Section("sceUtility", 1, 2); + auto s = p.Section("sceUtility", 1, 4); if (!s) { return; } @@ -191,6 +199,29 @@ void __UtilityDoState(PointerWrap &p) { currentlyLoadedModules[*it] = 0; } } + + if (s >= 3) { + Do(p, volatileUnlockEvent); + } else { + volatileUnlockEvent = -1; + } + CoreTiming::RestoreRegisterEvent(volatileUnlockEvent, "UtilityVolatileUnlock", UtilityVolatileUnlock); + + bool hasAccessThread = accessThread != nullptr; + if (s >= 4) { + Do(p, hasAccessThread); + if (hasAccessThread) { + Do(p, accessThread); + } + } else { + hasAccessThread = false; + } + + if (!hasAccessThread && accessThread) { + accessThread->Forget(); + delete accessThread; + accessThread = nullptr; + } } void __UtilityShutdown() { @@ -200,48 +231,98 @@ void __UtilityShutdown() { netDialog.Shutdown(true); screenshotDialog.Shutdown(true); gamedataInstallDialog.Shutdown(true); + + if (accessThread) { + delete accessThread; + accessThread = nullptr; + } } -static int sceUtilitySavedataInitStart(u32 paramAddr) -{ - if (currentDialogActive && currentDialogType != UTILITY_DIALOG_SAVEDATA) - { - if (PSP_CoreParameter().compat.flags().YugiohSaveFix) - { +void UtilityDialogShutdown(UtilityDialogType type, int delayUs, int priority) { + // Break it up so better-priority rescheduling happens. + // The windows aren't this regular, but close. + int partDelay = delayUs / 4; + const u32_le insts[] = { + // Make sure we don't discard/deadbeef 'em. + (u32_le)MIPS_MAKE_ORI(MIPS_REG_S0, MIPS_REG_A0, 0), + (u32_le)MIPS_MAKE_SYSCALL("sceUtility", "__UtilityWorkUs"), + (u32_le)MIPS_MAKE_ORI(MIPS_REG_A0, MIPS_REG_S0, 0), + (u32_le)MIPS_MAKE_SYSCALL("sceUtility", "__UtilityWorkUs"), + (u32_le)MIPS_MAKE_ORI(MIPS_REG_A0, MIPS_REG_S0, 0), + (u32_le)MIPS_MAKE_SYSCALL("sceUtility", "__UtilityWorkUs"), + (u32_le)MIPS_MAKE_ORI(MIPS_REG_A0, MIPS_REG_S0, 0), + (u32_le)MIPS_MAKE_SYSCALL("sceUtility", "__UtilityWorkUs"), + + (u32_le)MIPS_MAKE_ORI(MIPS_REG_A0, MIPS_REG_ZERO, (int)type), + (u32_le)MIPS_MAKE_JR_RA(), + (u32_le)MIPS_MAKE_SYSCALL("sceUtility", "__UtilityFinishDialog"), + }; + + CleanupDialogThreads(); + _assert_(accessThread == nullptr); + accessThread = new HLEHelperThread("ScePafJob", insts, (uint32_t)ARRAY_SIZE(insts), priority, 0x200); + accessThread->Start(partDelay, 0); +} + +static int UtilityWorkUs(int us) { + // This blocks, but other better priority threads can get time. + // Simulate this by allowing a reschedule. + hleEatMicro(us); + hleReSchedule("utility work"); + return 0; +} + +static int UtilityFinishDialog(int type) { + switch ((UtilityDialogType)type) { + case UtilityDialogType::NONE: + break; + case UtilityDialogType::SAVEDATA: + return hleLogSuccessI(SCEUTILITY, saveDialog.FinishShutdown()); + case UtilityDialogType::MSG: + return hleLogSuccessI(SCEUTILITY, msgDialog.FinishShutdown()); + case UtilityDialogType::OSK: + return hleLogSuccessI(SCEUTILITY, oskDialog.FinishShutdown()); + case UtilityDialogType::NET: + return hleLogSuccessI(SCEUTILITY, netDialog.FinishShutdown()); + case UtilityDialogType::SCREENSHOT: + return hleLogSuccessI(SCEUTILITY, screenshotDialog.FinishShutdown()); + case UtilityDialogType::GAMESHARING: + return hleLogError(SCEUTILITY, -1, "unimplemented"); + case UtilityDialogType::GAMEDATAINSTALL: + return hleLogSuccessI(SCEUTILITY, gamedataInstallDialog.FinishShutdown()); + } + return hleLogError(SCEUTILITY, 0, "invalid dialog type?"); +} + +static int sceUtilitySavedataInitStart(u32 paramAddr) { + if (currentDialogActive && currentDialogType != UtilityDialogType::SAVEDATA) { + if (PSP_CoreParameter().compat.flags().YugiohSaveFix) { WARN_LOG(SCEUTILITY, "Yugioh Savedata Correction"); - } - else { + } else { WARN_LOG(SCEUTILITY, "sceUtilitySavedataInitStart(%08x): wrong dialog type", paramAddr); return SCE_ERROR_UTILITY_WRONG_TYPE; } } oldStatus = 100; - ActivateDialog(UTILITY_DIALOG_SAVEDATA); + ActivateDialog(UtilityDialogType::SAVEDATA); int ret = saveDialog.Init(paramAddr); DEBUG_LOG(SCEUTILITY,"%08x=sceUtilitySavedataInitStart(%08x)",ret,paramAddr); return ret; } -static int sceUtilitySavedataShutdownStart() -{ - if (currentDialogType != UTILITY_DIALOG_SAVEDATA) - { - WARN_LOG(SCEUTILITY, "sceUtilitySavedataShutdownStart(): wrong dialog type"); - return SCE_ERROR_UTILITY_WRONG_TYPE; - } +static int sceUtilitySavedataShutdownStart() { + if (currentDialogType != UtilityDialogType::SAVEDATA) + return hleLogWarning(SCEUTILITY, SCE_ERROR_UTILITY_WRONG_TYPE, "wrong dialog type"); - DeactivateDialog(); DeactivateDialog(); int ret = saveDialog.Shutdown(); - DEBUG_LOG(SCEUTILITY,"%08x=sceUtilitySavedataShutdownStart()",ret); - return ret; + hleEatCycles(30000); + return hleLogSuccessX(SCEUTILITY, ret); } -static int sceUtilitySavedataGetStatus() -{ - if (currentDialogType != UTILITY_DIALOG_SAVEDATA) - { +static int sceUtilitySavedataGetStatus() { + if (currentDialogType != UtilityDialogType::SAVEDATA) { DEBUG_LOG(SCEUTILITY, "sceUtilitySavedataGetStatus(): wrong dialog type"); hleEatCycles(200); return SCE_ERROR_UTILITY_WRONG_TYPE; @@ -253,13 +334,12 @@ static int sceUtilitySavedataGetStatus() DEBUG_LOG(SCEUTILITY, "%08x=sceUtilitySavedataGetStatus()", status); } hleEatCycles(200); + CleanupDialogThreads(); return status; } -static int sceUtilitySavedataUpdate(int animSpeed) -{ - if (currentDialogType != UTILITY_DIALOG_SAVEDATA) - { +static int sceUtilitySavedataUpdate(int animSpeed) { + if (currentDialogType != UtilityDialogType::SAVEDATA) { WARN_LOG(SCEUTILITY, "sceUtilitySavedataUpdate(%i): wrong dialog type", animSpeed); return SCE_ERROR_UTILITY_WRONG_TYPE; } @@ -354,26 +434,21 @@ static u32 sceUtilityUnloadModule(u32 module) { return hleDelayResult(hleLogSuccessInfoI(SCEUTILITY, 0), "utility module unloaded", 400); } -static int sceUtilityMsgDialogInitStart(u32 paramAddr) -{ - if (currentDialogActive && currentDialogType != UTILITY_DIALOG_MSG) - { +static int sceUtilityMsgDialogInitStart(u32 paramAddr) { + if (currentDialogActive && currentDialogType != UtilityDialogType::MSG) { WARN_LOG(SCEUTILITY, "sceUtilityMsgDialogInitStart(%08x): wrong dialog type", paramAddr); return SCE_ERROR_UTILITY_WRONG_TYPE; } oldStatus = 100; - currentDialogType = UTILITY_DIALOG_MSG; - currentDialogActive = true; + ActivateDialog(UtilityDialogType::MSG); int ret = msgDialog.Init(paramAddr); INFO_LOG(SCEUTILITY, "%08x=sceUtilityMsgDialogInitStart(%08x)", ret, paramAddr); return ret; } -static int sceUtilityMsgDialogShutdownStart() -{ - if (currentDialogType != UTILITY_DIALOG_MSG) - { +static int sceUtilityMsgDialogShutdownStart() { + if (currentDialogType != UtilityDialogType::MSG) { WARN_LOG(SCEUTILITY, "sceUtilityMsgDialogShutdownStart(): wrong dialog type"); return SCE_ERROR_UTILITY_WRONG_TYPE; } @@ -384,10 +459,8 @@ static int sceUtilityMsgDialogShutdownStart() return ret; } -static int sceUtilityMsgDialogUpdate(int animSpeed) -{ - if (currentDialogType != UTILITY_DIALOG_MSG) - { +static int sceUtilityMsgDialogUpdate(int animSpeed) { + if (currentDialogType != UtilityDialogType::MSG) { WARN_LOG(SCEUTILITY, "sceUtilityMsgDialogUpdate(%i): wrong dialog type", animSpeed); return SCE_ERROR_UTILITY_WRONG_TYPE; } @@ -399,10 +472,8 @@ static int sceUtilityMsgDialogUpdate(int animSpeed) return ret; } -static int sceUtilityMsgDialogGetStatus() -{ - if (currentDialogType != UTILITY_DIALOG_MSG) - { +static int sceUtilityMsgDialogGetStatus() { + if (currentDialogType != UtilityDialogType::MSG) { DEBUG_LOG(SCEUTILITY, "sceUtilityMsgDialogGetStatus(): wrong dialog type"); return SCE_ERROR_UTILITY_WRONG_TYPE; } @@ -412,13 +483,12 @@ static int sceUtilityMsgDialogGetStatus() oldStatus = status; DEBUG_LOG(SCEUTILITY, "%08x=sceUtilityMsgDialogGetStatus()", status); } + CleanupDialogThreads(); return status; } -static int sceUtilityMsgDialogAbort() -{ - if (currentDialogType != UTILITY_DIALOG_MSG) - { +static int sceUtilityMsgDialogAbort() { + if (currentDialogType != UtilityDialogType::MSG) { WARN_LOG(SCEUTILITY, "sceUtilityMsgDialogAbort(): wrong dialog type"); return SCE_ERROR_UTILITY_WRONG_TYPE; } @@ -430,24 +500,21 @@ static int sceUtilityMsgDialogAbort() // On screen keyboard -static int sceUtilityOskInitStart(u32 oskPtr) -{ - if (currentDialogActive && currentDialogType != UTILITY_DIALOG_OSK) - { +static int sceUtilityOskInitStart(u32 oskPtr) { + if (currentDialogActive && currentDialogType != UtilityDialogType::OSK) { WARN_LOG(SCEUTILITY, "sceUtilityOskInitStart(%08x): wrong dialog type", oskPtr); return SCE_ERROR_UTILITY_WRONG_TYPE; } oldStatus = 100; - ActivateDialog(UTILITY_DIALOG_OSK); + ActivateDialog(UtilityDialogType::OSK); int ret = oskDialog.Init(oskPtr); INFO_LOG(SCEUTILITY, "%08x=sceUtilityOskInitStart(%08x)", ret, oskPtr); return ret; } -static int sceUtilityOskShutdownStart() -{ - if (currentDialogType != UTILITY_DIALOG_OSK) +static int sceUtilityOskShutdownStart() { + if (currentDialogType != UtilityDialogType::OSK) { WARN_LOG(SCEUTILITY, "sceUtilityOskShutdownStart(): wrong dialog type"); return SCE_ERROR_UTILITY_WRONG_TYPE; @@ -459,10 +526,8 @@ static int sceUtilityOskShutdownStart() return ret; } -static int sceUtilityOskUpdate(int animSpeed) -{ - if (currentDialogType != UTILITY_DIALOG_OSK) - { +static int sceUtilityOskUpdate(int animSpeed) { + if (currentDialogType != UtilityDialogType::OSK) { WARN_LOG(SCEUTILITY, "sceUtilityOskUpdate(%i): wrong dialog type", animSpeed); return SCE_ERROR_UTILITY_WRONG_TYPE; } @@ -472,10 +537,8 @@ static int sceUtilityOskUpdate(int animSpeed) return ret; } -static int sceUtilityOskGetStatus() -{ - if (currentDialogType != UTILITY_DIALOG_OSK) - { +static int sceUtilityOskGetStatus() { + if (currentDialogType != UtilityDialogType::OSK) { DEBUG_LOG(SCEUTILITY, "sceUtilityOskGetStatus(): wrong dialog type"); return SCE_ERROR_UTILITY_WRONG_TYPE; } @@ -485,22 +548,23 @@ static int sceUtilityOskGetStatus() oldStatus = status; DEBUG_LOG(SCEUTILITY, "%08x=sceUtilityOskGetStatus()", status); } + CleanupDialogThreads(); return status; } static int sceUtilityNetconfInitStart(u32 paramsAddr) { - if (currentDialogActive && currentDialogType != UTILITY_DIALOG_NET) { + if (currentDialogActive && currentDialogType != UtilityDialogType::NET) { return hleLogWarning(SCEUTILITY, SCE_ERROR_UTILITY_WRONG_TYPE, "wrong dialog type"); } oldStatus = 100; - ActivateDialog(UTILITY_DIALOG_NET); + ActivateDialog(UtilityDialogType::NET); return hleLogSuccessInfoI(SCEUTILITY, netDialog.Init(paramsAddr)); } static int sceUtilityNetconfShutdownStart() { - if (currentDialogType != UTILITY_DIALOG_NET) { + if (currentDialogType != UtilityDialogType::NET) { return hleLogWarning(SCEUTILITY, SCE_ERROR_UTILITY_WRONG_TYPE, "wrong dialog type"); } @@ -509,7 +573,7 @@ static int sceUtilityNetconfShutdownStart() { } static int sceUtilityNetconfUpdate(int animSpeed) { - if (currentDialogType != UTILITY_DIALOG_NET) { + if (currentDialogType != UtilityDialogType::NET) { return hleLogWarning(SCEUTILITY, SCE_ERROR_UTILITY_WRONG_TYPE, "wrong dialog type"); } @@ -517,7 +581,7 @@ static int sceUtilityNetconfUpdate(int animSpeed) { } static int sceUtilityNetconfGetStatus() { - if (currentDialogType != UTILITY_DIALOG_NET) { + if (currentDialogType != UtilityDialogType::NET) { // Spam in Danball Senki BOOST. return hleLogDebug(SCEUTILITY, SCE_ERROR_UTILITY_WRONG_TYPE, "wrong dialog type"); } @@ -527,6 +591,7 @@ static int sceUtilityNetconfGetStatus() { oldStatus = status; return hleLogSuccessI(SCEUTILITY, status); } + CleanupDialogThreads(); return hleLogSuccessVerboseI(SCEUTILITY, status); } @@ -541,25 +606,21 @@ static int sceUtilityCheckNetParam(int id) //TODO: Implement all sceUtilityScreenshot* for real, it doesn't seem to be complex //but it requires more investigation -static int sceUtilityScreenshotInitStart(u32 paramAddr) -{ - if (currentDialogActive && currentDialogType != UTILITY_DIALOG_SCREENSHOT) - { +static int sceUtilityScreenshotInitStart(u32 paramAddr) { + if (currentDialogActive && currentDialogType != UtilityDialogType::SCREENSHOT) { WARN_LOG(SCEUTILITY, "sceUtilityScreenshotInitStart(%08x): wrong dialog type", paramAddr); return SCE_ERROR_UTILITY_WRONG_TYPE; } oldStatus = 100; - ActivateDialog(UTILITY_DIALOG_SCREENSHOT); + ActivateDialog(UtilityDialogType::SCREENSHOT); u32 retval = screenshotDialog.Init(paramAddr); WARN_LOG_REPORT(SCEUTILITY, "%08x=sceUtilityScreenshotInitStart(%08x)", retval, paramAddr); return retval; } -static int sceUtilityScreenshotShutdownStart() -{ - if (currentDialogType != UTILITY_DIALOG_SCREENSHOT) - { +static int sceUtilityScreenshotShutdownStart() { + if (currentDialogType != UtilityDialogType::SCREENSHOT) { WARN_LOG(SCEUTILITY, "sceUtilityScreenshotShutdownStart(): wrong dialog type"); return SCE_ERROR_UTILITY_WRONG_TYPE; } @@ -570,10 +631,8 @@ static int sceUtilityScreenshotShutdownStart() return ret; } -static int sceUtilityScreenshotUpdate(u32 animSpeed) -{ - if (currentDialogType != UTILITY_DIALOG_SCREENSHOT) - { +static int sceUtilityScreenshotUpdate(u32 animSpeed) { + if (currentDialogType != UtilityDialogType::SCREENSHOT) { WARN_LOG(SCEUTILITY, "sceUtilityScreenshotUpdate(%i): wrong dialog type", animSpeed); return SCE_ERROR_UTILITY_WRONG_TYPE; } @@ -583,10 +642,8 @@ static int sceUtilityScreenshotUpdate(u32 animSpeed) return ret; } -static int sceUtilityScreenshotGetStatus() -{ - if (currentDialogType != UTILITY_DIALOG_SCREENSHOT) - { +static int sceUtilityScreenshotGetStatus() { + if (currentDialogType != UtilityDialogType::SCREENSHOT) { DEBUG_LOG(SCEUTILITY, "sceUtilityScreenshotGetStatus(): wrong dialog type"); return SCE_ERROR_UTILITY_WRONG_TYPE; } @@ -596,13 +653,12 @@ static int sceUtilityScreenshotGetStatus() oldStatus = status; WARN_LOG(SCEUTILITY, "%08x=sceUtilityScreenshotGetStatus()", status); } + CleanupDialogThreads(); return status; } -static int sceUtilityScreenshotContStart(u32 paramAddr) -{ - if (currentDialogType != UTILITY_DIALOG_SCREENSHOT) - { +static int sceUtilityScreenshotContStart(u32 paramAddr) { + if (currentDialogType != UtilityDialogType::SCREENSHOT) { WARN_LOG(SCEUTILITY, "sceUtilityScreenshotContStart(): wrong dialog type"); return SCE_ERROR_UTILITY_WRONG_TYPE; } @@ -612,23 +668,20 @@ static int sceUtilityScreenshotContStart(u32 paramAddr) return ret; } -static int sceUtilityGamedataInstallInitStart(u32 paramsAddr) -{ - if (currentDialogActive && currentDialogType != UTILITY_DIALOG_GAMEDATAINSTALL) - { +static int sceUtilityGamedataInstallInitStart(u32 paramsAddr) { + if (currentDialogActive && currentDialogType != UtilityDialogType::GAMEDATAINSTALL) { WARN_LOG(SCEUTILITY, "sceUtilityGamedataInstallInitStart(%08x): wrong dialog type", paramsAddr); return SCE_ERROR_UTILITY_WRONG_TYPE; } - ActivateDialog(UTILITY_DIALOG_GAMEDATAINSTALL); + ActivateDialog(UtilityDialogType::GAMEDATAINSTALL); int ret = gamedataInstallDialog.Init(paramsAddr); INFO_LOG(SCEUTILITY, "%08x=sceUtilityGamedataInstallInitStart(%08x)",ret,paramsAddr); return ret; } static int sceUtilityGamedataInstallShutdownStart() { - if (currentDialogType != UTILITY_DIALOG_GAMEDATAINSTALL) - { + if (currentDialogType != UtilityDialogType::GAMEDATAINSTALL) { WARN_LOG(SCEUTILITY, "sceUtilityGamedataInstallShutdownStart(): wrong dialog type"); return SCE_ERROR_UTILITY_WRONG_TYPE; } @@ -639,8 +692,7 @@ static int sceUtilityGamedataInstallShutdownStart() { } static int sceUtilityGamedataInstallUpdate(int animSpeed) { - if (currentDialogType != UTILITY_DIALOG_GAMEDATAINSTALL) - { + if (currentDialogType != UtilityDialogType::GAMEDATAINSTALL) { WARN_LOG(SCEUTILITY, "sceUtilityGamedataInstallUpdate(%i): wrong dialog type", animSpeed); return SCE_ERROR_UTILITY_WRONG_TYPE; } @@ -650,10 +702,8 @@ static int sceUtilityGamedataInstallUpdate(int animSpeed) { return ret; } -static int sceUtilityGamedataInstallGetStatus() -{ - if (currentDialogType != UTILITY_DIALOG_GAMEDATAINSTALL) - { +static int sceUtilityGamedataInstallGetStatus() { + if (currentDialogType != UtilityDialogType::GAMEDATAINSTALL) { // This is called incorrectly all the time by some games. So let's not bother warning. // WARN_LOG(SCEUTILITY, "sceUtilityGamedataInstallGetStatus(): wrong dialog type"); return SCE_ERROR_UTILITY_WRONG_TYPE; @@ -661,13 +711,12 @@ static int sceUtilityGamedataInstallGetStatus() int status = gamedataInstallDialog.GetStatus(); DEBUG_LOG(SCEUTILITY, "%08x=sceUtilityGamedataInstallGetStatus()", status); + CleanupDialogThreads(); return status; } -static int sceUtilityGamedataInstallAbort() -{ - if (currentDialogType != UTILITY_DIALOG_GAMEDATAINSTALL) - { +static int sceUtilityGamedataInstallAbort() { + if (currentDialogType != UtilityDialogType::GAMEDATAINSTALL) { WARN_LOG(SCEUTILITY, "sceUtilityGamedataInstallAbort(): wrong dialog type"); return SCE_ERROR_UTILITY_WRONG_TYPE; } @@ -829,10 +878,8 @@ static int sceUtilityStoreCheckoutGetStatus() return 0; } -static int sceUtilityGameSharingShutdownStart() -{ - if (currentDialogType != UTILITY_DIALOG_GAMESHARING) - { +static int sceUtilityGameSharingShutdownStart() { + if (currentDialogType != UtilityDialogType::GAMESHARING) { WARN_LOG(SCEUTILITY, "sceUtilityGameSharingShutdownStart(): wrong dialog type"); return SCE_ERROR_UTILITY_WRONG_TYPE; } @@ -842,23 +889,19 @@ static int sceUtilityGameSharingShutdownStart() return 0; } -static int sceUtilityGameSharingInitStart(u32 paramsPtr) -{ - if (currentDialogActive && currentDialogType != UTILITY_DIALOG_GAMESHARING) - { +static int sceUtilityGameSharingInitStart(u32 paramsPtr) { + if (currentDialogActive && currentDialogType != UtilityDialogType::GAMESHARING) { WARN_LOG(SCEUTILITY, "sceUtilityGameSharingInitStart(%08x)", paramsPtr); return SCE_ERROR_UTILITY_WRONG_TYPE; } - ActivateDialog(UTILITY_DIALOG_GAMESHARING); + ActivateDialog(UtilityDialogType::GAMESHARING); ERROR_LOG_REPORT(SCEUTILITY, "UNIMPL sceUtilityGameSharingInitStart(%08x)", paramsPtr); return 0; } -static int sceUtilityGameSharingUpdate(int animSpeed) -{ - if (currentDialogType != UTILITY_DIALOG_GAMESHARING) - { +static int sceUtilityGameSharingUpdate(int animSpeed) { + if (currentDialogType != UtilityDialogType::GAMESHARING) { WARN_LOG(SCEUTILITY, "sceUtilityGameSharingUpdate(%i): wrong dialog type", animSpeed); return SCE_ERROR_UTILITY_WRONG_TYPE; } @@ -867,15 +910,14 @@ static int sceUtilityGameSharingUpdate(int animSpeed) return 0; } -static int sceUtilityGameSharingGetStatus() -{ - if (currentDialogType != UTILITY_DIALOG_GAMESHARING) - { +static int sceUtilityGameSharingGetStatus() { + if (currentDialogType != UtilityDialogType::GAMESHARING) { DEBUG_LOG(SCEUTILITY, "sceUtilityGameSharingGetStatus(): wrong dialog type"); return SCE_ERROR_UTILITY_WRONG_TYPE; } ERROR_LOG(SCEUTILITY, "UNIMPL sceUtilityGameSharingGetStatus()"); + CleanupDialogThreads(); return 0; } @@ -1038,6 +1080,10 @@ const HLEFunction sceUtility[] = {0X70267ADF, nullptr, "sceUtility_70267ADF", '?', "" }, {0XECE1D3E5, nullptr, "sceUtility_ECE1D3E5", '?', "" }, {0XEF3582B2, nullptr, "sceUtility_EF3582B2", '?', "" }, + + // Fake functions for PPSSPP's use. + {0xC0DE0001, &WrapI_I, "__UtilityFinishDialog", 'i', "i" }, + {0xC0DE0002, &WrapI_I, "__UtilityWorkUs", 'i', "i" }, }; void Register_sceUtility() diff --git a/Core/HLE/sceUtility.h b/Core/HLE/sceUtility.h index 6d0be037bf..1cc4798a0a 100644 --- a/Core/HLE/sceUtility.h +++ b/Core/HLE/sceUtility.h @@ -79,8 +79,21 @@ class PointerWrap; #define PSP_SYSTEMPARAM_BUTTON_CIRCLE 0 #define PSP_SYSTEMPARAM_BUTTON_CROSS 1 +enum class UtilityDialogType { + NONE, + SAVEDATA, + MSG, + OSK, + NET, + SCREENSHOT, + GAMESHARING, + GAMEDATAINSTALL, +}; + void __UtilityInit(); void __UtilityDoState(PointerWrap &p); void __UtilityShutdown(); +void UtilityDialogShutdown(UtilityDialogType type, int delayUs, int priority); + void Register_sceUtility(); diff --git a/Core/HW/BufferQueue.cpp b/Core/HW/BufferQueue.cpp index 9fbffe0c65..19dc2abd00 100644 --- a/Core/HW/BufferQueue.cpp +++ b/Core/HW/BufferQueue.cpp @@ -20,7 +20,7 @@ #include "Core/HW/BufferQueue.h" void BufferQueue::DoState(PointerWrap &p) { - auto s = p.Section("BufferQueue", 0, 1); + auto s = p.Section("BufferQueue", 0, 2); Do(p, bufQueueSize); Do(p, start); @@ -31,5 +31,12 @@ void BufferQueue::DoState(PointerWrap &p) { if (s >= 1) { Do(p, ptsMarks); + } else { + ptsMarks.clear(); + } + if (s >= 2) { + Do(p, filled); + } else { + filled = calcQueueSize(); } } diff --git a/Core/HW/BufferQueue.h b/Core/HW/BufferQueue.h index b587b29498..6b950e9071 100644 --- a/Core/HW/BufferQueue.h +++ b/Core/HW/BufferQueue.h @@ -18,12 +18,13 @@ #pragma once #include +#include #include +#include "Common/Log.h" #include "Common/Serialize/Serializer.h" struct BufferQueue { BufferQueue(int size = 0x20000) { - bufQueue = 0; alloc(size); } @@ -33,28 +34,23 @@ struct BufferQueue { } bool alloc(int size) { - if (size < 0) - return false; + _assert_(size > 0); if (bufQueue) delete [] bufQueue; bufQueue = new unsigned char[size]; - start = 0; - end = 0; bufQueueSize = size; + clear(); return true; } void clear() { start = 0; end = 0; + filled = 0; } inline int getQueueSize() { - if (end >= start) { - return end - start; - } else { - return bufQueueSize + end - start; - } + return filled; } inline int getRemainSize() { @@ -62,49 +58,56 @@ struct BufferQueue { } bool push(const unsigned char *buf, int addsize, s64 pts = 0) { - int queuesz = getQueueSize(); - int space = bufQueueSize - queuesz; + int space = getRemainSize(); if (space < addsize || addsize < 0) return false; savePts(pts); if (end + addsize <= bufQueueSize) { + // If end is before start, there's enough space. Otherwise, we're nearing the queue size. memcpy(bufQueue + end, buf, addsize); end += addsize; + if (end == bufQueueSize) + end = 0; } else { + // Time to wrap end. Fill what remains, then fill before start. + _assert_(end >= start); int firstSize = bufQueueSize - end; memcpy(bufQueue + end, buf, firstSize); memcpy(bufQueue, buf + firstSize, addsize - firstSize); end = addsize - firstSize; } + filled += addsize; + verifyQueueSize(); return true; } - int pop_front(unsigned char *buf, int wantedsize, s64 *pts = NULL) { + int pop_front(unsigned char *buf, int wantedsize, s64 *pts = nullptr) { if (wantedsize <= 0) return 0; int bytesgot = getQueueSize(); if (wantedsize < bytesgot) bytesgot = wantedsize; - if (pts != NULL) { + if (pts != nullptr) { *pts = findPts(bytesgot); } + + int firstSize = bufQueueSize - start; if (buf) { - if (start + bytesgot <= bufQueueSize) { + if (bytesgot <= firstSize) { memcpy(buf, bufQueue + start, bytesgot); - start += bytesgot; } else { - int firstSize = bufQueueSize - start; memcpy(buf, bufQueue + start, firstSize); memcpy(buf + firstSize, bufQueue, bytesgot - firstSize); - start = bytesgot - firstSize; } - } else { - int firstSize = bufQueueSize - start; - if (start + bytesgot <= bufQueueSize) - start += bytesgot; - else - start = bytesgot - firstSize; } + if (bytesgot <= firstSize) + start += bytesgot; + else + start = bytesgot - firstSize; + if (start == bufQueueSize) + start = 0; + filled -= bytesgot; + verifyQueueSize(); return bytesgot; } @@ -114,12 +117,12 @@ struct BufferQueue { int bytesgot = getQueueSize(); if (wantedsize < bytesgot) bytesgot = wantedsize; - if (start + bytesgot <= bufQueueSize) { + int firstSize = bufQueueSize - start; + if (bytesgot <= firstSize) { memcpy(buf, bufQueue + start, bytesgot); } else { - int size = bufQueueSize - start; - memcpy(buf, bufQueue + start, size); - memcpy(buf + size, bufQueue, bytesgot - size); + memcpy(buf, bufQueue + start, firstSize); + memcpy(buf + firstSize, bufQueue, bytesgot - firstSize); } return bytesgot; } @@ -150,18 +153,35 @@ private: u64 pts = findPts(earliest, latest); // If it wraps around, we have to look at the other half too. - if (pts == 0 && start + packetSize > bufQueueSize) { + if (start + packetSize > bufQueueSize) { earliest = ptsMarks.begin(); latest = ptsMarks.lower_bound(start + packetSize - bufQueueSize); - return findPts(earliest, latest); + // This also clears the range, so we always call on wrap. + u64 latePts = findPts(earliest, latest); + if (pts == 0) + pts = latePts; } return pts; } - unsigned char* bufQueue; - int start, end; - int bufQueueSize; + inline int calcQueueSize() { + if (end < start) { + return bufQueueSize + end - start; + } + return end - start; + } + + inline void verifyQueueSize() { + _assert_(calcQueueSize() == filled || (end == start && filled == bufQueueSize)); + } + + uint8_t *bufQueue = nullptr; + // Model: end may be less than start, indicating the space between end and start is free. + // If end equals start, we're empty. + int start = 0, end = 0; + int filled = 0; + int bufQueueSize = 0; std::map ptsMarks; }; diff --git a/Core/HW/Camera.cpp b/Core/HW/Camera.cpp index 415ac0a190..0339fe4d67 100644 --- a/Core/HW/Camera.cpp +++ b/Core/HW/Camera.cpp @@ -15,6 +15,7 @@ // Official git repository and contact information can be found at // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. +#include "ppsspp_config.h" #include "Camera.h" #include "Core/Config.h" diff --git a/Core/HW/MediaEngine.cpp b/Core/HW/MediaEngine.cpp index 13f4b4a1ec..3c168a817e 100644 --- a/Core/HW/MediaEngine.cpp +++ b/Core/HW/MediaEngine.cpp @@ -17,7 +17,7 @@ #include "Common/Serialize/SerializeFuncs.h" #include "Core/Config.h" -#include "Core/Debugger/Breakpoints.h" +#include "Core/Debugger/MemBlockInfo.h" #include "Core/HW/MediaEngine.h" #include "Core/MemMap.h" #include "Core/MIPS/MIPS.h" @@ -151,7 +151,6 @@ MediaEngine::MediaEngine(): m_pdata(0) { m_desHeight = 0; m_decodingsize = 0; m_bufSize = 0x2000; - m_videopts = 0; m_pdata = 0; m_demux = 0; m_audioContext = 0; @@ -184,7 +183,7 @@ void MediaEngine::closeMedia() { } void MediaEngine::DoState(PointerWrap &p) { - auto s = p.Section("MediaEngine", 1, 6); + auto s = p.Section("MediaEngine", 1, 7); if (!s) return; @@ -226,6 +225,11 @@ void MediaEngine::DoState(PointerWrap &p) { m_demux->DoState(p); Do(p, m_videopts); + if (s >= 7) { + Do(p, m_lastPts); + } else { + m_lastPts = m_videopts; + } Do(p, m_audiopts); if (s >= 2) { @@ -293,9 +297,8 @@ bool MediaEngine::SetupStreams() { for (int i = videoStreamNum + 1; i < m_expectedVideoStreams; i++) { addVideoStream(i); } - - #endif + return true; } @@ -327,8 +330,10 @@ bool MediaEngine::openContext(bool keepReadPos) { av_dict_free(&open_opt); if (!SetupStreams() || PSP_CoreParameter().compat.flags().HackFixVideo) { - // Fallback to old behavior. - if (avformat_find_stream_info(m_pFormatCtx, NULL) < 0) { + // Fallback to old behavior. Reads too much and corrupts when game doesn't read fast enough. + // SetupStreams sometimes work for newer FFmpeg 3.1+ now, but sometimes framerate is missing. + WARN_LOG_REPORT_ONCE(setupStreams, ME, "Failed to read valid video stream data from header"); + if (avformat_find_stream_info(m_pFormatCtx, nullptr) < 0) { closeContext(); return false; } @@ -341,13 +346,19 @@ bool MediaEngine::openContext(bool keepReadPos) { if (m_videoStream == -1) { // Find the first video stream - for(int i = 0; i < (int)m_pFormatCtx->nb_streams; i++) { - if(m_pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) { + for (int i = 0; i < (int)m_pFormatCtx->nb_streams; i++) { + const AVStream *s = m_pFormatCtx->streams[i]; +#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(57, 33, 100) + AVMediaType type = s->codecpar->codec_type; +#else + AVMediaType type = s->codec->codec_type; +#endif + if (type == AVMEDIA_TYPE_VIDEO) { m_videoStream = i; break; } } - if(m_videoStream == -1) + if (m_videoStream == -1) return false; } @@ -378,8 +389,13 @@ void MediaEngine::closeContext() av_free(m_pIOContext->buffer); if (m_pIOContext) av_free(m_pIOContext); - for (auto it = m_pCodecCtxs.begin(), end = m_pCodecCtxs.end(); it != end; ++it) - avcodec_close(it->second); + for (auto it : m_pCodecCtxs) { +#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(57, 33, 100) + avcodec_free_context(&it.second); +#else + avcodec_close(it.second); +#endif + } m_pCodecCtxs.clear(); if (m_pFormatCtx) avformat_close_input(&m_pFormatCtx); @@ -395,6 +411,7 @@ bool MediaEngine::loadStream(const u8 *buffer, int readSize, int RingbufferSize) closeMedia(); m_videopts = 0; + m_lastPts = -1; m_audiopts = 0; m_ringbuffersize = RingbufferSize; m_pdata = new BufferQueue(RingbufferSize + 2048); @@ -428,7 +445,12 @@ bool MediaEngine::addVideoStream(int streamNum, int streamId) { streamId = PSMF_VIDEO_STREAM_ID | streamNum; stream->id = 0x00000100 | streamId; +#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(57, 33, 100) + stream->codecpar->codec_type = AVMEDIA_TYPE_VIDEO; + stream->codecpar->codec_id = AV_CODEC_ID_H264; +#else stream->request_probe = 0; +#endif stream->need_parsing = AVSTREAM_PARSE_FULL; // We could set the width here, but we don't need to. if (streamNum >= m_expectedVideoStreams) { @@ -518,21 +540,30 @@ bool MediaEngine::setVideoStream(int streamNum, bool force) { if ((u32)streamNum >= m_pFormatCtx->nb_streams) { return false; } - AVCodecContext *m_pCodecCtx = m_pFormatCtx->streams[streamNum]->codec; -#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(57,33,100) - AVCodecParameters *m_pCodecPar = m_pFormatCtx->streams[streamNum]->codecpar; - // Update from deprecated public codec context - if (avcodec_parameters_from_context(m_pCodecPar, m_pCodecCtx) < 0) { + AVStream *stream = m_pFormatCtx->streams[streamNum]; +#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(57, 33, 100) + AVCodec *pCodec = avcodec_find_decoder(stream->codecpar->codec_id); + if (!pCodec) { + WARN_LOG_REPORT(ME, "Could not find decoder for %d", (int)stream->codecpar->codec_id); return false; } -#endif - + AVCodecContext *m_pCodecCtx = avcodec_alloc_context3(pCodec); + int paramResult = avcodec_parameters_to_context(m_pCodecCtx, stream->codecpar); + if (paramResult < 0) { + WARN_LOG_REPORT(ME, "Failed to prepare context parameters: %08x", paramResult); + return false; + } +#else + AVCodecContext *m_pCodecCtx = stream->codec; // Find the decoder for the video stream AVCodec *pCodec = avcodec_find_decoder(m_pCodecCtx->codec_id); if (pCodec == nullptr) { return false; } +#endif + + m_pCodecCtx->flags |= AV_CODEC_FLAG_OUTPUT_CORRUPT | AV_CODEC_FLAG_LOW_DELAY; AVDictionary *opt = nullptr; // Allow ffmpeg to use any number of threads it wants. Without this, it doesn't use threads. @@ -662,9 +693,9 @@ bool MediaEngine::stepVideo(int videoPixelMode, bool skipFrame) { while (!bGetFrame) { bool dataEnd = av_read_frame(m_pFormatCtx, &packet) < 0; // Even if we've read all frames, some may have been re-ordered frames at the end. - // Still need to decode those, so keep calling avcodec_decode_video2(). + // Still need to decode those, so keep calling avcodec_decode_video2() / avcodec_receive_frame(). if (dataEnd || packet.stream_index == m_videoStream) { - // avcodec_decode_video2() gives us the re-ordered frames with a NULL packet. + // avcodec_decode_video2() / avcodec_send_packet() gives us the re-ordered frames with a NULL packet. #if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 12, 100) if (dataEnd) av_packet_unref(&packet); @@ -673,7 +704,22 @@ bool MediaEngine::stepVideo(int videoPixelMode, bool skipFrame) { av_free_packet(&packet); #endif +#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 48, 101) + if (packet.size != 0) + avcodec_send_packet(m_pCodecCtx, &packet); + int result = avcodec_receive_frame(m_pCodecCtx, m_pFrame); + if (result == 0) { + result = m_pFrame->pkt_size; + frameFinished = 1; + } else if (result == AVERROR(EAGAIN)) { + result = 0; + frameFinished = 0; + } else { + frameFinished = 0; + } +#else int result = avcodec_decode_video2(m_pCodecCtx, m_pFrame, &frameFinished, &packet); +#endif if (frameFinished) { if (!m_pFrameRGB) { setVideoDim(); @@ -688,10 +734,28 @@ bool MediaEngine::stepVideo(int videoPixelMode, bool skipFrame) { m_pCodecCtx->height, m_pFrameRGB->data, m_pFrameRGB->linesize); } - if (av_frame_get_best_effort_timestamp(m_pFrame) != AV_NOPTS_VALUE) - m_videopts = av_frame_get_best_effort_timestamp(m_pFrame) + av_frame_get_pkt_duration(m_pFrame) - m_firstTimeStamp; - else - m_videopts += av_frame_get_pkt_duration(m_pFrame); +#if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(55, 58, 100) + int64_t bestPts = m_pFrame->best_effort_timestamp; + int64_t ptsDuration = m_pFrame->pkt_duration; +#else + int64_t bestPts = av_frame_get_best_effort_timestamp(m_pFrame); + int64_t ptsDuration = av_frame_get_pkt_duration(m_pFrame); +#endif + if (ptsDuration == 0) { + if (m_lastPts == bestPts - m_firstTimeStamp || bestPts == AV_NOPTS_VALUE) { + // TODO: Assuming 29.97 if missing. + m_videopts += 3003; + } else { + m_videopts = bestPts - m_firstTimeStamp; + m_lastPts = m_videopts; + } + } else if (bestPts != AV_NOPTS_VALUE) { + m_videopts = bestPts + ptsDuration - m_firstTimeStamp; + m_lastPts = m_videopts; + } else { + m_videopts += ptsDuration; + m_lastPts = m_videopts; + } bGetFrame = true; } if (result <= 0 && dataEnd) { @@ -838,7 +902,7 @@ int MediaEngine::writeVideoImage(u32 bufferPtr, int frameWidth, int videoPixelMo delete [] imgbuf; } - CBreakPoints::ExecMemCheck(bufferPtr, true, videoImageSize, currentMIPS->pc); + NotifyMemInfo(MemBlockFlags::WRITE, bufferPtr, videoImageSize, "VideoDecode"); return videoImageSize; #endif // USE_FFMPEG @@ -893,7 +957,6 @@ int MediaEngine::writeVideoImageWithRange(u32 bufferPtr, int frameWidth, int vid writeVideoLineRGBA(imgbuf, data, width); data += m_desWidth * sizeof(u32); imgbuf += videoLineSize; - CBreakPoints::ExecMemCheck(bufferPtr + y * frameWidth * sizeof(u32), true, width * sizeof(u32), currentMIPS->pc); } break; @@ -903,7 +966,6 @@ int MediaEngine::writeVideoImageWithRange(u32 bufferPtr, int frameWidth, int vid writeVideoLineABGR5650(imgbuf, data, width); data += m_desWidth * sizeof(u16); imgbuf += videoLineSize; - CBreakPoints::ExecMemCheck(bufferPtr + y * frameWidth * sizeof(u16), true, width * sizeof(u16), currentMIPS->pc); } break; @@ -913,7 +975,6 @@ int MediaEngine::writeVideoImageWithRange(u32 bufferPtr, int frameWidth, int vid writeVideoLineABGR5551(imgbuf, data, width); data += m_desWidth * sizeof(u16); imgbuf += videoLineSize; - CBreakPoints::ExecMemCheck(bufferPtr + y * frameWidth * sizeof(u16), true, width * sizeof(u16), currentMIPS->pc); } break; @@ -923,7 +984,6 @@ int MediaEngine::writeVideoImageWithRange(u32 bufferPtr, int frameWidth, int vid writeVideoLineABGR4444(imgbuf, data, width); data += m_desWidth * sizeof(u16); imgbuf += videoLineSize; - CBreakPoints::ExecMemCheck(bufferPtr + y * frameWidth * sizeof(u16), true, width * sizeof(u16), currentMIPS->pc); } break; @@ -943,9 +1003,9 @@ int MediaEngine::writeVideoImageWithRange(u32 bufferPtr, int frameWidth, int vid DoSwizzleTex16((const u32 *)imgbuf, buffer, bxc, byc, videoLineSize); delete [] imgbuf; } + NotifyMemInfo(MemBlockFlags::WRITE, bufferPtr, videoImageSize, "VideoDecodeRange"); - // Account for the y offset as well. - return videoImageSize + videoLineSize * ypos; + return videoImageSize; #endif // USE_FFMPEG return 0; } @@ -1018,7 +1078,7 @@ int MediaEngine::getAudioSamples(u32 bufferPtr) { ERROR_LOG(ME, "Audio (%s) decode failed during video playback", GetCodecName(m_audioType)); } - CBreakPoints::ExecMemCheck(bufferPtr, true, outbytes, currentMIPS->pc); + NotifyMemInfo(MemBlockFlags::WRITE, bufferPtr, outbytes, "VideoDecodeAudio"); } return 0x2000; diff --git a/Core/HW/MediaEngine.h b/Core/HW/MediaEngine.h index af81ae7447..69b0ccea81 100644 --- a/Core/HW/MediaEngine.h +++ b/Core/HW/MediaEngine.h @@ -126,7 +126,8 @@ public: // TODO: Very little of this below should be public. int m_desHeight; int m_decodingsize; int m_bufSize; - s64 m_videopts; + s64 m_videopts = 0; + s64 m_lastPts = -1; BufferQueue *m_pdata; MpegDemux *m_demux; diff --git a/Core/HW/SasAudio.cpp b/Core/HW/SasAudio.cpp index 6a0a87d3af..b6f359845d 100644 --- a/Core/HW/SasAudio.cpp +++ b/Core/HW/SasAudio.cpp @@ -566,13 +566,10 @@ void SasInstance::MixVoice(SasVoice &voice) { } void SasInstance::Mix(u32 outAddr, u32 inAddr, int leftVol, int rightVol) { - int voicesPlayingCount = 0; - for (int v = 0; v < PSP_SAS_VOICES_MAX; v++) { SasVoice &voice = voices[v]; if (!voice.playing || voice.paused) continue; - voicesPlayingCount++; MixVoice(voice); } diff --git a/Core/HW/SimpleAudioDec.cpp b/Core/HW/SimpleAudioDec.cpp index bc4ea5d310..5b7c175705 100644 --- a/Core/HW/SimpleAudioDec.cpp +++ b/Core/HW/SimpleAudioDec.cpp @@ -19,6 +19,7 @@ #include "Common/Serialize/SerializeFuncs.h" #include "Core/Config.h" +#include "Core/Debugger/MemBlockInfo.h" #include "Core/HLE/FunctionWrappers.h" #include "Core/HW/SimpleAudioDec.h" #include "Core/HW/MediaEngine.h" @@ -62,8 +63,12 @@ SimpleAudio::SimpleAudio(int audioType, int sample_rate, int channels) void SimpleAudio::Init() { #ifdef USE_FFMPEG +#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(58, 18, 100) avcodec_register_all(); +#endif +#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(58, 12, 100) av_register_all(); +#endif InitFFmpeg(); frame_ = av_frame_alloc(); @@ -188,7 +193,25 @@ bool SimpleAudio::Decode(void *inbuf, int inbytes, uint8_t *outbuf, int *outbyte *outbytes = 0; srcPos = 0; +#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 48, 101) + if (inbytes != 0) { + int err = avcodec_send_packet(codecCtx_, &packet); + if (err < 0) { + ERROR_LOG(ME, "Error sending audio frame to decoder (%d bytes): %d (%08x)", inbytes, err, err); + return false; + } + } + int err = avcodec_receive_frame(codecCtx_, frame_); + int len = 0; + if (err >= 0) { + len = frame_->pkt_size; + got_frame = 1; + } else if (err != AVERROR(EAGAIN)) { + len = err; + } +#else int len = avcodec_decode_audio4(codecCtx_, frame_, &got_frame, &packet); +#endif #if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 12, 100) av_packet_unref(&packet); #else @@ -329,9 +352,8 @@ size_t AuCtx::FindNextMp3Sync() { // return output pcm size, <0 error u32 AuCtx::AuDecode(u32 pcmAddr) { - if (!Memory::IsValidAddress(pcmAddr)){ - ERROR_LOG(ME, "%s: output bufferAddress %08x is invalctx", __FUNCTION__, pcmAddr); - return -1; + if (!Memory::GetPointer(PCMBuf)) { + return hleLogError(ME, -1, "ctx output bufferAddress %08x is invalid", PCMBuf); } auto outbuf = Memory::GetPointer(PCMBuf); @@ -378,7 +400,9 @@ u32 AuCtx::AuDecode(u32 pcmAddr) { memset(outbuf + outpcmbufsize, 0, PCMBufSize - outpcmbufsize); } - Memory::Write_U32(PCMBuf, pcmAddr); + NotifyMemInfo(MemBlockFlags::WRITE, pcmAddr, outpcmbufsize, "AuDecode"); + if (pcmAddr) + Memory::Write_U32(PCMBuf, pcmAddr); return outpcmbufsize; } diff --git a/Core/HW/StereoResampler.cpp b/Core/HW/StereoResampler.cpp index 8e7f22833d..6f40d310f3 100644 --- a/Core/HW/StereoResampler.cpp +++ b/Core/HW/StereoResampler.cpp @@ -32,6 +32,7 @@ #define CONTROL_FACTOR 0.2f // in freq_shift per fifo size offset #define CONTROL_AVG 32.0f +#include "ppsspp_config.h" #include #include @@ -161,6 +162,10 @@ void StereoResampler::Clear() { memset(m_buffer, 0, m_maxBufsize * 2 * sizeof(int16_t)); } +inline int16_t MixSingleSample(int16_t s1, int16_t s2, uint16_t frac) { + return s1 + (((s2 - s1) * frac) >> 16); +} + // Executed from sound stream thread, pulling sound out of the buffer. unsigned int StereoResampler::Mix(short* samples, unsigned int numSamples, bool consider_framelimit, int sample_rate) { if (!samples) @@ -222,10 +227,8 @@ unsigned int StereoResampler::Mix(short* samples, unsigned int numSamples, bool s16 r1 = m_buffer[(indexR + 1) & INDEX_MASK]; //current s16 l2 = m_buffer[indexR2 & INDEX_MASK]; //next s16 r2 = m_buffer[(indexR2 + 1) & INDEX_MASK]; //next - int sampleL = ((l1 << 16) + (l2 - l1) * (u16)frac) >> 16; - int sampleR = ((r1 << 16) + (r2 - r1) * (u16)frac) >> 16; - samples[currentSample] = sampleL; - samples[currentSample + 1] = sampleR; + samples[currentSample] = MixSingleSample(l1, l2, (u16)frac); + samples[currentSample + 1] = MixSingleSample(r1, r2, (u16)frac); frac += ratio; indexR += 2 * (frac >> 16); frac &= 0xffff; diff --git a/Core/Instance.cpp b/Core/Instance.cpp index 43536418e3..a36aca0049 100644 --- a/Core/Instance.cpp +++ b/Core/Instance.cpp @@ -15,9 +15,10 @@ // Official git repository and contact information can be found at // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. -#include "Instance.h" +#include "ppsspp_config.h" +#include "Core/Instance.h" -#if !PPSSPP_PLATFORM(WINDOWS) && !PPSSPP_PLATFORM(ANDROID) && !defined(__LIBRETRO__) +#if !PPSSPP_PLATFORM(WINDOWS) && !PPSSPP_PLATFORM(ANDROID) && !defined(__LIBRETRO__) && !PPSSPP_PLATFORM(SWITCH) #include #include #include @@ -64,7 +65,7 @@ static bool UpdateInstanceCounter(void (*callback)(volatile InstanceInfo *)) { sizeof(InstanceInfo)); if (!buf) { - ERROR_LOG(SCENET, "Could not map view of file %s (%d).", ID_SHM_NAME, GetLastError()); + ERROR_LOG(SCENET, "Could not map view of file %s (%08x)", ID_SHM_NAME, (uint32_t)GetLastError()); return false; } @@ -79,7 +80,7 @@ static bool UpdateInstanceCounter(void (*callback)(volatile InstanceInfo *)) { UnmapViewOfFile(buf); return result; -#elif PPSSPP_PLATFORM(ANDROID) || defined(__LIBRETRO__) +#elif PPSSPP_PLATFORM(ANDROID) || defined(__LIBRETRO__) || PPSSPP_PLATFORM(SWITCH) // TODO: replace shm_open & shm_unlink with ashmem or android-shmem return false; #else @@ -136,11 +137,11 @@ void InitInstanceCounter() { DWORD lasterr = GetLastError(); if (!hIDMapFile) { - ERROR_LOG(SCENET, "Could not create %s file mapping object (%d).", ID_SHM_NAME, lasterr); + ERROR_LOG(SCENET, "Could not create %s file mapping object (%08x)", ID_SHM_NAME, (uint32_t)lasterr); PPSSPP_ID = 1; return; } -#elif PPSSPP_PLATFORM(ANDROID) || defined(__LIBRETRO__) +#elif PPSSPP_PLATFORM(ANDROID) || defined(__LIBRETRO__) || PPSSPP_PLATFORM(SWITCH) // TODO : replace shm_open & shm_unlink with ashmem or android-shmem #else // Create shared memory object @@ -177,7 +178,7 @@ void ShutdownInstanceCounter() { CloseHandle(mapLock); mapLock = nullptr; } -#elif PPSSPP_PLATFORM(ANDROID) || defined(__LIBRETRO__) +#elif PPSSPP_PLATFORM(ANDROID) || defined(__LIBRETRO__) || PPSSPP_PLATFORM(SWITCH) // Do nothing #else if (hIDMapFile >= 0) { diff --git a/Core/Loaders.cpp b/Core/Loaders.cpp index c79faa2149..f61b499cdd 100644 --- a/Core/Loaders.cpp +++ b/Core/Loaders.cpp @@ -50,7 +50,7 @@ FileLoader *ConstructFileLoader(const std::string &filename) { } for (auto &iter : factories) { - if (startsWith(iter.first, filename)) { + if (startsWith(filename, iter.first)) { return iter.second->ConstructFileLoader(filename); } } @@ -126,10 +126,11 @@ IdentifiedFileType Identify_File(FileLoader *fileLoader) { size_t readSize = fileLoader->ReadAt(0, 4, 1, &id); if (readSize != 1) { + ERROR_LOG(LOADER, "Failed to read identification bytes"); return IdentifiedFileType::ERROR_IDENTIFYING; } - u32 psar_offset = 0, psar_id = 0; + u32_le psar_offset = 0, psar_id = 0; u32 _id = id; if (!memcmp(&_id, "PK\x03\x04", 4) || !memcmp(&_id, "PK\x05\x06", 4) || !memcmp(&_id, "PK\x07\x08", 4)) { return IdentifiedFileType::ARCHIVE_ZIP; @@ -293,7 +294,7 @@ bool LoadFile(FileLoader **fileLoaderPtr, std::string *error_string) { break; case IdentifiedFileType::ERROR_IDENTIFYING: - ERROR_LOG(LOADER, "Could not read file"); + ERROR_LOG(LOADER, "Could not read file enough to identify it"); *error_string = fileLoader ? fileLoader->LatestError() : ""; if (error_string->empty()) *error_string = "Error reading file"; diff --git a/Core/Loaders.h b/Core/Loaders.h index a076cfa675..fecb941a6d 100644 --- a/Core/Loaders.h +++ b/Core/Loaders.h @@ -100,8 +100,7 @@ public: class ProxiedFileLoader : public FileLoader { public: - ProxiedFileLoader(FileLoader *backend) : backend_(backend) { - } + ProxiedFileLoader(FileLoader *backend) : backend_(backend) {} ~ProxiedFileLoader() override { // Takes ownership. delete backend_; @@ -131,6 +130,12 @@ public: std::string LatestError() const override { return backend_->LatestError(); } + size_t ReadAt(s64 absolutePos, size_t bytes, size_t count, void *data, Flags flags = Flags::NONE) override { + return backend_->ReadAt(absolutePos, bytes, count, data, flags); + } + size_t ReadAt(s64 absolutePos, size_t bytes, void *data, Flags flags = Flags::NONE) override { + return backend_->ReadAt(absolutePos, bytes, data, flags); + } protected: FileLoader *backend_; diff --git a/Core/MIPS/ARM/ArmAsm.cpp b/Core/MIPS/ARM/ArmAsm.cpp index 44176dc293..4f9fb1e2a3 100644 --- a/Core/MIPS/ARM/ArmAsm.cpp +++ b/Core/MIPS/ARM/ArmAsm.cpp @@ -204,6 +204,7 @@ void ArmJit::GenerateFixedCode() { LDR(R0, CTXREG, offsetof(MIPSState, pc)); // TODO: In practice, do we ever run code from uncached space (| 0x40000000)? If not, we can remove this BIC. BIC(R0, R0, Operand2(0xC0, 4)); // &= 0x3FFFFFFF + dispatcherFetch = GetCodePtr(); LDR(R0, MEMBASEREG, R0); AND(R1, R0, Operand2(0xFF, 4)); // rotation is to the right, in 2-bit increments. BIC(R0, R0, Operand2(0xFF, 4)); @@ -212,7 +213,7 @@ void ArmJit::GenerateFixedCode() { // IDEA - we have 26 bits, why not just use offsets from base of code? // Another idea: Shift the bloc number left by two in the op, this would let us do // LDR(R0, R9, R0); here, replacing the next instructions. -#ifdef IOS +#if PPSSPP_PLATFORM(IOS) // On iOS, R9 (JITBASEREG) is volatile. We have to reload it. MOVI2R(JITBASEREG, (u32)(uintptr_t)GetBasePtr()); #endif diff --git a/Core/MIPS/ARM/ArmCompALU.cpp b/Core/MIPS/ARM/ArmCompALU.cpp index 6088454fad..0b5bf067f8 100644 --- a/Core/MIPS/ARM/ArmCompALU.cpp +++ b/Core/MIPS/ARM/ArmCompALU.cpp @@ -20,12 +20,13 @@ #include +#include "Common/BitSet.h" +#include "Common/CPUDetect.h" +#include "Common/Data/Convert/SmallDataConvert.h" #include "Core/MIPS/MIPS.h" #include "Core/MIPS/MIPSCodeUtils.h" #include "Core/MIPS/ARM/ArmJit.h" #include "Core/MIPS/ARM/ArmRegCache.h" -#include "Common/CPUDetect.h" -#include "Common/BitSet.h" using namespace MIPSAnalyst; @@ -75,9 +76,9 @@ namespace MIPSComp void ArmJit::Comp_IType(MIPSOpcode op) { CONDITIONAL_DISABLE(ALU_IMM); - s32 simm = (s32)(s16)(op & 0xFFFF); // sign extension u32 uimm = op & 0xFFFF; - u32 suimm = (u32)(s32)simm; + s32 simm = SignExtend16ToS32(op); + u32 suimm = SignExtend16ToU32(op); MIPSGPReg rt = _RT; MIPSGPReg rs = _RS; @@ -601,9 +602,9 @@ namespace MIPSComp return; switch ((op >> 6) & 31) { - case 16: // seb // R(rd) = (u32)(s32)(s8)(u8)R(rt); + case 16: // seb // R(rd) = SignExtend8ToU32(R(rt)); if (gpr.IsImm(rt)) { - gpr.SetImm(rd, (s32)(s8)(u8)gpr.GetImm(rt)); + gpr.SetImm(rd, SignExtend8ToU32(gpr.GetImm(rt))); return; } gpr.MapDirtyIn(rd, rt); @@ -612,7 +613,7 @@ namespace MIPSComp case 24: // seh if (gpr.IsImm(rt)) { - gpr.SetImm(rd, (s32)(s16)(u16)gpr.GetImm(rt)); + gpr.SetImm(rd, SignExtend16ToU32(gpr.GetImm(rt))); return; } gpr.MapDirtyIn(rd, rt); diff --git a/Core/MIPS/ARM/ArmCompBranch.cpp b/Core/MIPS/ARM/ArmCompBranch.cpp index f05bd96f30..05e0fa01f9 100644 --- a/Core/MIPS/ARM/ArmCompBranch.cpp +++ b/Core/MIPS/ARM/ArmCompBranch.cpp @@ -18,6 +18,7 @@ #include "ppsspp_config.h" #if PPSSPP_ARCH(ARM) +#include "Common/Data/Convert/SmallDataConvert.h" #include "Common/Profiler/Profiler.h" #include "Core/Config.h" @@ -47,8 +48,9 @@ #define _SA MIPS_GET_SA(op) #define _POS ((op>> 6) & 0x1F) #define _SIZE ((op>>11) & 0x1F) -#define _IMM16 (signed short)(op & 0xFFFF) #define _IMM26 (op & 0x03FFFFFF) +#define TARGET16 ((int)(SignExtend16ToU32(op) << 2)) +#define TARGET26 (_IMM26 << 2) #define LOOPOPTIMIZATION 0 @@ -69,7 +71,7 @@ void ArmJit::BranchRSRTComp(MIPSOpcode op, CCFlags cc, bool likely) ERROR_LOG_REPORT(JIT, "Branch in RSRTComp delay slot at %08x in block starting at %08x", GetCompilerPC(), js.blockStart); return; } - int offset = _IMM16 << 2; + int offset = TARGET16; MIPSGPReg rt = _RT; MIPSGPReg rs = _RS; u32 targetAddr = GetCompilerPC() + offset + 4; @@ -182,7 +184,7 @@ void ArmJit::BranchRSZeroComp(MIPSOpcode op, CCFlags cc, bool andLink, bool like ERROR_LOG_REPORT(JIT, "Branch in RSZeroComp delay slot at %08x in block starting at %08x", GetCompilerPC(), js.blockStart); return; } - int offset = _IMM16 << 2; + int offset = TARGET16; MIPSGPReg rs = _RS; u32 targetAddr = GetCompilerPC() + offset + 4; @@ -328,7 +330,7 @@ void ArmJit::BranchFPFlag(MIPSOpcode op, CCFlags cc, bool likely) ERROR_LOG_REPORT(JIT, "Branch in FPFlag delay slot at %08x in block starting at %08x", GetCompilerPC(), js.blockStart); return; } - int offset = _IMM16 << 2; + int offset = TARGET16; u32 targetAddr = GetCompilerPC() + offset + 4; MIPSOpcode delaySlotOp = GetOffsetInstruction(1); @@ -386,7 +388,7 @@ void ArmJit::BranchVFPUFlag(MIPSOpcode op, CCFlags cc, bool likely) ERROR_LOG_REPORT(JIT, "Branch in VFPU delay slot at %08x in block starting at %08x", GetCompilerPC(), js.blockStart); return; } - int offset = _IMM16 << 2; + int offset = TARGET16; u32 targetAddr = GetCompilerPC() + offset + 4; MIPSOpcode delaySlotOp = GetOffsetInstruction(1); @@ -456,7 +458,7 @@ void ArmJit::Comp_Jump(MIPSOpcode op) { ERROR_LOG_REPORT(JIT, "Branch in Jump delay slot at %08x in block starting at %08x", GetCompilerPC(), js.blockStart); return; } - u32 off = _IMM26 << 2; + u32 off = TARGET26; u32 targetAddr = (GetCompilerPC() & 0xF0000000) | off; // Might be a stubbed address or something? diff --git a/Core/MIPS/ARM/ArmCompVFPU.cpp b/Core/MIPS/ARM/ArmCompVFPU.cpp index 4291f694f2..d9056c8238 100644 --- a/Core/MIPS/ARM/ArmCompVFPU.cpp +++ b/Core/MIPS/ARM/ArmCompVFPU.cpp @@ -19,6 +19,8 @@ #if PPSSPP_ARCH(ARM) #include +#include "Common/CPUDetect.h" +#include "Common/Data/Convert/SmallDataConvert.h" #include "Common/Math/math_util.h" #include "Core/Compatibility.h" @@ -30,7 +32,6 @@ #include "Core/MIPS/MIPSTables.h" #include "Core/MIPS/MIPSAnalyst.h" #include "Core/MIPS/MIPSCodeUtils.h" -#include "Common/CPUDetect.h" #include "Core/MIPS/ARM/ArmJit.h" #include "Core/MIPS/ARM/ArmRegCache.h" @@ -2087,7 +2088,7 @@ namespace MIPSComp u8 dreg; GetVectorRegs(&dreg, V_Single, _VT); - s32 imm = (s32)(s16)(u16)(op & 0xFFFF); + s32 imm = SignExtend16ToS32(op); fpr.MapRegV(dreg, MAP_DIRTY | MAP_NOINIT); MOVI2F(fpr.V(dreg), (float)imm, SCRATCHREG1); diff --git a/Core/MIPS/ARM/ArmCompVFPUNEON.cpp b/Core/MIPS/ARM/ArmCompVFPUNEON.cpp index 5fd30516de..816b9e72ab 100644 --- a/Core/MIPS/ARM/ArmCompVFPUNEON.cpp +++ b/Core/MIPS/ARM/ArmCompVFPUNEON.cpp @@ -30,6 +30,7 @@ #include +#include "Common/Data/Convert/SmallDataConvert.h" #include "Common/Math/math_util.h" #include "Common/CPUDetect.h" @@ -1348,7 +1349,7 @@ void ArmJit::CompNEON_Viim(MIPSOpcode op) { DestARMReg vt = NEONMapPrefixD(_VT, V_Single, MAP_NOINIT | MAP_DIRTY); - s32 imm = (s32)(s16)(u16)(op & 0xFFFF); + s32 imm = SignExtend16ToS32(op); // TODO: Optimize for low registers. MOVI2F(S0, (float)imm, R0); VMOV_neon(vt.rd, D0); diff --git a/Core/MIPS/ARM/ArmJit.cpp b/Core/MIPS/ARM/ArmJit.cpp index 653e1afe4c..d8557da697 100644 --- a/Core/MIPS/ARM/ArmJit.cpp +++ b/Core/MIPS/ARM/ArmJit.cpp @@ -104,7 +104,7 @@ namespace MIPSComp using namespace ArmGen; using namespace ArmJitConstants; -ArmJit::ArmJit(MIPSState *mips) : blocks(mips, this), gpr(mips, &js, &jo), fpr(mips, &js, &jo), mips_(mips) { +ArmJit::ArmJit(MIPSState *mipsState) : blocks(mipsState, this), gpr(mipsState, &js, &jo), fpr(mipsState, &js, &jo), mips_(mipsState) { logBlocks = 0; dontLogBlocks = 0; blocks.Init(); @@ -476,7 +476,7 @@ void ArmJit::UnlinkBlock(u8 *checkedEntry, u32 originalAddress) { } bool ArmJit::ReplaceJalTo(u32 dest) { -#ifdef ARM +#if PPSSPP_ARCH(ARM) const ReplacementTableEntry *entry = nullptr; u32 funcSize = 0; if (!CanReplaceJalTo(dest, &entry, &funcSize)) { diff --git a/Core/MIPS/ARM/ArmJit.h b/Core/MIPS/ARM/ArmJit.h index 50322a65bc..829f53ef7d 100644 --- a/Core/MIPS/ARM/ArmJit.h +++ b/Core/MIPS/ARM/ArmJit.h @@ -35,7 +35,7 @@ namespace MIPSComp { class ArmJit : public ArmGen::ARMXCodeBlock, public JitInterface, public MIPSFrontendInterface { public: - ArmJit(MIPSState *mips); + ArmJit(MIPSState *mipsState); virtual ~ArmJit(); void DoState(PointerWrap &p) override; @@ -184,6 +184,9 @@ public: const u8 *GetDispatcher() const override { return dispatcher; } + bool IsAtDispatchFetch(const u8 *ptr) const override { + return ptr == dispatcherFetch; + } void LinkBlock(u8 *exitPoint, const u8 *checkedEntry) override; void UnlinkBlock(u8 *checkedEntry, u32 originalAddress) override; @@ -311,6 +314,7 @@ public: const u8 *dispatcherCheckCoreState; const u8 *dispatcherPCInR0; const u8 *dispatcher; + const u8 *dispatcherFetch; const u8 *dispatcherNoCheck; const u8 *restoreRoundingMode; diff --git a/Core/MIPS/ARM/ArmRegCache.cpp b/Core/MIPS/ARM/ArmRegCache.cpp index 8431caadf4..61b16b5c53 100644 --- a/Core/MIPS/ARM/ArmRegCache.cpp +++ b/Core/MIPS/ARM/ArmRegCache.cpp @@ -32,7 +32,7 @@ using namespace ArmGen; using namespace ArmJitConstants; -ArmRegCache::ArmRegCache(MIPSState *mips, MIPSComp::JitState *js, MIPSComp::JitOptions *jo) : mips_(mips), js_(js), jo_(jo) { +ArmRegCache::ArmRegCache(MIPSState *mipsState, MIPSComp::JitState *js, MIPSComp::JitOptions *jo) : mips_(mipsState), js_(js), jo_(jo) { } void ArmRegCache::Init(ARMXEmitter *emitter) { diff --git a/Core/MIPS/ARM/ArmRegCache.h b/Core/MIPS/ARM/ArmRegCache.h index 34dec9d779..bd85cc10b2 100644 --- a/Core/MIPS/ARM/ArmRegCache.h +++ b/Core/MIPS/ARM/ArmRegCache.h @@ -84,7 +84,7 @@ namespace MIPSComp { class ArmRegCache { public: - ArmRegCache(MIPSState *mips, MIPSComp::JitState *js, MIPSComp::JitOptions *jo); + ArmRegCache(MIPSState *mipsState, MIPSComp::JitState *js, MIPSComp::JitOptions *jo); ~ArmRegCache() {} void Init(ArmGen::ARMXEmitter *emitter); diff --git a/Core/MIPS/ARM/ArmRegCacheFPU.cpp b/Core/MIPS/ARM/ArmRegCacheFPU.cpp index 21001b2b58..c939e4840a 100644 --- a/Core/MIPS/ARM/ArmRegCacheFPU.cpp +++ b/Core/MIPS/ARM/ArmRegCacheFPU.cpp @@ -27,7 +27,7 @@ using namespace ArmGen; using namespace ArmJitConstants; -ArmRegCacheFPU::ArmRegCacheFPU(MIPSState *mips, MIPSComp::JitState *js, MIPSComp::JitOptions *jo) : mips_(mips), js_(js), jo_(jo), vr(mr + 32), initialReady(false) { +ArmRegCacheFPU::ArmRegCacheFPU(MIPSState *mipsState, MIPSComp::JitState *js, MIPSComp::JitOptions *jo) : mips_(mipsState), js_(js), jo_(jo), vr(mr + 32) { if (cpu_info.bNEON) { numARMFpuReg_ = 32; } else { @@ -324,7 +324,6 @@ void ArmRegCacheFPU::FlushArmReg(ARMReg r) { } else if (r >= D0 && r <= D31) { // TODO: Convert to S regs and flush them individually. } else if (r >= Q0 && r <= Q15) { - int quad = r - Q0; QFlush(r); } } @@ -732,8 +731,6 @@ void ArmRegCacheFPU::QFlush(int quad) { int ArmRegCacheFPU::QGetFreeQuad(int start, int count, const char *reason) { // Search for a free quad. A quad is free if the first register in it is free. - int quad = -1; - for (int i = 0; i < count; i++) { int q = (i + start) & 15; diff --git a/Core/MIPS/ARM/ArmRegCacheFPU.h b/Core/MIPS/ARM/ArmRegCacheFPU.h index f14dc4bad6..327ef6419a 100644 --- a/Core/MIPS/ARM/ArmRegCacheFPU.h +++ b/Core/MIPS/ARM/ArmRegCacheFPU.h @@ -79,7 +79,7 @@ namespace MIPSComp { class ArmRegCacheFPU { public: - ArmRegCacheFPU(MIPSState *mips, MIPSComp::JitState *js, MIPSComp::JitOptions *jo); + ArmRegCacheFPU(MIPSState *mipsState, MIPSComp::JitState *js, MIPSComp::JitOptions *jo); ~ArmRegCacheFPU() {} void Init(ArmGen::ARMXEmitter *emitter); @@ -206,7 +206,7 @@ private: FPURegMIPS *vr; bool pendingFlush; - bool initialReady; + bool initialReady = false; FPURegARM arInitial[MAX_ARMFPUREG]; FPURegMIPS mrInitial[NUM_MIPSFPUREG]; }; diff --git a/Core/MIPS/ARM64/Arm64Asm.cpp b/Core/MIPS/ARM64/Arm64Asm.cpp index b95b8e54b4..bca3f535e1 100644 --- a/Core/MIPS/ARM64/Arm64Asm.cpp +++ b/Core/MIPS/ARM64/Arm64Asm.cpp @@ -250,6 +250,7 @@ void Arm64Jit::GenerateFixedCode(const JitOptions &jo) { #ifdef MASKED_PSP_MEMORY ANDI2R(SCRATCH1, SCRATCH1, 0x3FFFFFFF); #endif + dispatcherFetch = GetCodePtr(); LDR(SCRATCH1, MEMBASEREG, SCRATCH1_64); LSR(SCRATCH2, SCRATCH1, 24); // or UBFX(SCRATCH2, SCRATCH1, 24, 8) ANDI2R(SCRATCH1, SCRATCH1, 0x00FFFFFF); diff --git a/Core/MIPS/ARM64/Arm64CompALU.cpp b/Core/MIPS/ARM64/Arm64CompALU.cpp index a4e5ea25e9..afb8d2b4a8 100644 --- a/Core/MIPS/ARM64/Arm64CompALU.cpp +++ b/Core/MIPS/ARM64/Arm64CompALU.cpp @@ -22,6 +22,7 @@ #include "Common/BitSet.h" #include "Common/CPUDetect.h" +#include "Common/Data/Convert/SmallDataConvert.h" #include "Core/MIPS/MIPS.h" #include "Core/MIPS/MIPSCodeUtils.h" #include "Core/MIPS/ARM64/Arm64Jit.h" @@ -72,9 +73,9 @@ void Arm64Jit::CompImmLogic(MIPSGPReg rs, MIPSGPReg rt, u32 uimm, void (ARM64XEm void Arm64Jit::Comp_IType(MIPSOpcode op) { CONDITIONAL_DISABLE(ALU_IMM); - s32 simm = (s32)(s16)(op & 0xFFFF); // sign extension u32 uimm = op & 0xFFFF; - u32 suimm = (u32)(s32)simm; + s32 simm = SignExtend16ToS32(op); + u32 suimm = SignExtend16ToU32(op); MIPSGPReg rt = _RT; MIPSGPReg rs = _RS; @@ -488,9 +489,9 @@ void Arm64Jit::Comp_Allegrex(MIPSOpcode op) { return; switch ((op >> 6) & 31) { - case 16: // seb // R(rd) = (u32)(s32)(s8)(u8)R(rt); + case 16: // seb // R(rd) = SignExtend8ToU32(R(rt)); if (gpr.IsImm(rt)) { - gpr.SetImm(rd, (s32)(s8)(u8)gpr.GetImm(rt)); + gpr.SetImm(rd, SignExtend8ToU32(gpr.GetImm(rt))); return; } gpr.MapDirtyIn(rd, rt); @@ -499,7 +500,7 @@ void Arm64Jit::Comp_Allegrex(MIPSOpcode op) { case 24: // seh if (gpr.IsImm(rt)) { - gpr.SetImm(rd, (s32)(s16)(u16)gpr.GetImm(rt)); + gpr.SetImm(rd, SignExtend16ToU32(gpr.GetImm(rt))); return; } gpr.MapDirtyIn(rd, rt); diff --git a/Core/MIPS/ARM64/Arm64CompBranch.cpp b/Core/MIPS/ARM64/Arm64CompBranch.cpp index 889ac212c7..73d7b85722 100644 --- a/Core/MIPS/ARM64/Arm64CompBranch.cpp +++ b/Core/MIPS/ARM64/Arm64CompBranch.cpp @@ -18,6 +18,7 @@ #include "ppsspp_config.h" #if PPSSPP_ARCH(ARM64) +#include "Common/Data/Convert/SmallDataConvert.h" #include "Common/Profiler/Profiler.h" #include "Core/Config.h" @@ -47,8 +48,9 @@ #define _SA MIPS_GET_SA(op) #define _POS ((op>> 6) & 0x1F) #define _SIZE ((op>>11) & 0x1F) -#define _IMM16 (signed short)(op & 0xFFFF) #define _IMM26 (op & 0x03FFFFFF) +#define TARGET16 ((int)(SignExtend16ToU32(op) << 2)) +#define TARGET26 (_IMM26 << 2) #define LOOPOPTIMIZATION 0 @@ -69,7 +71,7 @@ void Arm64Jit::BranchRSRTComp(MIPSOpcode op, CCFlags cc, bool likely) ERROR_LOG_REPORT(JIT, "Branch in RSRTComp delay slot at %08x in block starting at %08x", GetCompilerPC(), js.blockStart); return; } - int offset = _IMM16 << 2; + int offset = TARGET16; MIPSGPReg rt = _RT; MIPSGPReg rs = _RS; u32 targetAddr = GetCompilerPC() + offset + 4; @@ -200,7 +202,7 @@ void Arm64Jit::BranchRSZeroComp(MIPSOpcode op, CCFlags cc, bool andLink, bool li ERROR_LOG_REPORT(JIT, "Branch in RSZeroComp delay slot at %08x in block starting at %08x", GetCompilerPC(), js.blockStart); return; } - int offset = _IMM16 << 2; + int offset = TARGET16; MIPSGPReg rs = _RS; u32 targetAddr = GetCompilerPC() + offset + 4; @@ -345,7 +347,7 @@ void Arm64Jit::BranchFPFlag(MIPSOpcode op, CCFlags cc, bool likely) { ERROR_LOG_REPORT(JIT, "Branch in FPFlag delay slot at %08x in block starting at %08x", GetCompilerPC(), js.blockStart); return; } - int offset = _IMM16 << 2; + int offset = TARGET16; u32 targetAddr = GetCompilerPC() + offset + 4; MIPSOpcode delaySlotOp = GetOffsetInstruction(1); @@ -402,7 +404,7 @@ void Arm64Jit::BranchVFPUFlag(MIPSOpcode op, CCFlags cc, bool likely) { ERROR_LOG_REPORT(JIT, "Branch in VFPU delay slot at %08x in block starting at %08x", GetCompilerPC(), js.blockStart); return; } - int offset = _IMM16 << 2; + int offset = TARGET16; u32 targetAddr = GetCompilerPC() + offset + 4; MIPSOpcode delaySlotOp = GetOffsetInstruction(1); @@ -471,7 +473,7 @@ void Arm64Jit::Comp_Jump(MIPSOpcode op) { ERROR_LOG_REPORT(JIT, "Branch in Jump delay slot at %08x in block starting at %08x", GetCompilerPC(), js.blockStart); return; } - u32 off = _IMM26 << 2; + u32 off = TARGET26; u32 targetAddr = (GetCompilerPC() & 0xF0000000) | off; // Might be a stubbed address or something? diff --git a/Core/MIPS/ARM64/Arm64CompVFPU.cpp b/Core/MIPS/ARM64/Arm64CompVFPU.cpp index d90ed9bce8..1307baee29 100644 --- a/Core/MIPS/ARM64/Arm64CompVFPU.cpp +++ b/Core/MIPS/ARM64/Arm64CompVFPU.cpp @@ -19,6 +19,9 @@ #if PPSSPP_ARCH(ARM64) #include +#include "Common/Arm64Emitter.h" +#include "Common/CPUDetect.h" +#include "Common/Data/Convert/SmallDataConvert.h" #include "Common/Math/math_util.h" #include "Core/Compatibility.h" @@ -30,8 +33,6 @@ #include "Core/MIPS/MIPSTables.h" #include "Core/MIPS/MIPSAnalyst.h" #include "Core/MIPS/MIPSCodeUtils.h" -#include "Common/CPUDetect.h" -#include "Common/Arm64Emitter.h" #include "Core/MIPS/ARM64/Arm64Jit.h" #include "Core/MIPS/ARM64/Arm64RegCache.h" @@ -1811,7 +1812,7 @@ namespace MIPSComp { u8 dreg; GetVectorRegs(&dreg, V_Single, _VT); - s32 imm = (s32)(s16)(u16)(op & 0xFFFF); + s32 imm = SignExtend16ToS32(op); fpr.MapRegV(dreg, MAP_DIRTY | MAP_NOINIT); fp.MOVI2F(fpr.V(dreg), (float)imm, SCRATCH1); diff --git a/Core/MIPS/ARM64/Arm64Jit.cpp b/Core/MIPS/ARM64/Arm64Jit.cpp index 6a518cc976..1b989a8d6c 100644 --- a/Core/MIPS/ARM64/Arm64Jit.cpp +++ b/Core/MIPS/ARM64/Arm64Jit.cpp @@ -94,7 +94,7 @@ namespace MIPSComp using namespace Arm64Gen; using namespace Arm64JitConstants; -Arm64Jit::Arm64Jit(MIPSState *mips) : blocks(mips, this), gpr(mips, &js, &jo), fpr(mips, &js, &jo), mips_(mips), fp(this) { +Arm64Jit::Arm64Jit(MIPSState *mipsState) : blocks(mipsState, this), gpr(mipsState, &js, &jo), fpr(mipsState, &js, &jo), mips_(mipsState), fp(this) { // Automatically disable incompatible options. if (((intptr_t)Memory::base & 0x00000000FFFFFFFFUL) != 0) { jo.enablePointerify = false; diff --git a/Core/MIPS/ARM64/Arm64Jit.h b/Core/MIPS/ARM64/Arm64Jit.h index 17734ade59..a9c97a85ad 100644 --- a/Core/MIPS/ARM64/Arm64Jit.h +++ b/Core/MIPS/ARM64/Arm64Jit.h @@ -35,7 +35,7 @@ namespace MIPSComp { class Arm64Jit : public Arm64Gen::ARM64CodeBlock, public JitInterface, public MIPSFrontendInterface { public: - Arm64Jit(MIPSState *mips); + Arm64Jit(MIPSState *mipsState); virtual ~Arm64Jit(); void DoState(PointerWrap &p) override; @@ -185,6 +185,9 @@ public: const u8 *GetDispatcher() const override { return dispatcher; } + bool IsAtDispatchFetch(const u8 *ptr) const override { + return ptr == dispatcherFetch; + } void LinkBlock(u8 *exitPoint, const u8 *checkedEntry) override; void UnlinkBlock(u8 *checkedEntry, u32 originalAddress) override; @@ -275,6 +278,7 @@ public: const u8 *dispatcherPCInSCRATCH1; const u8 *dispatcher; const u8 *dispatcherNoCheck; + const u8 *dispatcherFetch; const u8 *saveStaticRegisters; const u8 *loadStaticRegisters; diff --git a/Core/MIPS/ARM64/Arm64RegCache.cpp b/Core/MIPS/ARM64/Arm64RegCache.cpp index b084adf109..ac97e33e74 100644 --- a/Core/MIPS/ARM64/Arm64RegCache.cpp +++ b/Core/MIPS/ARM64/Arm64RegCache.cpp @@ -33,7 +33,7 @@ using namespace Arm64Gen; using namespace Arm64JitConstants; -Arm64RegCache::Arm64RegCache(MIPSState *mips, MIPSComp::JitState *js, MIPSComp::JitOptions *jo) : mips_(mips), js_(js), jo_(jo) { +Arm64RegCache::Arm64RegCache(MIPSState *mipsState, MIPSComp::JitState *js, MIPSComp::JitOptions *jo) : mips_(mipsState), js_(js), jo_(jo) { } void Arm64RegCache::Init(ARM64XEmitter *emitter) { diff --git a/Core/MIPS/ARM64/Arm64RegCache.h b/Core/MIPS/ARM64/Arm64RegCache.h index b95caf7854..62f7771192 100644 --- a/Core/MIPS/ARM64/Arm64RegCache.h +++ b/Core/MIPS/ARM64/Arm64RegCache.h @@ -82,7 +82,7 @@ namespace MIPSComp { class Arm64RegCache { public: - Arm64RegCache(MIPSState *mips, MIPSComp::JitState *js, MIPSComp::JitOptions *jo); + Arm64RegCache(MIPSState *mipsState, MIPSComp::JitState *js, MIPSComp::JitOptions *jo); ~Arm64RegCache() {} void Init(Arm64Gen::ARM64XEmitter *emitter); diff --git a/Core/MIPS/ARM64/Arm64RegCacheFPU.cpp b/Core/MIPS/ARM64/Arm64RegCacheFPU.cpp index fffb38fcc7..9d177ba498 100644 --- a/Core/MIPS/ARM64/Arm64RegCacheFPU.cpp +++ b/Core/MIPS/ARM64/Arm64RegCacheFPU.cpp @@ -31,7 +31,7 @@ using namespace Arm64Gen; using namespace Arm64JitConstants; -Arm64RegCacheFPU::Arm64RegCacheFPU(MIPSState *mips, MIPSComp::JitState *js, MIPSComp::JitOptions *jo) : mips_(mips), vr(mr + 32), js_(js), jo_(jo), initialReady(false) { +Arm64RegCacheFPU::Arm64RegCacheFPU(MIPSState *mipsState, MIPSComp::JitState *js, MIPSComp::JitOptions *jo) : mips_(mipsState), vr(mr + 32), js_(js), jo_(jo) { numARMFpuReg_ = 32; } diff --git a/Core/MIPS/ARM64/Arm64RegCacheFPU.h b/Core/MIPS/ARM64/Arm64RegCacheFPU.h index 1fe2537049..13284d2389 100644 --- a/Core/MIPS/ARM64/Arm64RegCacheFPU.h +++ b/Core/MIPS/ARM64/Arm64RegCacheFPU.h @@ -79,7 +79,7 @@ namespace MIPSComp { class Arm64RegCacheFPU { public: - Arm64RegCacheFPU(MIPSState *mips, MIPSComp::JitState *js, MIPSComp::JitOptions *jo); + Arm64RegCacheFPU(MIPSState *mipsState, MIPSComp::JitState *js, MIPSComp::JitOptions *jo); ~Arm64RegCacheFPU() {} void Init(Arm64Gen::ARM64XEmitter *emitter, Arm64Gen::ARM64FloatEmitter *fp); @@ -175,7 +175,7 @@ private: FPURegMIPS *vr; bool pendingFlush; - bool initialReady; + bool initialReady = false; FPURegARM64 arInitial[MAX_ARMFPUREG]; FPURegMIPS mrInitial[NUM_MIPSFPUREG]; }; diff --git a/Core/MIPS/IR/IRCompALU.cpp b/Core/MIPS/IR/IRCompALU.cpp index 72f9a1ff42..62d813f3b2 100644 --- a/Core/MIPS/IR/IRCompALU.cpp +++ b/Core/MIPS/IR/IRCompALU.cpp @@ -47,9 +47,9 @@ namespace MIPSComp { void IRFrontend::Comp_IType(MIPSOpcode op) { CONDITIONAL_DISABLE(ALU_IMM); - s32 simm = (s32)_IMM16; // sign extension u32 uimm = (u16)_IMM16; - u32 suimm = (u32)(s32)simm; + s32 simm = SignExtend16ToS32(op); + u32 suimm = SignExtend16ToU32(op); MIPSGPReg rt = _RT; MIPSGPReg rs = _RS; @@ -274,7 +274,7 @@ void IRFrontend::Comp_Allegrex(MIPSOpcode op) { return; switch ((op >> 6) & 31) { - case 16: // seb // R(rd) = (u32)(s32)(s8)(u8)R(rt); + case 16: // seb // R(rd) = SignExtend8ToU32(R(rt)); ir.Write(IROp::Ext8to32, rd, rt); break; diff --git a/Core/MIPS/IR/IRCompBranch.cpp b/Core/MIPS/IR/IRCompBranch.cpp index 2592d6227c..0f8bbc4ff2 100644 --- a/Core/MIPS/IR/IRCompBranch.cpp +++ b/Core/MIPS/IR/IRCompBranch.cpp @@ -15,6 +15,7 @@ // Official git repository and contact information can be found at // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. +#include "Common/Data/Convert/SmallDataConvert.h" #include "Common/Profiler/Profiler.h" #include "Core/Reporting.h" @@ -40,8 +41,9 @@ #define _SA MIPS_GET_SA(op) #define _POS ((op>> 6) & 0x1F) #define _SIZE ((op>>11) & 0x1F) -#define _IMM16 (signed short)(op & 0xFFFF) #define _IMM26 (op & 0x03FFFFFF) +#define TARGET16 ((int)(SignExtend16ToU32(op) << 2)) +#define TARGET26 (_IMM26 << 2) #define LOOPOPTIMIZATION 0 @@ -57,7 +59,7 @@ void IRFrontend::BranchRSRTComp(MIPSOpcode op, IRComparison cc, bool likely) { ERROR_LOG_REPORT(JIT, "Branch in RSRTComp delay slot at %08x in block starting at %08x", GetCompilerPC(), js.blockStart); return; } - int offset = _IMM16 << 2; + int offset = TARGET16; MIPSGPReg rt = _RT; MIPSGPReg rs = _RS; u32 targetAddr = GetCompilerPC() + offset + 4; @@ -114,7 +116,7 @@ void IRFrontend::BranchRSZeroComp(MIPSOpcode op, IRComparison cc, bool andLink, ERROR_LOG_REPORT(JIT, "Branch in RSZeroComp delay slot at %08x in block starting at %08x", GetCompilerPC(), js.blockStart); return; } - int offset = _IMM16 << 2; + int offset = TARGET16; MIPSGPReg rs = _RS; u32 targetAddr = GetCompilerPC() + offset + 4; @@ -192,7 +194,7 @@ void IRFrontend::BranchFPFlag(MIPSOpcode op, IRComparison cc, bool likely) { ERROR_LOG_REPORT(JIT, "Branch in FPFlag delay slot at %08x in block starting at %08x", GetCompilerPC(), js.blockStart); return; } - int offset = _IMM16 << 2; + int offset = TARGET16; u32 targetAddr = GetCompilerPC() + offset + 4; ir.Write(IROp::FpCondToReg, IRTEMP_LHS); @@ -235,7 +237,7 @@ void IRFrontend::BranchVFPUFlag(MIPSOpcode op, IRComparison cc, bool likely) { ERROR_LOG_REPORT(JIT, "Branch in VFPU delay slot at %08x in block starting at %08x", GetCompilerPC(), js.blockStart); return; } - int offset = _IMM16 << 2; + int offset = TARGET16; u32 targetAddr = GetCompilerPC() + offset + 4; MIPSOpcode delaySlotOp = GetOffsetInstruction(1); @@ -290,7 +292,7 @@ void IRFrontend::Comp_Jump(MIPSOpcode op) { return; } - u32 off = _IMM26 << 2; + u32 off = TARGET26; u32 targetAddr = (GetCompilerPC() & 0xF0000000) | off; // Might be a stubbed address or something? diff --git a/Core/MIPS/IR/IRCompVFPU.cpp b/Core/MIPS/IR/IRCompVFPU.cpp index 5226e57fd1..2a5e00a861 100644 --- a/Core/MIPS/IR/IRCompVFPU.cpp +++ b/Core/MIPS/IR/IRCompVFPU.cpp @@ -17,9 +17,9 @@ #include -#include "Common/Math/math_util.h" - #include "Common/CPUDetect.h" +#include "Common/Data/Convert/SmallDataConvert.h" +#include "Common/Math/math_util.h" #include "Core/Compatibility.h" #include "Core/Config.h" #include "Core/MemMap.h" @@ -1502,7 +1502,6 @@ namespace MIPSComp { memcpy(srcregs, sregs, sizeof(sregs)); memcpy(tempregs, dregs, sizeof(dregs)); - int n = GetNumVectorElements(sz); int nOut = GetNumVectorElements(outsize); // If src registers aren't contiguous, make them. @@ -1778,7 +1777,7 @@ namespace MIPSComp { // Vector integer immediate // d[0] = float(imm) - s32 imm = (s32)(s16)(u16)(op & 0xFFFF); + s32 imm = SignExtend16ToS32(op); u8 dreg; GetVectorRegsPrefixD(&dreg, V_Single, _VT); ir.Write(IROp::SetConstF, dreg, ir.AddConstantFloat((float)imm)); @@ -1818,7 +1817,7 @@ namespace MIPSComp { int n = GetNumVectorElements(sz); u8 dregs[4]; - GetVectorRegsPrefixD(dregs, sz, _VD); + GetVectorRegsPrefixD(dregs, sz, vd); for (int i = 0; i < n; i++) { ir.Write(IROp::SetConstF, dregs[i], ir.AddConstantFloat(cst_constants[conNum])); } diff --git a/Core/MIPS/IR/IRInst.h b/Core/MIPS/IR/IRInst.h index a5ec8a8434..1e2b6c1410 100644 --- a/Core/MIPS/IR/IRInst.h +++ b/Core/MIPS/IR/IRInst.h @@ -334,7 +334,7 @@ struct IRInst { }; // Returns the new PC. -u32 IRInterpret(MIPSState *mips, const IRInst *inst, int count); +u32 IRInterpret(MIPSState *ms, const IRInst *inst, int count); // Each IR block gets a constant pool. class IRWriter { diff --git a/Core/MIPS/IR/IRInterpreter.cpp b/Core/MIPS/IR/IRInterpreter.cpp index aa962dbb5e..132f095dc1 100644 --- a/Core/MIPS/IR/IRInterpreter.cpp +++ b/Core/MIPS/IR/IRInterpreter.cpp @@ -2,9 +2,10 @@ #include #include "ppsspp_config.h" -#include "Common/Math/math_util.h" -#include "Common/Common.h" #include "Common/BitScan.h" +#include "Common/Common.h" +#include "Common/Data/Convert/SmallDataConvert.h" +#include "Common/Math/math_util.h" #ifdef _M_SSE #include @@ -32,6 +33,12 @@ #include "Core/MIPS/IR/IRInterpreter.h" #include "Core/System.h" +#ifdef mips +// Why do MIPS compilers define something so generic? Try to keep defined, at least... +#undef mips +#define mips mips +#endif + alignas(16) static const float vec4InitValues[8][4] = { { 0.0f, 0.0f, 0.0f, 0.0f }, { 1.0f, 1.0f, 1.0f, 1.0f }, @@ -126,10 +133,10 @@ u32 IRInterpret(MIPSState *mips, const IRInst *inst, int count) { mips->r[inst->dest] = ~mips->r[inst->src1]; break; case IROp::Ext8to32: - mips->r[inst->dest] = (s32)(s8)mips->r[inst->src1]; + mips->r[inst->dest] = SignExtend8ToU32(mips->r[inst->src1]); break; case IROp::Ext16to32: - mips->r[inst->dest] = (s32)(s16)mips->r[inst->src1]; + mips->r[inst->dest] = SignExtend16ToU32(mips->r[inst->src1]); break; case IROp::ReverseBits: mips->r[inst->dest] = ReverseBits32(mips->r[inst->src1]); @@ -139,13 +146,13 @@ u32 IRInterpret(MIPSState *mips, const IRInst *inst, int count) { mips->r[inst->dest] = Memory::ReadUnchecked_U8(mips->r[inst->src1] + inst->constant); break; case IROp::Load8Ext: - mips->r[inst->dest] = (s32)(s8)Memory::ReadUnchecked_U8(mips->r[inst->src1] + inst->constant); + mips->r[inst->dest] = SignExtend8ToU32(Memory::ReadUnchecked_U8(mips->r[inst->src1] + inst->constant)); break; case IROp::Load16: mips->r[inst->dest] = Memory::ReadUnchecked_U16(mips->r[inst->src1] + inst->constant); break; case IROp::Load16Ext: - mips->r[inst->dest] = (s32)(s16)Memory::ReadUnchecked_U16(mips->r[inst->src1] + inst->constant); + mips->r[inst->dest] = SignExtend16ToU32(Memory::ReadUnchecked_U16(mips->r[inst->src1] + inst->constant)); break; case IROp::Load32: mips->r[inst->dest] = Memory::ReadUnchecked_U32(mips->r[inst->src1] + inst->constant); diff --git a/Core/MIPS/IR/IRInterpreter.h b/Core/MIPS/IR/IRInterpreter.h index 092c2ac936..ec2c6f270b 100644 --- a/Core/MIPS/IR/IRInterpreter.h +++ b/Core/MIPS/IR/IRInterpreter.h @@ -20,4 +20,4 @@ inline static u32 ReverseBits32(u32 v) { return v; } -u32 IRInterpret(MIPSState *mips, const IRInst *inst, int count); +u32 IRInterpret(MIPSState *ms, const IRInst *inst, int count); diff --git a/Core/MIPS/IR/IRJit.cpp b/Core/MIPS/IR/IRJit.cpp index 77d988d139..f938619a9f 100644 --- a/Core/MIPS/IR/IRJit.cpp +++ b/Core/MIPS/IR/IRJit.cpp @@ -41,8 +41,8 @@ namespace MIPSComp { -IRJit::IRJit(MIPSState *mips) : frontend_(mips->HasDefaultPrefix()), mips_(mips) { - u32 size = 128 * 1024; +IRJit::IRJit(MIPSState *mipsState) : frontend_(mipsState->HasDefaultPrefix()), mips_(mipsState) { + // u32 size = 128 * 1024; // blTrampolines_ = kernelMemory.Alloc(size, true, "trampoline"); InitIR(); diff --git a/Core/MIPS/IR/IRJit.h b/Core/MIPS/IR/IRJit.h index 9beabe585a..a8e30db888 100644 --- a/Core/MIPS/IR/IRJit.h +++ b/Core/MIPS/IR/IRJit.h @@ -135,7 +135,7 @@ private: class IRJit : public JitInterface { public: - IRJit(MIPSState *mips); + IRJit(MIPSState *mipsState); virtual ~IRJit(); void DoState(PointerWrap &p) override; diff --git a/Core/MIPS/IR/IRPassSimplify.cpp b/Core/MIPS/IR/IRPassSimplify.cpp index ee65084a60..baf43491b6 100644 --- a/Core/MIPS/IR/IRPassSimplify.cpp +++ b/Core/MIPS/IR/IRPassSimplify.cpp @@ -3,6 +3,7 @@ #include #include "Common/BitSet.h" +#include "Common/Data/Convert/SmallDataConvert.h" #include "Common/Log.h" #include "Core/MIPS/IR/IRInterpreter.h" #include "Core/MIPS/IR/IRPassSimplify.h" @@ -36,8 +37,8 @@ u32 Evaluate(u32 a, IROp op) { case IROp::Neg: return -(s32)a; case IROp::BSwap16: return ((a & 0xFF00FF00) >> 8) | ((a & 0x00FF00FF) << 8); case IROp::BSwap32: return swap32(a); - case IROp::Ext8to32: return (u32)(s32)(s8)(u8)a; - case IROp::Ext16to32: return (u32)(s32)(s16)(u16)a; + case IROp::Ext8to32: return SignExtend8ToU32(a); + case IROp::Ext16to32: return SignExtend16ToU32(a); case IROp::ReverseBits: return ReverseBits32(a); case IROp::Clz: { int x = 31; @@ -163,7 +164,6 @@ bool ThreeOpToTwoOp(const IRWriter &in, IRWriter &out, const IROptions &opts) { bool logBlocks = false; for (int i = 0; i < (int)in.GetInstructions().size(); i++) { IRInst inst = in.GetInstructions()[i]; - const IRMeta *meta = GetIRMeta(inst.op); switch (inst.op) { case IROp::Sub: case IROp::Slt: diff --git a/Core/MIPS/JitCommon/JitBlockCache.cpp b/Core/MIPS/JitCommon/JitBlockCache.cpp index 9807ce9551..3f5e797312 100644 --- a/Core/MIPS/JitCommon/JitBlockCache.cpp +++ b/Core/MIPS/JitCommon/JitBlockCache.cpp @@ -15,13 +15,7 @@ // Official git repository and contact information can be found at // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. -// Enable define below to enable oprofile integration. For this to work, -// it requires at least oprofile version 0.9.4, and changing the build -// system to link the Dolphin executable against libopagent. Since the -// dependency is a little inconvenient and this is possibly a slight -// performance hit, it's not enabled by default, but it's useful for -// locating performance issues. - +#include "ppsspp_config.h" #include #include @@ -45,6 +39,12 @@ // #include "JitBase.h" +// Enable define below to enable oprofile integration. For this to work, +// it requires at least oprofile version 0.9.4, and changing the build +// system to link the Dolphin executable against libopagent. Since the +// dependency is a little inconvenient and this is possibly a slight +// performance hit, it's not enabled by default, but it's useful for +// locating performance issues. #if defined USE_OPROFILE && USE_OPROFILE #include @@ -59,7 +59,7 @@ op_agent_t agent; const u32 INVALID_EXIT = 0xFFFFFFFF; -JitBlockCache::JitBlockCache(MIPSState *mips, CodeBlockCommon *codeBlock) : +JitBlockCache::JitBlockCache(MIPSState *mipsState, CodeBlockCommon *codeBlock) : codeBlock_(codeBlock), blocks_(nullptr), num_blocks_(0) { } diff --git a/Core/MIPS/JitCommon/JitBlockCache.h b/Core/MIPS/JitCommon/JitBlockCache.h index 0b26138fb2..fe6217da1f 100644 --- a/Core/MIPS/JitCommon/JitBlockCache.h +++ b/Core/MIPS/JitCommon/JitBlockCache.h @@ -113,7 +113,7 @@ public: class JitBlockCache : public JitBlockCacheDebugInterface { public: - JitBlockCache(MIPSState *mips_, CodeBlockCommon *codeBlock); + JitBlockCache(MIPSState *mipsState, CodeBlockCommon *codeBlock); ~JitBlockCache(); int AllocateBlock(u32 em_address); diff --git a/Core/MIPS/JitCommon/JitCommon.cpp b/Core/MIPS/JitCommon/JitCommon.cpp index 397cc85ad9..8c7aecf905 100644 --- a/Core/MIPS/JitCommon/JitCommon.cpp +++ b/Core/MIPS/JitCommon/JitCommon.cpp @@ -15,6 +15,7 @@ // Official git repository and contact information can be found at // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. +#include "ppsspp_config.h" #include #include "ext/disarm.h" @@ -63,17 +64,17 @@ namespace MIPSComp { } } - JitInterface *CreateNativeJit(MIPSState *mips) { + JitInterface *CreateNativeJit(MIPSState *mipsState) { #if PPSSPP_ARCH(ARM) - return new MIPSComp::ArmJit(mips); + return new MIPSComp::ArmJit(mipsState); #elif PPSSPP_ARCH(ARM64) - return new MIPSComp::Arm64Jit(mips); + return new MIPSComp::Arm64Jit(mipsState); #elif PPSSPP_ARCH(X86) || PPSSPP_ARCH(AMD64) - return new MIPSComp::Jit(mips); + return new MIPSComp::Jit(mipsState); #elif PPSSPP_ARCH(MIPS) - return new MIPSComp::MipsJit(mips); + return new MIPSComp::MipsJit(mipsState); #else - return new MIPSComp::FakeJit(mips); + return new MIPSComp::FakeJit(mipsState); #endif } diff --git a/Core/MIPS/JitCommon/JitCommon.h b/Core/MIPS/JitCommon/JitCommon.h index 08063f866e..8515a31765 100644 --- a/Core/MIPS/JitCommon/JitCommon.h +++ b/Core/MIPS/JitCommon/JitCommon.h @@ -123,6 +123,9 @@ namespace MIPSComp { virtual bool CodeInRange(const u8 *ptr) const = 0; virtual bool DescribeCodePtr(const u8 *ptr, std::string &name) = 0; + virtual bool IsAtDispatchFetch(const u8 *ptr) const { + return false; + } virtual const u8 *GetDispatcher() const = 0; virtual const u8 *GetCrashHandler() const = 0; virtual JitBlockCache *GetBlockCache() = 0; @@ -154,5 +157,5 @@ namespace MIPSComp { void DoDummyJitState(PointerWrap &p); - JitInterface *CreateNativeJit(MIPSState *mips); + JitInterface *CreateNativeJit(MIPSState *mipsState); } diff --git a/Core/MIPS/JitCommon/JitState.cpp b/Core/MIPS/JitCommon/JitState.cpp index 895589cadf..08ce7926f9 100644 --- a/Core/MIPS/JitCommon/JitState.cpp +++ b/Core/MIPS/JitCommon/JitState.cpp @@ -15,6 +15,7 @@ // Official git repository and contact information can be found at // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. +#include "ppsspp_config.h" #include "Common/CPUDetect.h" #include "Core/Config.h" #include "Core/MIPS/JitCommon/JitState.h" @@ -47,7 +48,11 @@ namespace MIPSComp { // We can get block linking to work with W^X by doing even more unprotect/re-protect, but let's try without first. // enableBlocklink = !PlatformIsWXExclusive(); // Revert to this line if block linking is slow in W^X mode +#if PPSSPP_ARCH(MIPS) + enableBlocklink = false; +#else enableBlocklink = !Disabled(JitDisable::BLOCKLINK); +#endif immBranches = false; continueBranches = false; continueJumps = false; diff --git a/Core/MIPS/MIPS.h b/Core/MIPS/MIPS.h index 31df9d8baf..6635a153ff 100644 --- a/Core/MIPS/MIPS.h +++ b/Core/MIPS/MIPS.h @@ -162,7 +162,7 @@ enum class CPUCore; (int)(offsetof(MIPSState, x) - offsetof(MIPSState, f[0]) + (i) * 4)) // To get RIP/relative addressing (requires tight memory control so generated code isn't too far from the binary, and a reachable variable called mips): -// #define MIPSSTATE_VAR(x) M(&mips->x) +// #define MIPSSTATE_VAR(x) M(&mips_->x) #endif diff --git a/Core/MIPS/MIPS/MipsJit.cpp b/Core/MIPS/MIPS/MipsJit.cpp index 5201437784..2a096759c4 100644 --- a/Core/MIPS/MIPS/MipsJit.cpp +++ b/Core/MIPS/MIPS/MipsJit.cpp @@ -20,6 +20,7 @@ #include "Common/Profiler/Profiler.h" #include "Common/Serialize/Serializer.h" +#include "Common/Serialize/SerializeFuncs.h" #include "Core/Reporting.h" #include "Core/Config.h" #include "Core/Core.h" @@ -41,7 +42,7 @@ void DisassembleMIPS(const u8 *data, int size) { namespace MIPSComp { -MipsJit::MipsJit(MIPSState *mips) : blocks(mips, this), mips_(mips) +MipsJit::MipsJit(MIPSState *mipsState) : blocks(mipsState, this), mips_(mipsState) { logBlocks = 0; dontLogBlocks = 0; @@ -79,10 +80,20 @@ void MipsJit::FlushPrefixV() { } +MIPSOpcode MipsJit::GetOriginalOp(MIPSOpcode op) { + JitBlockCache *bc = GetBlockCache(); + int block_num = bc->GetBlockNumberFromEmuHackOp(op, true); + if (block_num >= 0) { + return bc->GetOriginalFirstOp(block_num); + } else { + return op; + } +} + void MipsJit::ClearCache() { blocks.Clear(); - ClearCodeSpace(); + ClearCodeSpace(0); //GenerateFixedCode(); } @@ -226,6 +237,14 @@ void MipsJit::Comp_RunBlock(MIPSOpcode op) ERROR_LOG(JIT, "Comp_RunBlock should never be reached!"); } +void MipsJit::LinkBlock(u8 *exitPoint, const u8 *checkedEntry) { + // TODO +} + +void MipsJit::UnlinkBlock(u8 *checkedEntry, u32 originalAddress) { + // TODO +} + bool MipsJit::ReplaceJalTo(u32 dest) { const ReplacementTableEntry *entry = nullptr; u32 funcSize = 0; diff --git a/Core/MIPS/MIPS/MipsJit.h b/Core/MIPS/MIPS/MipsJit.h index 23b164b1e7..195579b37d 100644 --- a/Core/MIPS/MIPS/MipsJit.h +++ b/Core/MIPS/MIPS/MipsJit.h @@ -35,7 +35,7 @@ namespace MIPSComp class MipsJit : public MIPSGen::MIPSCodeBlock, public JitInterface, public MIPSFrontendInterface { public: - MipsJit(MIPSState *mips); + MipsJit(MIPSState *mipsState); void DoState(PointerWrap &p) override; @@ -49,6 +49,8 @@ public: void Compile(u32 em_address) override; // Compiles a block at current MIPS PC const u8 *DoJit(u32 em_address, JitBlock *b); + const u8 *GetCrashHandler() const override { return nullptr; } + bool CodeInRange(const u8 *ptr) const override { return IsInSpace(ptr); } bool DescribeCodePtr(const u8 *ptr, std::string &name); void CompileDelaySlot(int flags); @@ -131,6 +133,8 @@ public: JitBlockCache *GetBlockCache() override { return &blocks; } JitBlockCacheDebugInterface *GetBlockCacheDebugInterface() override { return &blocks; } + MIPSOpcode GetOriginalOp(MIPSOpcode op) override; + std::vector SaveAndClearEmuHackOps() override { return blocks.SaveAndClearEmuHackOps(); } void RestoreSavedEmuHackOps(std::vector saved) override { blocks.RestoreSavedEmuHackOps(saved); } @@ -138,6 +142,13 @@ public: void InvalidateCacheAt(u32 em_address, int length = 4) override; void UpdateFCR31() override; + const u8 *GetDispatcher() const override { + return dispatcher; + } + + void LinkBlock(u8 *exitPoint, const u8 *checkedEntry) override; + void UnlinkBlock(u8 *checkedEntry, u32 originalAddress) override; + void EatPrefix() override { js.EatPrefix(); } private: @@ -183,8 +194,5 @@ public: const u8 *dispatcherNoCheck; }; -typedef void (MipsJit::*MIPSCompileFunc)(MIPSOpcode opcode); -typedef int (MipsJit::*MIPSReplaceFunc)(); - } // namespace MIPSComp diff --git a/Core/MIPS/MIPSAnalyst.cpp b/Core/MIPS/MIPSAnalyst.cpp index 1a39988abc..6e88bbf63f 100644 --- a/Core/MIPS/MIPSAnalyst.cpp +++ b/Core/MIPS/MIPSAnalyst.cpp @@ -15,6 +15,7 @@ // Official git repository and contact information can be found at // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. +#include "ppsspp_config.h" #include #include #include @@ -102,6 +103,7 @@ static const HardHashTableEntry hardcodedHashes[] = { { 0x030507c9a1f0fc85, 92, "matrix_rot_x", }, { 0x0483fceefa4557ff, 1360, "__udivdi3", }, { 0x0558ad5c5be00ca1, 76, "vtfm_t", }, + { 0x05aceb23092fd6a1, 36, "zettai_hero_update_minimap_tex", }, // Zettai Hero Project (US) { 0x05aedd0c04b451a1, 356, "sqrt", }, { 0x0654fc8adbe16ef7, 28, "vmul_q", }, { 0x06628f6052cda3c1, 1776, "toheart2_download_frame", }, // To Heart 2 Portable @@ -325,6 +327,7 @@ static const HardHashTableEntry hardcodedHashes[] = { { 0x8df2928848857e97, 164, "strcat", }, { 0x8e48cabd529ca6b5, 52, "vector_multiply_t", }, { 0x8e97dcb03fbaba5c, 104, "vmmul_q_transp", }, + { 0x8ecf804bbe7922e5, 572, "worms_copy_normalize_alpha" }, // Worms Battle Islands (US) { 0x8ee81b03d2eef1e7, 28, "vmul_t", }, { 0x8f09fb8693c3c49d, 992, "kirameki_school_life_download_frame", }, // Hentai Ouji To Warawanai Neko { 0x8f19c41e8b987e18, 100, "matrix_mogrify", }, @@ -502,6 +505,7 @@ static const HardHashTableEntry hardcodedHashes[] = { { 0xfe4f0280240008e9, 28, "vavg_q", }, { 0xfe5dd338ab862291, 216, "memset", }, // Metal Gear Solid: Peace Walker demo { 0xffc8f5f8f946152c, 192, "dl_write_light_color", }, + { 0x249a3c5981c73480, 1472, "openseason_data_decode", }, // Open Season }; namespace MIPSAnalyst { @@ -757,7 +761,7 @@ namespace MIPSAnalyst { std::lock_guard guard(functions_lock); hashToFunction.clear(); // Really need to detect C++11 features with better defines. -#if !defined(IOS) +#if !PPSSPP_PLATFORM(IOS) hashToFunction.reserve(functions.size()); #endif for (auto iter = functions.begin(); iter != functions.end(); iter++) { diff --git a/Core/MIPS/MIPSCodeUtils.cpp b/Core/MIPS/MIPSCodeUtils.cpp index ce91b2199c..faf620d5b1 100644 --- a/Core/MIPS/MIPSCodeUtils.cpp +++ b/Core/MIPS/MIPSCodeUtils.cpp @@ -28,72 +28,58 @@ namespace MIPSCodeUtils #define OP_SYSCALL_MASK 0xFC00003F #define _RS ((op>>21) & 0x1F) #define _RT ((op>>16) & 0x1F) +#define _IMM26 (op & 0x03FFFFFF) +#define TARGET16 ((int)(SignExtend16ToU32(op) << 2)) +#define TARGET26 (_IMM26 << 2) - u32 GetJumpTarget(u32 addr) - { + u32 GetJumpTarget(u32 addr) { MIPSOpcode op = Memory::Read_Instruction(addr, true); - if (op != 0) - { + if (op != 0) { MIPSInfo info = MIPSGetInfo(op); if ((info & IS_JUMP) && (info & IN_IMM26)) - { - u32 target = (addr & 0xF0000000) | ((op&0x03FFFFFF) << 2); - return target; - } + return (addr & 0xF0000000) | TARGET26; else return INVALIDTARGET; - } - else + } else { return INVALIDTARGET; + } } - u32 GetBranchTarget(u32 addr) - { + u32 GetBranchTarget(u32 addr) { MIPSOpcode op = Memory::Read_Instruction(addr, true); - if (op != 0) - { + if (op != 0) { MIPSInfo info = MIPSGetInfo(op); if (info & IS_CONDBRANCH) - { - return addr + 4 + ((signed short)(op&0xFFFF)<<2); - } + return addr + 4 + TARGET16; else return INVALIDTARGET; - } - else + } else { return INVALIDTARGET; + } } - u32 GetBranchTargetNoRA(u32 addr) - { + u32 GetBranchTargetNoRA(u32 addr) { MIPSOpcode op = Memory::Read_Instruction(addr, true); return GetBranchTargetNoRA(addr, op); } - u32 GetBranchTargetNoRA(u32 addr, MIPSOpcode op) - { - if (op != 0) - { + u32 GetBranchTargetNoRA(u32 addr, MIPSOpcode op) { + if (op != 0) { MIPSInfo info = MIPSGetInfo(op); if ((info & IS_CONDBRANCH) && !(info & OUT_RA)) - { - return addr + 4 + ((signed short)(op&0xFFFF)<<2); - } + return addr + 4 + TARGET16; else return INVALIDTARGET; - } - else + } else { return INVALIDTARGET; + } } - u32 GetSureBranchTarget(u32 addr) - { + u32 GetSureBranchTarget(u32 addr) { MIPSOpcode op = Memory::Read_Instruction(addr, true); - if (op != 0) - { + if (op != 0) { MIPSInfo info = MIPSGetInfo(op); - if ((info & IS_CONDBRANCH) && !(info & (IN_FPUFLAG | IS_VFPU))) - { + if ((info & IS_CONDBRANCH) && !(info & (IN_FPUFLAG | IS_VFPU))) { bool sure; bool takeBranch; switch (info & CONDTYPE_MASK) @@ -125,17 +111,17 @@ namespace MIPSCodeUtils } if (sure && takeBranch) - return addr + 4 + ((signed short)(op&0xFFFF)<<2); + return addr + 4 + TARGET16; else if (sure && !takeBranch) return addr + 8; else return INVALIDTARGET; - } - else + } else { return INVALIDTARGET; - } - else + } + } else { return INVALIDTARGET; + } } bool IsVFPUBranch(MIPSOpcode op) { diff --git a/Core/MIPS/MIPSCodeUtils.h b/Core/MIPS/MIPSCodeUtils.h index b6c0a3642e..17789f72c7 100644 --- a/Core/MIPS/MIPSCodeUtils.h +++ b/Core/MIPS/MIPSCodeUtils.h @@ -18,6 +18,7 @@ #pragma once #include "Common/CommonTypes.h" +#include "Common/Data/Convert/SmallDataConvert.h" #include "Core/MIPS/MIPS.h" // Invalid branch target address @@ -52,6 +53,21 @@ #define MIPS_GET_VS(op) ((op>>8) & 0x7F) #define MIPS_GET_VT(op) ((op>>16) & 0x7F) +inline int32_t SignExtend8ToS32(MIPSOpcode op) { + return SignExtend8ToS32(op.encoding); +} + +inline uint32_t SignExtend8ToU32(MIPSOpcode op) { + return SignExtend8ToU32(op.encoding); +} + +inline int32_t SignExtend16ToS32(MIPSOpcode op) { + return SignExtend16ToS32(op.encoding); +} + +inline uint32_t SignExtend16ToU32(MIPSOpcode op) { + return SignExtend16ToU32(op.encoding); +} namespace MIPSCodeUtils { diff --git a/Core/MIPS/MIPSDis.cpp b/Core/MIPS/MIPSDis.cpp index 9cc169e588..c1c61f6a9c 100644 --- a/Core/MIPS/MIPSDis.cpp +++ b/Core/MIPS/MIPSDis.cpp @@ -19,6 +19,7 @@ #include "Core/HLE/HLE.h" #include "Core/MemMap.h" #include "Core/MIPS/MIPS.h" +#include "Core/MIPS/MIPSCodeUtils.h" #include "Core/MIPS/MIPSDis.h" #include "Core/MIPS/MIPSTables.h" #include "Core/MIPS/MIPSDebugInterface.h" @@ -171,9 +172,9 @@ namespace MIPSDis void Dis_IType(MIPSOpcode op, char *out) { - s32 simm = (s32)(s16)(op & 0xFFFF); - u32 uimm = (u32)(u16)(op & 0xFFFF); - u32 suimm = (u32)simm; + u32 uimm = op & 0xFFFF; + u32 suimm = SignExtend16ToU32(op); + s32 simm = SignExtend16ToS32(op); int rt = _RT; int rs = _RS; diff --git a/Core/MIPS/MIPSInt.cpp b/Core/MIPS/MIPSInt.cpp index be67cc915d..0ad48fa874 100644 --- a/Core/MIPS/MIPSInt.cpp +++ b/Core/MIPS/MIPSInt.cpp @@ -17,6 +17,7 @@ #include +#include "Common/Data/Convert/SmallDataConvert.h" #include "Common/Math/math_util.h" #include "Common/BitSet.h" @@ -27,6 +28,7 @@ #include "Core/Host.h" #include "Core/MemMap.h" #include "Core/MIPS/MIPS.h" +#include "Core/MIPS/MIPSCodeUtils.h" #include "Core/MIPS/MIPSInt.h" #include "Core/MIPS/MIPSTables.h" #include "Core/Reporting.h" @@ -72,11 +74,7 @@ static inline void SkipLikely() int MIPS_SingleStep() { -#if defined(ARM) - MIPSOpcode op = MIPSOpcode(Memory::ReadUnchecked_U32(mipsr4k.pc)); -#else MIPSOpcode op = Memory::Read_Opcode_JIT(mipsr4k.pc); -#endif if (mipsr4k.inDelaySlot) { MIPSInterpret(op); if (mipsr4k.inDelaySlot) { @@ -295,10 +293,9 @@ namespace MIPSInt void Int_IType(MIPSOpcode op) { - s32 simm = (s32)(s16)(op & 0xFFFF); - u32 uimm = (u32)(u16)(op & 0xFFFF); - - u32 suimm = (u32)simm; + u32 uimm = op & 0xFFFF; + u32 suimm = SignExtend16ToU32(op); + s32 simm = SignExtend16ToS32(op); int rt = _RT; int rs = _RS; @@ -363,7 +360,6 @@ namespace MIPSInt int rt = _RT; int rs = _RS; int rd = _RD; - static bool has_warned = false; // Don't change $zr. if (rd == 0) @@ -411,8 +407,8 @@ namespace MIPSInt switch (op >> 26) { - case 32: R(rt) = (u32)(s32)(s8) Memory::Read_U8(addr); break; //lb - case 33: R(rt) = (u32)(s32)(s16)Memory::Read_U16(addr); break; //lh + case 32: R(rt) = SignExtend8ToU32(Memory::Read_U8(addr)); break; //lb + case 33: R(rt) = SignExtend16ToU32(Memory::Read_U16(addr)); break; //lh case 35: R(rt) = Memory::Read_U32(addr); break; //lw case 36: R(rt) = Memory::Read_U8 (addr); break; //lbu case 37: R(rt) = Memory::Read_U16(addr); break; //lhu @@ -738,7 +734,7 @@ namespace MIPSInt switch((op>>6)&31) { case 16: // seb - R(rd) = (u32)(s32)(s8)(u8)R(rt); + R(rd) = SignExtend8ToU32(R(rt)); break; case 20: // bitrev @@ -756,7 +752,7 @@ namespace MIPSInt break; case 24: // seh - R(rd) = (u32)(s32)(s16)(u16)R(rt); + R(rd) = SignExtend16ToU32(R(rt)); break; default: diff --git a/Core/MIPS/MIPSIntVFPU.cpp b/Core/MIPS/MIPSIntVFPU.cpp index a9c3b66832..0281b6bb42 100644 --- a/Core/MIPS/MIPSIntVFPU.cpp +++ b/Core/MIPS/MIPSIntVFPU.cpp @@ -720,7 +720,6 @@ namespace MIPSInt u32 tprefixAdd = VFPU_MAKE_CONSTANTS(VFPUConst::ONE, VFPUConst::ZERO, VFPUConst::ONE, VFPUConst::ZERO); ApplyPrefixST(t, VFPURewritePrefix(VFPU_CTRL_TPREFIX, tprefixRemove, tprefixAdd), outSize); - int n = GetNumVectorElements(sz); // Essentially D prefix saturation is forced. d[0] = nanclamp(t[0] + s[0], 0.0f, 1.0f); d[1] = nanclamp(t[1] + s[1], 0.0f, 1.0f); @@ -1226,7 +1225,6 @@ namespace MIPSInt ReadVector(s, sz, vs); ReadVector(t, sz, vs); - int n = GetNumVectorElements(sz); if (op & 0x10000) { // vbfy2 // S prefix forces the negate flags (so z and w are negative.) @@ -1973,7 +1971,7 @@ namespace MIPSInt ApplySwizzleS(s, sz); ReadVector(t, sz, vt); ApplySwizzleT(t, sz); - for (int i = 0; i < GetNumVectorElements(sz); i++) { + for (int i = 0; i < numElements; i++) { if ( my_isnan(s[i]) || my_isnan(t[i]) ) d[i] = 0.0f; else @@ -1998,7 +1996,7 @@ namespace MIPSInt ApplySwizzleS(s, sz); ReadVector(t, sz, vt); ApplySwizzleT(t, sz); - for (int i = 0; i < GetNumVectorElements(sz); i++) { + for (int i = 0; i < numElements; i++) { if ( my_isnan(s[i]) || my_isnan(t[i]) ) d[i] = 0.0f; else diff --git a/Core/MIPS/MIPSTables.cpp b/Core/MIPS/MIPSTables.cpp index 2270fc3630..ca8487e423 100644 --- a/Core/MIPS/MIPSTables.cpp +++ b/Core/MIPS/MIPSTables.cpp @@ -964,7 +964,6 @@ int MIPSInterpret_RunUntil(u64 globalTicks) while (coreState == CORE_RUNNING) { CoreTiming::Advance(); - u32 lastPC = 0; // NEVER stop in a delay slot! while (curMips->downcount >= 0 && coreState == CORE_RUNNING) @@ -974,17 +973,6 @@ int MIPSInterpret_RunUntil(u64 globalTicks) again: MIPSOpcode op = MIPSOpcode(Memory::Read_U32(curMips->pc)); //MIPSOpcode op = Memory::Read_Opcode_JIT(mipsr4k.pc); - /* - // Choke on VFPU - MIPSInfo info = MIPSGetInfo(op); - if (info & IS_VFPU) - { - if (!Core_IsStepping() && !GetAsyncKeyState(VK_LSHIFT)) - { - Core_EnableStepping(true); - return; - } - }*/ //2: check for breakpoint (VERY SLOW) #if defined(_DEBUG) @@ -1002,18 +990,6 @@ int MIPSInterpret_RunUntil(u64 globalTicks) #endif bool wasInDelaySlot = curMips->inDelaySlot; - - /* - if (curMips->pc != lastPC + 4) { - if (blockCount > 0) { - MIPSState *mips_ = curMips; - fprintf(f, "BLOCK : %08x v0: %08x v1: %08x a0: %08x s0: %08x s4: %08x\n", mips_->pc, mips_->r[MIPS_REG_V0], mips_->r[MIPS_REG_V1], mips_->r[MIPS_REG_A0], mips_->r[MIPS_REG_S0], mips_->r[MIPS_REG_S4]); - fflush(f); - blockCount--; - } - } - lastPC = curMips->pc; - */ MIPSInterpret(op); if (curMips->inDelaySlot) diff --git a/Core/MIPS/MIPSVFPUUtils.cpp b/Core/MIPS/MIPSVFPUUtils.cpp index 917fa16a04..e3d0e5ce14 100644 --- a/Core/MIPS/MIPSVFPUUtils.cpp +++ b/Core/MIPS/MIPSVFPUUtils.cpp @@ -873,7 +873,6 @@ float vfpu_rsqrt(float a) { uint32_t z = 0x00800000 >> less_bits; uint32_t halfsp = sp >> (1 + less_bits); for (int i = 0; i < 6; ++i) { - uint32_t oldz = z; uint32_t zsq = mant_mul(z, z); uint32_t correction = 0x00C00000 - mant_mul(halfsp, zsq); z = mant_mul(z, correction); diff --git a/Core/MIPS/fake/FakeJit.cpp b/Core/MIPS/fake/FakeJit.cpp index 257005a429..8150e26118 100644 --- a/Core/MIPS/fake/FakeJit.cpp +++ b/Core/MIPS/fake/FakeJit.cpp @@ -16,10 +16,12 @@ // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. #include "Common/Serialize/Serializer.h" +#include "Common/Serialize/SerializeFuncs.h" #include "Core/Reporting.h" #include "Core/Config.h" #include "Core/Core.h" #include "Core/CoreTiming.h" +#include "Core/Debugger/Breakpoints.h" #include "Core/Debugger/SymbolMap.h" #include "Core/MemMap.h" #include "Core/MIPS/MIPS.h" @@ -38,26 +40,29 @@ void DisassembleFake(const u8 *data, int size) { namespace MIPSComp { -FakeJit::FakeJit(MIPSState *mips) : blocks(mips, this), mips_(mips) +FakeJit::FakeJit(MIPSState *mipsState) : blocks(mipsState, this), mips_(mipsState) { logBlocks = 0; dontLogBlocks = 0; blocks.Init(); } -void FakeJit::DoState(PointerWrap &p) -{ - auto s = p.Section("FakeJit", 1, 2); +void FakeJit::DoState(PointerWrap &p) { + auto s = p.Section("Jit", 1, 2); if (!s) return; - p.Do(js.startDefaultPrefix); + Do(p, js.startDefaultPrefix); if (s >= 2) { - p.Do(js.hasSetRounding); + Do(p, js.hasSetRounding); js.lastSetRounding = 0; } else { js.hasSetRounding = 1; } + + // The debugger sets this so that "go" on a breakpoint will actually... go. + // But if they load a state, we can end up hitting it by mistake, since it's based on PC and ticks. + CBreakPoints::SetSkipFirst(0); } // This is here so the savestate matches between jit and non-jit. @@ -68,10 +73,10 @@ void FakeJit::DoDummyState(PointerWrap &p) return; bool dummy = false; - p.Do(dummy); + Do(p, dummy); if (s >= 2) { dummy = true; - p.Do(dummy); + Do(p, dummy); } } @@ -89,7 +94,7 @@ void FakeJit::FlushPrefixV() void FakeJit::ClearCache() { blocks.Clear(); - ClearCodeSpace(); + ClearCodeSpace(0); //GenerateFixedCode(); } diff --git a/Core/MIPS/fake/FakeJit.h b/Core/MIPS/fake/FakeJit.h index d23303a654..8c3403d566 100644 --- a/Core/MIPS/fake/FakeJit.h +++ b/Core/MIPS/fake/FakeJit.h @@ -18,8 +18,9 @@ #pragma once #include "Common/FakeEmitter.h" -#include "Core/MIPS/JitCommon/JitState.h" #include "Core/MIPS/JitCommon/JitBlockCache.h" +#include "Core/MIPS/JitCommon/JitState.h" +#include "Core/MIPS/JitCommon/JitCommon.h" #include "../MIPSVFPUUtils.h" #ifndef offsetof @@ -30,9 +31,9 @@ namespace MIPSComp { typedef int FakeReg; -class FakeJit : public FakeGen::FakeXCodeBlock { +class FakeJit : public FakeGen::FakeXCodeBlock, public JitInterface, public MIPSFrontendInterface { public: - FakeJit(MIPSState *mips); + FakeJit(MIPSState *mipsState); void DoState(PointerWrap &p); static void DoDummyState(PointerWrap &p); @@ -46,7 +47,9 @@ public: void Compile(u32 em_address); // Compiles a block at current MIPS PC const u8 *DoJit(u32 em_address, JitBlock *b); - bool DescribeCodePtr(const u8 *ptr, std::string &name); + const u8 *GetCrashHandler() const override { return nullptr; } + bool CodeInRange(const u8 *ptr) const override { return IsInSpace(ptr); } + bool DescribeCodePtr(const u8 *ptr, std::string &name) override; void CompileDelaySlot(int flags); void EatInstruction(MIPSOpcode op); @@ -126,9 +129,23 @@ public: int Replace_fabsf() { return 0; } JitBlockCache *GetBlockCache() { return &blocks; } + JitBlockCacheDebugInterface *GetBlockCacheDebugInterface() override { return &blocks; } - void ClearCache(); - void InvalidateCacheAt(u32 em_address, int length = 4); + MIPSOpcode GetOriginalOp(MIPSOpcode op) override { return op; } + + std::vector SaveAndClearEmuHackOps() override { return blocks.SaveAndClearEmuHackOps(); } + void RestoreSavedEmuHackOps(std::vector saved) override { blocks.RestoreSavedEmuHackOps(saved); } + + void ClearCache() override; + void InvalidateCacheAt(u32 em_address, int length = 4) override; + void UpdateFCR31() override {} + + const u8 *GetDispatcher() const override { + return nullptr; + } + + void LinkBlock(u8 *exitPoint, const u8 *checkedEntry) override {} + void UnlinkBlock(u8 *checkedEntry, u32 originalAddress) override {} void EatPrefix() { js.EatPrefix(); } diff --git a/Core/MIPS/x86/Asm.cpp b/Core/MIPS/x86/Asm.cpp index a099e1044b..03ce2ab7f1 100644 --- a/Core/MIPS/x86/Asm.cpp +++ b/Core/MIPS/x86/Asm.cpp @@ -66,7 +66,7 @@ void ImHere() { } void Jit::GenerateFixedCode(JitOptions &jo) { - const u8 *start = AlignCodePage(); + AlignCodePage(); BeginWrite(); restoreRoundingMode = AlignCode16(); { @@ -111,7 +111,7 @@ void Jit::GenerateFixedCode(JitOptions &jo) { enterDispatcher = AlignCode16(); ABI_PushAllCalleeSavedRegsAndAdjustStack(); -#ifdef _M_X64 +#if PPSSPP_ARCH(AMD64) // Two statically allocated registers. MOV(64, R(MEMBASEREG), ImmPtr(Memory::base)); uintptr_t jitbase = (uintptr_t)GetBasePtr(); @@ -161,11 +161,11 @@ void Jit::GenerateFixedCode(JitOptions &jo) { #ifdef MASKED_PSP_MEMORY AND(32, R(EAX), Imm32(Memory::MEMVIEW32_MASK)); #endif - -#ifdef _M_IX86 + dispatcherFetch = GetCodePtr(); +#if PPSSPP_ARCH(X86) _assert_msg_( Memory::base != 0, "Memory base bogus"); MOV(32, R(EAX), MDisp(EAX, (u32)Memory::base)); -#elif _M_X64 +#elif PPSSPP_ARCH(AMD64) MOV(32, R(EAX), MComplex(MEMBASEREG, RAX, SCALE_1, 0)); #endif MOV(32, R(EDX), R(EAX)); @@ -178,9 +178,9 @@ void Jit::GenerateFixedCode(JitOptions &jo) { } //grab from list and jump to it AND(32, R(EAX), Imm32(MIPS_EMUHACK_VALUE_MASK)); -#ifdef _M_IX86 +#if PPSSPP_ARCH(X86) ADD(32, R(EAX), ImmPtr(GetBasePtr())); -#elif _M_X64 +#elif PPSSPP_ARCH(AMD64) if (jo.reserveR15ForAsm) ADD(64, R(RAX), R(JITBASEREG)); else diff --git a/Core/MIPS/x86/CompALU.cpp b/Core/MIPS/x86/CompALU.cpp index 4c262cf1a4..fdb58765f6 100644 --- a/Core/MIPS/x86/CompALU.cpp +++ b/Core/MIPS/x86/CompALU.cpp @@ -53,7 +53,7 @@ namespace MIPSComp using namespace X64JitConstants; static bool HasLowSubregister(OpArg arg) { -#ifndef _M_X64 +#if !PPSSPP_ARCH(AMD64) // Can't use ESI or EDI (which we use), no 8-bit versions. Only these. if (!arg.IsSimpleReg(EAX) && !arg.IsSimpleReg(EBX) && !arg.IsSimpleReg(ECX) && !arg.IsSimpleReg(EDX)) { return false; @@ -78,9 +78,9 @@ namespace MIPSComp void Jit::Comp_IType(MIPSOpcode op) { CONDITIONAL_DISABLE(ALU_IMM); - s32 simm = (s32)_IMM16; // sign extension u32 uimm = op & 0xFFFF; - u32 suimm = (u32)(s32)simm; + s32 simm = SignExtend16ToS32(op); + u32 suimm = SignExtend16ToU32(op); MIPSGPReg rt = _RT; MIPSGPReg rs = _RS; @@ -798,10 +798,10 @@ namespace MIPSComp switch ((op >> 6) & 31) { - case 16: // seb // R(rd) = (u32)(s32)(s8)(u8)R(rt); + case 16: // seb // R(rd) = SignExtend8ToU32(R(rt)); if (gpr.IsImm(rt)) { - gpr.SetImm(rd, (u32)(s32)(s8)(u8)gpr.GetImm(rt)); + gpr.SetImm(rd, SignExtend8ToU32(gpr.GetImm(rt))); break; } @@ -876,10 +876,10 @@ namespace MIPSComp gpr.UnlockAll(); break; - case 24: // seh // R(rd) = (u32)(s32)(s16)(u16)R(rt); + case 24: // seh // R(rd) = SignExtend16ToU32(R(rt)); if (gpr.IsImm(rt)) { - gpr.SetImm(rd, (u32)(s32)(s16)(u16)gpr.GetImm(rt)); + gpr.SetImm(rd, SignExtend16ToU32(gpr.GetImm(rt))); break; } diff --git a/Core/MIPS/x86/CompBranch.cpp b/Core/MIPS/x86/CompBranch.cpp index 875ff24399..fef35e87d5 100644 --- a/Core/MIPS/x86/CompBranch.cpp +++ b/Core/MIPS/x86/CompBranch.cpp @@ -18,6 +18,7 @@ #include "ppsspp_config.h" #if PPSSPP_ARCH(X86) || PPSSPP_ARCH(AMD64) +#include "Common/Data/Convert/SmallDataConvert.h" #include "Common/Profiler/Profiler.h" #include "Core/Config.h" @@ -46,8 +47,9 @@ #define _SA MIPS_GET_SA(op) #define _POS ((op>> 6) & 0x1F) #define _SIZE ((op>>11) & 0x1F) -#define _IMM16 (signed short)(op & 0xFFFF) #define _IMM26 (op & 0x03FFFFFF) +#define TARGET16 ((int)(SignExtend16ToU32(op) << 2)) +#define TARGET26 (_IMM26 << 2) #define LOOPOPTIMIZATION 0 @@ -319,7 +321,7 @@ void Jit::BranchRSRTComp(MIPSOpcode op, Gen::CCFlags cc, bool likely) ERROR_LOG_REPORT(JIT, "Branch in RSRTComp delay slot at %08x in block starting at %08x", GetCompilerPC(), js.blockStart); return; } - int offset = _IMM16 << 2; + int offset = TARGET16; MIPSGPReg rt = _RT; MIPSGPReg rs = _RS; u32 targetAddr = GetCompilerPC() + offset + 4; @@ -395,7 +397,7 @@ void Jit::BranchRSZeroComp(MIPSOpcode op, Gen::CCFlags cc, bool andLink, bool li ERROR_LOG_REPORT(JIT, "Branch in RSZeroComp delay slot at %08x in block starting at %08x", GetCompilerPC(), js.blockStart); return; } - int offset = _IMM16 << 2; + int offset = TARGET16; MIPSGPReg rs = _RS; u32 targetAddr = GetCompilerPC() + offset + 4; @@ -511,7 +513,7 @@ void Jit::BranchFPFlag(MIPSOpcode op, Gen::CCFlags cc, bool likely) ERROR_LOG_REPORT(JIT, "Branch in FPFlag delay slot at %08x in block starting at %08x", GetCompilerPC(), js.blockStart); return; } - int offset = _IMM16 << 2; + int offset = TARGET16; u32 targetAddr = GetCompilerPC() + offset + 4; MIPSOpcode delaySlotOp = GetOffsetInstruction(1); @@ -551,7 +553,7 @@ void Jit::BranchVFPUFlag(MIPSOpcode op, Gen::CCFlags cc, bool likely) WARN_LOG(JIT, "Branch in VFPU delay slot at %08x in block starting at %08x", GetCompilerPC(), js.blockStart); return; } - int offset = _IMM16 << 2; + int offset = TARGET16; u32 targetAddr = GetCompilerPC() + offset + 4; MIPSOpcode delaySlotOp = GetOffsetInstruction(1); @@ -602,7 +604,7 @@ void Jit::Comp_Jump(MIPSOpcode op) { ERROR_LOG_REPORT(JIT, "Branch in Jump delay slot at %08x in block starting at %08x", GetCompilerPC(), js.blockStart); return; } - u32 off = _IMM26 << 2; + u32 off = TARGET26; u32 targetAddr = (GetCompilerPC() & 0xF0000000) | off; // Might be a stubbed address or something? diff --git a/Core/MIPS/x86/CompLoadStore.cpp b/Core/MIPS/x86/CompLoadStore.cpp index 403f613f33..1d5e398d7f 100644 --- a/Core/MIPS/x86/CompLoadStore.cpp +++ b/Core/MIPS/x86/CompLoadStore.cpp @@ -94,7 +94,7 @@ namespace MIPSComp { gpr.MapReg(rt, true, false); } -#ifdef _M_IX86 +#if PPSSPP_ARCH(X86) // We use EDX so we can have DL for 8-bit ops. const bool needSwap = bits == 8 && !gpr.R(rt).IsSimpleReg(EDX) && !gpr.R(rt).IsSimpleReg(ECX); if (needSwap) @@ -138,14 +138,13 @@ namespace MIPSComp { void Jit::CompITypeMemUnpairedLR(MIPSOpcode op, bool isStore) { CONDITIONAL_DISABLE(LSU); - int o = op>>26; int offset = _IMM16; MIPSGPReg rt = _RT; MIPSGPReg rs = _RS; X64Reg shiftReg = ECX; gpr.FlushLockX(ECX, EDX); -#ifdef _M_X64 +#if PPSSPP_ARCH(AMD64) // On x64, we need ECX for CL, but it's also the first arg and gets lost. Annoying. gpr.FlushLockX(R9); shiftReg = R9; @@ -288,7 +287,6 @@ namespace MIPSComp { CONDITIONAL_DISABLE(LSU); int offset = _IMM16; MIPSGPReg rt = _RT; - MIPSGPReg rs = _RS; int o = op>>26; if (((op >> 29) & 1) == 0 && rt == MIPS_REG_ZERO) { // Don't load anything into $zr @@ -309,11 +307,11 @@ namespace MIPSComp { CompITypeMemRead(op, 32, &XEmitter::MOVZX, safeMemFuncs.readU32); break; - case 32: //R(rt) = (u32)(s32)(s8) ReadMem8 (addr); break; //lb + case 32: //R(rt) = SignExtend8ToU32 (ReadMem8 (addr)); break; //lb CompITypeMemRead(op, 8, &XEmitter::MOVSX, safeMemFuncs.readU8); break; - case 33: //R(rt) = (u32)(s32)(s16)ReadMem16(addr); break; //lh + case 33: //R(rt) = SignExtend16ToU32(ReadMem16(addr)); break; //lh CompITypeMemRead(op, 16, &XEmitter::MOVSX, safeMemFuncs.readU16); break; @@ -395,7 +393,7 @@ namespace MIPSComp { default: Comp_Generic(op); - return ; + return; } } diff --git a/Core/MIPS/x86/CompVFPU.cpp b/Core/MIPS/x86/CompVFPU.cpp index 95d0704da3..fd2f9ea3d4 100644 --- a/Core/MIPS/x86/CompVFPU.cpp +++ b/Core/MIPS/x86/CompVFPU.cpp @@ -239,8 +239,6 @@ bool IsOverlapSafe(int dreg, int di, int sn, u8 sregs[], int tn = 0, u8 tregs[] return IsOverlapSafeAllowS(dreg, di, sn, sregs, tn, tregs) && sregs[di] != dreg; } -alignas(16) static u32 ssLoadStoreTemp; - void Jit::Comp_SV(MIPSOpcode op) { CONDITIONAL_DISABLE(LSU_VFPU); @@ -327,7 +325,7 @@ void Jit::Comp_SVQ(MIPSOpcode op) { FixupBranch next = J_CC(CC_NE); auto PSPMemAddr = [](X64Reg scaled, int offset) { -#ifdef _M_IX86 +#if PPSSPP_ARCH(X86) return MDisp(scaled, (u32)Memory::base + offset); #else return MComplex(MEMBASEREG, scaled, 1, offset); @@ -797,7 +795,6 @@ void Jit::Comp_VCrossQuat(MIPSOpcode op) { DISABLE; VectorSize sz = GetVecSize(op); - int n = GetNumVectorElements(sz); u8 sregs[4], tregs[4], dregs[4]; GetVectorRegs(sregs, sz, _VS); @@ -1809,8 +1806,6 @@ extern const double mulTableVf2i[32] = { (1ULL<<28),(1ULL<<29),(1ULL<<30),(1ULL<<31), }; -static const float half = 0.5f; - static const double maxMinIntAsDouble[2] = { (double)0x7fffffff, (double)(int)0x80000000 }; // that's not equal to 0x80000000 void Jit::Comp_Vf2i(MIPSOpcode op) { @@ -1935,7 +1930,7 @@ void Jit::Comp_Vcst(MIPSOpcode op) { int n = GetNumVectorElements(sz); u8 dregs[4]; - GetVectorRegsPrefixD(dregs, sz, _VD); + GetVectorRegsPrefixD(dregs, sz, vd); if (RipAccessible(cst_constants)) { MOVSS(XMM0, M(&cst_constants[conNum])); // rip accessible @@ -2170,7 +2165,7 @@ union u32float { } }; -#ifdef _M_X64 +#if PPSSPP_ARCH(AMD64) typedef float SinCosArg; #else typedef u32float SinCosArg; @@ -2208,7 +2203,7 @@ void Jit::Comp_VV2Op(MIPSOpcode op) { DISABLE; auto trigCallHelper = [this](void (*sinCosFunc)(SinCosArg, float *output), u8 sreg) { -#ifdef _M_X64 +#if PPSSPP_ARCH(AMD64) MOVSS(XMM0, fpr.V(sreg)); // TODO: This reg might be different on Linux... #ifdef _WIN32 @@ -2916,7 +2911,7 @@ void Jit::Comp_Vmmul(MIPSOpcode op) { // Map the D column. u8 dcol[4]; GetVectorRegs(dcol, vsz, dcols[i]); -#ifndef _M_X64 +#if !PPSSPP_ARCH(AMD64) fpr.MapRegsVS(dcol, vsz, MAP_DIRTY | MAP_NOINIT | MAP_NOLOCK); #else fpr.MapRegsVS(dcol, vsz, MAP_DIRTY | MAP_NOINIT); @@ -2929,7 +2924,7 @@ void Jit::Comp_Vmmul(MIPSOpcode op) { } } -#ifndef _M_X64 +#if !PPSSPP_ARCH(AMD64) fpr.ReleaseSpillLocks(); #endif if (transposeDest) { @@ -3453,7 +3448,7 @@ void Jit::Comp_Viim(MIPSOpcode op) { // Flush SIMD. fpr.SimpleRegsV(&dreg, V_Single, MAP_NOINIT | MAP_DIRTY); - s32 imm = (s32)(s16)(u16)(op & 0xFFFF); + s32 imm = SignExtend16ToS32(op); FP32 fp; fp.f = (float)imm; MOV(32, R(TEMPREG), Imm32(fp.u)); @@ -3572,7 +3567,7 @@ void Jit::Comp_VRot(MIPSOpcode op) { bool negSin1 = (imm & 0x10) ? true : false; -#ifdef _M_X64 +#if PPSSPP_ARCH(AMD64) #ifdef _WIN32 LEA(64, RDX, MIPSSTATE_VAR(sincostemp)); #else diff --git a/Core/MIPS/x86/Jit.cpp b/Core/MIPS/x86/Jit.cpp index 97682d5f7d..2409af9e8e 100644 --- a/Core/MIPS/x86/Jit.cpp +++ b/Core/MIPS/x86/Jit.cpp @@ -105,8 +105,8 @@ static void JitLogMiss(MIPSOpcode op) // JitBlockCache doesn't use this, just stores it. #pragma warning(disable:4355) #endif -Jit::Jit(MIPSState *mips) - : blocks(mips, this), mips_(mips) { +Jit::Jit(MIPSState *mipsState) + : blocks(mipsState, this), mips_(mipsState) { blocks.Init(); gpr.SetEmitter(this); fpr.SetEmitter(this); @@ -216,7 +216,7 @@ void Jit::ClearCache() void Jit::SaveFlags() { PUSHF(); -#if defined(_M_X64) +#if PPSSPP_ARCH(AMD64) // On X64, the above misaligns the stack. However there might be a cheaper solution than this. POP(64, R(EAX)); MOV(64, MIPSSTATE_VAR(saved_flags), R(EAX)); @@ -224,7 +224,7 @@ void Jit::SaveFlags() { } void Jit::LoadFlags() { -#if defined(_M_X64) +#if PPSSPP_ARCH(AMD64) MOV(64, R(EAX), MIPSSTATE_VAR(saved_flags)); PUSH(64, R(EAX)); #endif @@ -274,6 +274,11 @@ void Jit::Compile(u32 em_address) { ClearCache(); } + if (!Memory::IsValidAddress(em_address)) { + Core_ExecException(em_address, em_address, ExecExceptionType::JUMP); + return; + } + BeginWrite(); int block_num = blocks.AllocateBlock(em_address); diff --git a/Core/MIPS/x86/Jit.h b/Core/MIPS/x86/Jit.h index b065d90522..e6e11ac899 100644 --- a/Core/MIPS/x86/Jit.h +++ b/Core/MIPS/x86/Jit.h @@ -46,7 +46,7 @@ struct RegCacheState { class Jit : public Gen::XCodeBlock, public JitInterface, public MIPSFrontendInterface { public: - Jit(MIPSState *mips); + Jit(MIPSState *mipsState); virtual ~Jit(); const JitOptions &GetJitOptions() { return jo; } @@ -298,6 +298,11 @@ private: } return true; } + + bool IsAtDispatchFetch(const u8 *codePtr) const override { + return codePtr == dispatcherFetch; + } + void SaveFlags(); void LoadFlags(); @@ -321,6 +326,7 @@ private: const u8 *dispatcherCheckCoreState; const u8 *dispatcherNoCheck; const u8 *dispatcherInEAXNoCheck; + const u8 *dispatcherFetch; const u8 *restoreRoundingMode; const u8 *applyRoundingMode; diff --git a/Core/MIPS/x86/RegCache.cpp b/Core/MIPS/x86/RegCache.cpp index fa8479323d..10b2676526 100644 --- a/Core/MIPS/x86/RegCache.cpp +++ b/Core/MIPS/x86/RegCache.cpp @@ -34,18 +34,18 @@ using namespace X64JitConstants; static const X64Reg allocationOrder[] = { // R12, when used as base register, for example in a LEA, can generate bad code! Need to look into this. // On x64, RCX and RDX are the first args. CallProtectedFunction() assumes they're not regcached. -#ifdef _M_X64 +#if PPSSPP_ARCH(AMD64) #ifdef _WIN32 RSI, RDI, R8, R9, R10, R11, R12, R13, #else RBP, R8, R9, R10, R11, R12, R13, #endif -#elif _M_IX86 +#elif PPSSPP_ARCH(X86) ESI, EDI, EDX, ECX, EBX, #endif }; -#ifdef _M_X64 +#if PPSSPP_ARCH(AMD64) static X64Reg allocationOrderR15[ARRAY_SIZE(allocationOrder) + 1] = {INVALID_REG}; #endif @@ -54,20 +54,18 @@ void GPRRegCache::FlushBeforeCall() { Flush(); } -GPRRegCache::GPRRegCache() : mips(0), emit(0) { - memset(regs, 0, sizeof(regs)); - memset(xregs, 0, sizeof(xregs)); +GPRRegCache::GPRRegCache() { } -void GPRRegCache::Start(MIPSState *mips, MIPSComp::JitState *js, MIPSComp::JitOptions *jo, MIPSAnalyst::AnalysisResults &stats) { -#ifdef _M_X64 +void GPRRegCache::Start(MIPSState *mipsState, MIPSComp::JitState *js, MIPSComp::JitOptions *jo, MIPSAnalyst::AnalysisResults &stats) { +#if PPSSPP_ARCH(AMD64) if (allocationOrderR15[0] == INVALID_REG) { memcpy(allocationOrderR15, allocationOrder, sizeof(allocationOrder)); allocationOrderR15[ARRAY_SIZE(allocationOrderR15) - 1] = R15; } #endif - this->mips = mips; + mips_ = mipsState; for (int i = 0; i < NUM_X_REGS; i++) { xregs[i].free = true; xregs[i].dirty = false; @@ -307,7 +305,7 @@ u32 GPRRegCache::GetImm(MIPSGPReg preg) const { } const X64Reg *GPRRegCache::GetAllocationOrder(int &count) { -#ifdef _M_X64 +#if PPSSPP_ARCH(AMD64) if (!jo_->reserveR15ForAsm) { count = ARRAY_SIZE(allocationOrderR15); return allocationOrderR15; @@ -421,7 +419,7 @@ void GPRRegCache::Flush() { else if (regs[i].location.IsImm()) { StoreFromRegister(r); } else { - _assert_msg_(false, "Jit64 - Flush unhandled case, reg %d PC: %08x", i, mips->pc); + _assert_msg_(false, "Jit64 - Flush unhandled case, reg %d PC: %08x", i, mips_->pc); } } } diff --git a/Core/MIPS/x86/RegCache.h b/Core/MIPS/x86/RegCache.h index cdda09eb3f..218677485d 100644 --- a/Core/MIPS/x86/RegCache.h +++ b/Core/MIPS/x86/RegCache.h @@ -17,12 +17,13 @@ #pragma once +#include "ppsspp_config.h" #include "Common/x64Emitter.h" #include "Core/MIPS/MIPS.h" #include "Core/MIPS/MIPSAnalyst.h" namespace X64JitConstants { -#ifdef _M_X64 +#if PPSSPP_ARCH(AMD64) const Gen::X64Reg MEMBASEREG = Gen::RBX; const Gen::X64Reg CTXREG = Gen::R14; const Gen::X64Reg JITBASEREG = Gen::R15; @@ -34,9 +35,9 @@ namespace X64JitConstants { const Gen::X64Reg TEMPREG = Gen::EAX; const int NUM_MIPS_GPRS = 36; -#ifdef _M_X64 +#if PPSSPP_ARCH(AMD64) const u32 NUM_X_REGS = 16; -#elif _M_IX86 +#elif PPSSPP_ARCH(X86) const u32 NUM_X_REGS = 8; #endif } @@ -69,7 +70,7 @@ class GPRRegCache public: GPRRegCache(); ~GPRRegCache() {} - void Start(MIPSState *mips, MIPSComp::JitState *js, MIPSComp::JitOptions *jo, MIPSAnalyst::AnalysisResults &stats); + void Start(MIPSState *mipsState, MIPSComp::JitState *js, MIPSComp::JitOptions *jo, MIPSAnalyst::AnalysisResults &stats); void DiscardRegContentsIfCached(MIPSGPReg preg); void DiscardR(MIPSGPReg preg); @@ -119,17 +120,17 @@ public: void GetState(GPRRegCacheState &state) const; void RestoreState(const GPRRegCacheState& state); - MIPSState *mips; + MIPSState *mips_ = nullptr; private: Gen::X64Reg GetFreeXReg(); Gen::X64Reg FindBestToSpill(bool unusedOnly, bool *clobbered); const Gen::X64Reg *GetAllocationOrder(int &count); - MIPSCachedReg regs[X64JitConstants::NUM_MIPS_GPRS]; - X64CachedReg xregs[X64JitConstants::NUM_X_REGS]; + MIPSCachedReg regs[X64JitConstants::NUM_MIPS_GPRS]{}; + X64CachedReg xregs[X64JitConstants::NUM_X_REGS]{}; - Gen::XEmitter *emit; + Gen::XEmitter *emit = nullptr; MIPSComp::JitState *js_; MIPSComp::JitOptions *jo_; }; diff --git a/Core/MIPS/x86/RegCacheFPU.cpp b/Core/MIPS/x86/RegCacheFPU.cpp index e217bfc9e7..de2ef1e7ff 100644 --- a/Core/MIPS/x86/RegCacheFPU.cpp +++ b/Core/MIPS/x86/RegCacheFPU.cpp @@ -31,14 +31,12 @@ using namespace Gen; using namespace X64JitConstants; -FPURegCache::FPURegCache() : mips(0), initialReady(false), emit(0) { - memset(regs, 0, sizeof(regs)); - memset(xregs, 0, sizeof(xregs)); +FPURegCache::FPURegCache() { vregs = regs + 32; } -void FPURegCache::Start(MIPSState *mips, MIPSComp::JitState *js, MIPSComp::JitOptions *jo, MIPSAnalyst::AnalysisResults &stats, bool useRip) { - this->mips = mips; +void FPURegCache::Start(MIPSState *mipsState, MIPSComp::JitState *js, MIPSComp::JitOptions *jo, MIPSAnalyst::AnalysisResults &stats, bool useRip) { + mips_ = mipsState; useRip_ = useRip; if (!initialReady) { SetupInitialRegs(); @@ -409,7 +407,7 @@ X64Reg FPURegCache::LoadRegsVS(const u8 *v, int n) { break; } } - const float *f = v[0] < 128 ? &mips->v[voffset[v[0]]] : &mips->tempValues[v[0] - 128]; + const float *f = v[0] < 128 ? &mips_->v[voffset[v[0]]] : &mips_->tempValues[v[0] - 128]; if (((intptr_t)f & 0x7) == 0 && n == 2) { emit->MOVQ_xmm(res, vregs[v[0]].location); } else if (((intptr_t)f & 0xf) == 0) { @@ -665,7 +663,7 @@ void FPURegCache::StoreFromRegister(int i) { } } - const float *f = mri[0] - 32 < 128 ? &mips->v[voffset[mri[0] - 32]] : &mips->tempValues[mri[0] - 32 - 128]; + const float *f = mri[0] - 32 < 128 ? &mips_->v[voffset[mri[0] - 32]] : &mips_->tempValues[mri[0] - 32 - 128]; int align = (intptr_t)f & 0xf; // If we can do a multistore... @@ -872,7 +870,7 @@ void FPURegCache::Flush() { } else if (regs[i].location.IsImm()) { StoreFromRegister(i); } else { - _assert_msg_(false, "Jit64 - Flush unhandled case, reg %i PC: %08x", i, mips->pc); + _assert_msg_(false, "Jit64 - Flush unhandled case, reg %i PC: %08x", i, mips_->pc); } } } @@ -887,13 +885,13 @@ OpArg FPURegCache::GetDefaultLocation(int reg) const { } else if (reg < 32 + 128) { // Here, RIP has the advantage so let's use it when possible if (useRip_) { - return M(&mips->v[voffset[reg - 32]]); // rip accessible + return M(&mips_->v[voffset[reg - 32]]); // rip accessible } else { return MIPSSTATE_VAR_ELEM32(v[0], voffset[reg - 32]); } } else { if (useRip_) { - return M(&mips->tempValues[reg - 32 - 128]); // rip accessible + return M(&mips_->tempValues[reg - 32 - 128]); // rip accessible } else { return MIPSSTATE_VAR_ELEM32(tempValues[0], reg - 32 - 128); } @@ -1022,9 +1020,9 @@ int FPURegCache::SanityCheck() const { const int *FPURegCache::GetAllocationOrder(int &count) { static const int allocationOrder[] = { -#ifdef _M_X64 +#if PPSSPP_ARCH(AMD64) XMM6, XMM7, XMM8, XMM9, XMM10, XMM11, XMM12, XMM13, XMM14, XMM15, XMM2, XMM3, XMM4, XMM5 -#elif _M_IX86 +#elif PPSSPP_ARCH(X86) XMM2, XMM3, XMM4, XMM5, XMM6, XMM7, #endif }; diff --git a/Core/MIPS/x86/RegCacheFPU.h b/Core/MIPS/x86/RegCacheFPU.h index 3cf33d53b4..04faf64cdd 100644 --- a/Core/MIPS/x86/RegCacheFPU.h +++ b/Core/MIPS/x86/RegCacheFPU.h @@ -43,14 +43,16 @@ // take into account in which XMM registers the values are. Fallback: Just dump out the values // and do it the old way. +#include "ppsspp_config.h" + enum { TEMP0 = 32 + 128, NUM_MIPS_FPRS = 32 + 128 + NUM_X86_FPU_TEMPS, }; -#ifdef _M_X64 +#if PPSSPP_ARCH(AMD64) #define NUM_X_FPREGS 16 -#elif _M_IX86 +#elif PPSSPP_ARCH(X86) #define NUM_X_FPREGS 8 #endif @@ -97,7 +99,7 @@ public: FPURegCache(); ~FPURegCache() {} - void Start(MIPSState *mips, MIPSComp::JitState *js, MIPSComp::JitOptions *jo, MIPSAnalyst::AnalysisResults &stats, bool useRip); + void Start(MIPSState *mipsState, MIPSComp::JitState *js, MIPSComp::JitOptions *jo, MIPSAnalyst::AnalysisResults &stats, bool useRip); void MapReg(int preg, bool doLoad = true, bool makeDirty = true); void StoreFromRegister(int preg); void StoreFromRegisterV(int preg) { @@ -126,11 +128,11 @@ public: const Gen::OpArg &R(int freg) const {return regs[freg].location;} const Gen::OpArg &V(int vreg) const { - _dbg_assert_msg_(vregs[vreg].lane == 0, "SIMD reg %d used as V reg (use VS instead). pc=%08x", vreg, mips->pc); + _dbg_assert_msg_(vregs[vreg].lane == 0, "SIMD reg %d used as V reg (use VS instead). pc=%08x", vreg, mips_->pc); return vregs[vreg].location; } const Gen::OpArg &VS(const u8 *vs) const { - _dbg_assert_msg_(vregs[vs[0]].lane != 0, "V reg %d used as VS reg (use V instead). pc=%08x", vs[0], mips->pc); + _dbg_assert_msg_(vregs[vs[0]].lane != 0, "V reg %d used as VS reg (use V instead). pc=%08x", vs[0], mips_->pc); return vregs[vs[0]].location; } @@ -142,7 +144,7 @@ public: } Gen::X64Reg VX(int vreg) const { - _dbg_assert_msg_(vregs[vreg].lane == 0, "SIMD reg %d used as V reg (use VSX instead). pc=%08x", vreg, mips->pc); + _dbg_assert_msg_(vregs[vreg].lane == 0, "SIMD reg %d used as V reg (use VSX instead). pc=%08x", vreg, mips_->pc); if (vregs[vreg].away && vregs[vreg].location.IsSimpleReg()) return vregs[vreg].location.GetSimpleReg(); _assert_msg_(false, "Not so simple - v%i", vreg); @@ -150,7 +152,7 @@ public: } Gen::X64Reg VSX(const u8 *vs) const { - _dbg_assert_msg_(vregs[vs[0]].lane != 0, "V reg %d used as VS reg (use VX instead). pc=%08x", vs[0], mips->pc); + _dbg_assert_msg_(vregs[vs[0]].lane != 0, "V reg %d used as VS reg (use VX instead). pc=%08x", vs[0], mips_->pc); if (vregs[vs[0]].away && vregs[vs[0]].location.IsSimpleReg()) return vregs[vs[0]].location.GetSimpleReg(); _assert_msg_(false, "Not so simple - v%i", vs[0]); @@ -206,7 +208,7 @@ public: void GetState(FPURegCacheState &state) const; void RestoreState(const FPURegCacheState& state); - MIPSState *mips; + MIPSState *mips_ = nullptr; void FlushX(Gen::X64Reg reg); Gen::X64Reg GetFreeXReg(); @@ -227,17 +229,17 @@ private: Gen::X64Reg LoadRegsVS(const u8 *v, int n); - MIPSCachedFPReg regs[NUM_MIPS_FPRS]; - X64CachedFPReg xregs[NUM_X_FPREGS]; + MIPSCachedFPReg regs[NUM_MIPS_FPRS]{}; + X64CachedFPReg xregs[NUM_X_FPREGS]{}; MIPSCachedFPReg *vregs; bool useRip_; bool pendingFlush; - bool initialReady; + bool initialReady = false; MIPSCachedFPReg regsInitial[NUM_MIPS_FPRS]; X64CachedFPReg xregsInitial[NUM_X_FPREGS]; - Gen::XEmitter *emit; + Gen::XEmitter *emit = nullptr; MIPSComp::JitState *js_; MIPSComp::JitOptions *jo_; }; diff --git a/Core/MemFault.cpp b/Core/MemFault.cpp index 38944bf98b..db2a4d4d98 100644 --- a/Core/MemFault.cpp +++ b/Core/MemFault.cpp @@ -41,11 +41,14 @@ namespace Memory { static int64_t g_numReportedBadAccesses = 0; const uint8_t *g_lastCrashAddress; +MemoryExceptionType g_lastMemoryExceptionType; + std::unordered_set g_ignoredAddresses; void MemFault_Init() { g_numReportedBadAccesses = 0; g_lastCrashAddress = nullptr; + g_lastMemoryExceptionType = MemoryExceptionType::NONE; g_ignoredAddresses.clear(); } @@ -122,11 +125,15 @@ bool HandleFault(uintptr_t hostAddress, void *ctx) { std::string infoString = ""; + bool isAtDispatch = false; if (MIPSComp::jit) { std::string desc; if (MIPSComp::jit->DescribeCodePtr(codePtr, desc)) { infoString += desc + "\n"; } + if (MIPSComp::jit->IsAtDispatchFetch(codePtr)) { + isAtDispatch = true; + } } int instructionSize = 4; @@ -158,7 +165,19 @@ bool HandleFault(uintptr_t hostAddress, void *ctx) { infoString += disassembly + "\n"; } - if (success) { + if (isAtDispatch) { + u32 targetAddr = currentMIPS->pc; // bad approximation + // TODO: Do the other archs and platforms. +#if PPSSPP_ARCH(AMD64) && PPSSPP_PLATFORM(WINDOWS) + // We know which register the address is in, look in Asm.cpp. + targetAddr = context->Rax; +#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); + return true; + } else if (success) { if (info.isMemoryWrite) { type = MemoryExceptionType::WRITE_WORD; } else { @@ -168,6 +187,8 @@ bool HandleFault(uintptr_t hostAddress, void *ctx) { type = MemoryExceptionType::UNKNOWN; } + g_lastMemoryExceptionType = type; + if (success && (g_Config.bIgnoreBadMemAccess || g_ignoredAddresses.find(codePtr) != g_ignoredAddresses.end())) { if (!info.isMemoryWrite) { // It was a read. Fill the destination register with 0. diff --git a/Core/MemMap.cpp b/Core/MemMap.cpp index d027fdd191..f69a46db6e 100644 --- a/Core/MemMap.cpp +++ b/Core/MemMap.cpp @@ -38,7 +38,7 @@ #include "Core/Core.h" #include "Core/Debugger/SymbolMap.h" -#include "Core/Debugger/Breakpoints.h" +#include "Core/Debugger/MemBlockInfo.h" #include "Core/Config.h" #include "Core/ConfigValues.h" #include "Core/HLE/ReplaceTables.h" @@ -129,7 +129,7 @@ inline static bool CanIgnoreView(const MemoryView &view) { #endif } -#if defined(IOS) && PPSSPP_ARCH(64BIT) +#if PPSSPP_PLATFORM(IOS) && PPSSPP_ARCH(64BIT) #define SKIP(a_flags, b_flags) \ if ((b_flags) & MV_KERNEL) \ continue; @@ -459,7 +459,7 @@ void Write_Opcode_JIT(const u32 _Address, const Opcode& _Value) Memory::WriteUnchecked_U32(_Value.encoding, _Address); } -void Memset(const u32 _Address, const u8 _iValue, const u32 _iLength) { +void Memset(const u32 _Address, const u8 _iValue, const u32 _iLength, const std::string &tag) { if (IsValidRange(_Address, _iLength)) { uint8_t *ptr = GetPointerUnchecked(_Address); memset(ptr, _iValue, _iLength); @@ -468,7 +468,7 @@ void Memset(const u32 _Address, const u8 _iValue, const u32 _iLength) { Write_U8(_iValue, (u32)(_Address + i)); } - CBreakPoints::ExecMemCheck(_Address, true, _iLength, currentMIPS->pc); + NotifyMemInfo(MemBlockFlags::WRITE, _Address, _iLength, tag); } } // namespace diff --git a/Core/MemMap.h b/Core/MemMap.h index 1bd6027c22..465802fb32 100644 --- a/Core/MemMap.h +++ b/Core/MemMap.h @@ -84,7 +84,7 @@ extern u32 g_PSPModel; // UWP has such limited memory management that we need to mask // even in 64-bit mode. Also, when using the sanitizer, we need to mask as well. -#if PPSSPP_ARCH(32BIT) || PPSSPP_PLATFORM(UWP) || USE_ADDRESS_SANITIZER || PPSSPP_PLATFORM(IOS) +#if PPSSPP_ARCH(32BIT) || PPSSPP_PLATFORM(UWP) || USE_ASAN || PPSSPP_PLATFORM(IOS) #define MASKED_PSP_MEMORY #endif @@ -175,9 +175,9 @@ inline u32 ReadUnchecked_U32(const u32 address) { inline float ReadUnchecked_Float(const u32 address) { #ifdef MASKED_PSP_MEMORY - return *(float *)(base + (address & MEMVIEW32_MASK)); + return *(float_le *)(base + (address & MEMVIEW32_MASK)); #else - return *(float *)(base + address); + return *(float_le *)(base + address); #endif } @@ -207,9 +207,9 @@ inline void WriteUnchecked_U32(u32 data, u32 address) { inline void WriteUnchecked_Float(float data, u32 address) { #ifdef MASKED_PSP_MEMORY - *(float *)(base + (address & MEMVIEW32_MASK)) = data; + *(float_le *)(base + (address & MEMVIEW32_MASK)) = data; #else - *(float *)(base + address) = data; + *(float_le *)(base + address) = data; #endif } @@ -465,8 +465,11 @@ inline u32 PSP_GetScratchpadMemoryEnd() { return 0x00014000;} inline u32 PSP_GetKernelMemoryBase() { return 0x08000000;} inline u32 PSP_GetUserMemoryEnd() { return PSP_GetKernelMemoryBase() + Memory::g_MemorySize;} inline u32 PSP_GetKernelMemoryEnd() { return 0x08400000;} + // "Volatile" RAM is between 0x08400000 and 0x08800000, can be requested by the // game through sceKernelVolatileMemTryLock. +inline u32 PSP_GetVolatileMemoryStart() { return 0x08400000; } +inline u32 PSP_GetVolatileMemoryEnd() { return 0x08800000; } inline u32 PSP_GetUserMemoryBase() { return 0x08800000;} inline u32 PSP_GetDefaultLoadAddress() { return 0;} diff --git a/Core/MemMapHelpers.h b/Core/MemMapHelpers.h index 9431db5c0d..0983abdf9b 100644 --- a/Core/MemMapHelpers.h +++ b/Core/MemMapHelpers.h @@ -18,7 +18,7 @@ #pragma once #include "Common/CommonTypes.h" -#include "Core/Debugger/Breakpoints.h" +#include "Core/Debugger/MemBlockInfo.h" #include "Core/MemMap.h" #include "Core/MIPS/MIPS.h" @@ -28,33 +28,31 @@ extern MIPSState *currentMIPS; namespace Memory { -inline void Memcpy(const u32 to_address, const void *from_data, const u32 len) -{ +inline void Memcpy(const u32 to_address, const void *from_data, const u32 len, const std::string &tag = "Memcpy") { u8 *to = GetPointer(to_address); if (to) { memcpy(to, from_data, len); - CBreakPoints::ExecMemCheck(to_address, true, len, currentMIPS->pc); + NotifyMemInfo(MemBlockFlags::WRITE, to_address, len, tag); } // if not, GetPointer will log. } -inline void Memcpy(void *to_data, const u32 from_address, const u32 len) -{ +inline void Memcpy(void *to_data, const u32 from_address, const u32 len, const std::string &tag = "Memcpy") { const u8 *from = GetPointer(from_address); if (from) { memcpy(to_data, from, len); - CBreakPoints::ExecMemCheck(from_address, false, len, currentMIPS->pc); + NotifyMemInfo(MemBlockFlags::READ, from_address, len, tag); } // if not, GetPointer will log. } -inline void Memcpy(const u32 to_address, const u32 from_address, const u32 len) -{ +inline void Memcpy(const u32 to_address, const u32 from_address, const u32 len, const std::string &tag = "Memcpy") { Memcpy(GetPointer(to_address), from_address, len); - CBreakPoints::ExecMemCheck(to_address, true, len, currentMIPS->pc); + NotifyMemInfo(MemBlockFlags::READ, from_address, len, tag); + NotifyMemInfo(MemBlockFlags::WRITE, to_address, len, tag); } -void Memset(const u32 _Address, const u8 _Data, const u32 _iLength); +void Memset(const u32 _Address, const u8 _Data, const u32 _iLength, const std::string &tag = "Memset"); template void ReadStruct(u32 address, T *ptr) diff --git a/Core/PSPLoaders.cpp b/Core/PSPLoaders.cpp index 69a1b94855..ae718cddce 100644 --- a/Core/PSPLoaders.cpp +++ b/Core/PSPLoaders.cpp @@ -185,6 +185,28 @@ void InitMemoryForGamePBP(FileLoader *fileLoader) { if (paramSFO.ReadSFO(sfoData)) { // This is the parameter CFW uses to determine homebrew wants the full 64MB. UseLargeMem(paramSFO.GetValueInt("MEMSIZE")); + + // Take this moment to bring over the title, if set. + bool updateTitle = false; + std::string title = paramSFO.GetValueString("TITLE"); + if (g_paramSFO.GetValueString("TITLE").empty() && !title.empty()) { + g_paramSFO.SetValue("TITLE", title, (int)title.size()); + } + + std::string discID = paramSFO.GetValueString("DISC_ID"); + // Homebrew typically always leave this zero. + bool discTotalCheck = paramSFO.GetValueInt("DISC_TOTAL") != 0; + // A lot of homebrew reuse real game disc IDs - avoid. + bool formatCheck = discID.substr(0, 2) != "NP" && discID.substr(0, 2) != "UL" && discID.substr(0, 2) != "UC"; + char region = discID.size() > 3 ? discID[2] : '\0'; + bool regionCheck = region != 'A' && region != 'E' && region != 'H' && region != 'I' && region != 'J' && region != 'K' && region != 'U' && region != 'X'; + if (formatCheck || regionCheck || discTotalCheck) { + g_paramSFO.SetValue("DISC_ID", discID, (int)discID.size()); + std::string ver = paramSFO.GetValueString("DISC_VERSION"); + if (ver.empty()) + ver = "1.00"; + g_paramSFO.SetValue("DISC_VERSION", ver, (int)ver.size()); + } } } } @@ -393,33 +415,35 @@ bool Load_PSP_ELF_PBP(FileLoader *fileLoader, std::string *error_string) { std::string homebrewName = PSP_CoreParameter().fileToStart; std::size_t lslash = homebrewName.find_last_of("/"); - homebrewName = homebrewName.substr(lslash + 1); + if (lslash != homebrewName.npos) + homebrewName = homebrewName.substr(lslash + 1); + std::string homebrewTitle = g_paramSFO.GetValueString("TITLE"); + if (homebrewTitle.empty()) + homebrewTitle = homebrewName; + std::string discID = g_paramSFO.GetDiscID(); + std::string discVersion = g_paramSFO.GetValueString("DISC_VERSION"); std::string madeUpID = g_paramSFO.GenerateFakeID(); - std::string title = StringFromFormat("%s : %s", madeUpID.c_str(), homebrewName.c_str()); + std::string title = StringFromFormat("%s : %s", discID.c_str(), homebrewTitle.c_str()); INFO_LOG(LOADER, "%s", title.c_str()); host->SetWindowTitle(title.c_str()); - // Temporary code - // TODO: Remove this after ~ 1.6 - // It checks for old filenames for homebrew savestates(folder name) and rename them to new fakeID format - std::string savestateDir = GetSysDirectory(DIRECTORY_SAVESTATE); + // Migrate old save states from old versions of fake game IDs. + const std::string savestateDir = GetSysDirectory(DIRECTORY_SAVESTATE); + for (int i = 0; i < 5; ++i) { + std::string newPrefix = StringFromFormat("%s%s_%s_%d", savestateDir.c_str(), discID.c_str(), discVersion.c_str(), i); + std::string oldNamePrefix = StringFromFormat("%s%s_%d", savestateDir.c_str(), homebrewName.c_str(), i); + std::string oldIDPrefix = StringFromFormat("%s%s_1.00_%d", savestateDir.c_str(), madeUpID.c_str(), i); - for (int i = 0; i < 5; i += 1) { - std::string oldName = StringFromFormat("%s%s_%d.ppst", savestateDir.c_str(), homebrewName.c_str(), i); - if (File::Exists(oldName)) { - std::string newName = StringFromFormat("%s%s_1.00_%d.ppst", savestateDir.c_str(), madeUpID.c_str(), i); - File::Rename(oldName, newName); - } + if (oldIDPrefix != newPrefix && File::Exists(oldIDPrefix + ".ppst")) + File::Rename(oldIDPrefix + ".ppst", newPrefix + ".ppst"); + else if (File::Exists(oldNamePrefix + ".ppst")) + File::Rename(oldNamePrefix + ".ppst", newPrefix + ".ppst"); + if (oldIDPrefix != newPrefix && File::Exists(oldIDPrefix + ".jpg")) + File::Rename(oldIDPrefix + ".jpg", newPrefix + ".jpg"); + else if (File::Exists(oldNamePrefix + ".jpg")) + File::Rename(oldNamePrefix + ".jpg", newPrefix + ".jpg"); } - for (int i = 0; i < 5; i += 1) { - std::string oldName = StringFromFormat("%s%s_%d.jpg", savestateDir.c_str(), homebrewName.c_str(), i); - if (File::Exists(oldName)) { - std::string newName = StringFromFormat("%s%s_1.00_%d.jpg", savestateDir.c_str(), madeUpID.c_str(), i); - File::Rename(oldName, newName); - } - } - // End of temporary code PSPLoaders_Shutdown(); // Note: See Load_PSP_ISO for notes about this thread. diff --git a/Core/Replay.cpp b/Core/Replay.cpp index fb03eeea4a..eaf1d5a959 100644 --- a/Core/Replay.cpp +++ b/Core/Replay.cpp @@ -15,6 +15,13 @@ // Official git repository and contact information can be found at // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. +#ifdef __MINGW32__ +#include +#ifndef _POSIX_THREAD_SAFE_FUNCTIONS +#define _POSIX_THREAD_SAFE_FUNCTIONS 200112L +#endif +#endif + #include #include #include diff --git a/Core/Reporting.cpp b/Core/Reporting.cpp index a5870f87c3..350713dffd 100644 --- a/Core/Reporting.cpp +++ b/Core/Reporting.cpp @@ -15,6 +15,7 @@ // Official git repository and contact information can be found at // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. +#include "ppsspp_config.h" #include #include #include @@ -102,6 +103,8 @@ namespace Reporting static std::condition_variable crcCond; static std::string crcFilename; static std::map crcResults; + static volatile bool crcPending = false; + static volatile bool crcCancel = false; static std::thread crcThread; static int CalculateCRCThread() { @@ -113,7 +116,7 @@ namespace Reporting u32 crc = 0; if (blockDevice) { - crc = blockDevice->CalculateCRC(); + crc = blockDevice->CalculateCRC(&crcCancel); } delete blockDevice; @@ -121,15 +124,15 @@ namespace Reporting std::lock_guard guard(crcLock); crcResults[crcFilename] = crc; + crcPending = false; crcCond.notify_one(); return 0; } - void QueueCRC() { + void QueueCRC(const std::string &gamePath) { std::lock_guard guard(crcLock); - const std::string &gamePath = PSP_CoreParameter().fileToStart; auto it = crcResults.find(gamePath); if (it != crcResults.end()) { // Nothing to do, we've already calculated it. @@ -137,18 +140,24 @@ namespace Reporting return; } - if (crcFilename == gamePath) { + if (crcPending) { // Already in process. return; } crcFilename = gamePath; + crcPending = true; + crcCancel = false; crcThread = std::thread(CalculateCRCThread); } - u32 RetrieveCRC() { - const std::string &gamePath = PSP_CoreParameter().fileToStart; - QueueCRC(); + bool HasCRC(const std::string &gamePath) { + std::lock_guard guard(crcLock); + return crcResults.find(gamePath) != crcResults.end(); + } + + uint32_t RetrieveCRC(const std::string &gamePath) { + QueueCRC(gamePath); std::unique_lock guard(crcLock); auto it = crcResults.find(gamePath); @@ -162,6 +171,26 @@ namespace Reporting return it->second; } + static uint32_t RetrieveCRCUnlessPowerSaving(const std::string &gamePath) { + // It's okay to use it if we have it already. + if (Core_GetPowerSaving() && !HasCRC(gamePath)) { + return 0; + } + + return RetrieveCRC(gamePath); + } + + static void PurgeCRC() { + std::unique_lock guard(crcLock); + crcCancel = true; + while (crcPending) { + crcCond.wait(guard); + } + + if (crcThread.joinable()) + crcThread.join(); + } + // Returns the full host (e.g. report.ppsspp.org:80.) std::string ServerHost() { @@ -231,7 +260,6 @@ namespace Reporting bool SendReportRequest(const char *uri, const std::string &data, const std::string &mimeType, Buffer *output = NULL) { - bool result = false; http::Client http; Buffer theVoid; @@ -274,7 +302,7 @@ namespace Reporting return "Windows ARM32"; #elif defined(_WIN32) return "Windows"; -#elif defined(IOS) +#elif PPSSPP_PLATFORM(IOS) return "iOS"; #elif defined(__APPLE__) return "Mac"; @@ -321,6 +349,7 @@ namespace Reporting compatThread.join(); if (messageThread.joinable()) messageThread.join(); + PurgeCRC(); // Just so it can be enabled in the menu again. Init(); @@ -372,7 +401,7 @@ namespace Reporting std::string CurrentGameID() { // TODO: Maybe ParamSFOData shouldn't include nulls in std::strings? Don't work to break savedata, though... - const std::string disc_id = StripTrailingNull(g_paramSFO.GetValueString("DISC_ID")); + const std::string disc_id = StripTrailingNull(g_paramSFO.GetDiscID()); const std::string disc_version = StripTrailingNull(g_paramSFO.GetValueString("DISC_VERSION")); return disc_id + "_" + disc_version; } @@ -468,7 +497,7 @@ namespace Reporting postdata.Add("graphics", StringFromFormat("%d", payload.int1)); postdata.Add("speed", StringFromFormat("%d", payload.int2)); postdata.Add("gameplay", StringFromFormat("%d", payload.int3)); - postdata.Add("crc", StringFromFormat("%08x", Core_GetPowerSaving() ? 0 : RetrieveCRC())); + postdata.Add("crc", StringFromFormat("%08x", RetrieveCRCUnlessPowerSaving(PSP_CoreParameter().fileToStart))); postdata.Add("suggestions", payload.string1 != "perfect" && payload.string1 != "playable" ? "1" : "0"); AddScreenshotData(postdata, payload.string2); payload.string1.clear(); diff --git a/Core/Reporting.h b/Core/Reporting.h index 50b24f93c8..3aa20e4eb9 100644 --- a/Core/Reporting.h +++ b/Core/Reporting.h @@ -87,6 +87,16 @@ namespace Reporting // Get the latest compatibility result. Only valid when GetStatus() is not BUSY. std::vector CompatibilitySuggestions(); + // Queues game for CRC hash if needed. + void QueueCRC(const std::string &gamePath); + + // Returns true if the hash is available, does not queue if not. + bool HasCRC(const std::string &gamePath); + + // Blocks until the CRC hash is available for game, and returns it. + // To avoid stalling, call HasCRC() in update() or similar and call this if it returns true. + uint32_t RetrieveCRC(const std::string &gamePath); + // Returns true if that identifier has not been logged yet. bool ShouldLogNTimes(const char *identifier, int n); diff --git a/Core/SaveState.cpp b/Core/SaveState.cpp index a31274dab9..d7381f53f9 100644 --- a/Core/SaveState.cpp +++ b/Core/SaveState.cpp @@ -249,6 +249,7 @@ namespace SaveState }; static bool needsProcess = false; + static bool needsRestart = false; static std::vector pending; static std::mutex mutex; static int screenshotFailures = 0; @@ -663,17 +664,9 @@ namespace SaveState } // We tried, our only remaining option is to reset the game. - PSP_Shutdown(); - std::string resetError; - if (!PSP_Init(PSP_CoreParameter(), &resetError)) - { - ERROR_LOG(BOOT, "Error resetting: %s", resetError.c_str()); - // TODO: This probably doesn't clean up well enough. - Core_Stop(); - return false; - } - host->BootDone(); - host->UpdateDisassembly(); + needsRestart = true; + // Make sure we don't proceed to run anything yet. + coreState = CORE_NEXTFRAME; return false; } @@ -770,11 +763,11 @@ namespace SaveState // Using save states instead of saves simulates many hour play sessions. // Sometimes this exposes game bugs that were rarely seen on real devices, // because few people played on a real PSP for 10 hours straight. - callbackMessage = slot_prefix + sc->T("Loaded. Save in game, restart, and load for less bugs."); + callbackMessage = slot_prefix + sc->T("Loaded. Save in game, restart, and load for less bugs."); callbackResult = Status::WARNING; } else if (!g_Config.bHideStateWarnings && IsOldVersion()) { // Save states also preserve bugs from old PPSSPP versions, so warn. - callbackMessage = slot_prefix + sc->T("Loaded. Save in game, restart, and load for less bugs."); + callbackMessage = slot_prefix + sc->T("Loaded. Save in game, restart, and load for less bugs."); callbackResult = Status::WARNING; } @@ -904,6 +897,22 @@ namespace SaveState } } + void Cleanup() { + if (needsRestart) { + PSP_Shutdown(); + std::string resetError; + if (!PSP_Init(PSP_CoreParameter(), &resetError)) { + ERROR_LOG(BOOT, "Error resetting: %s", resetError.c_str()); + // TODO: This probably doesn't clean up well enough. + Core_Stop(); + return; + } + host->BootDone(); + host->UpdateDisassembly(); + needsRestart = false; + } + } + void Init() { // Make sure there's a directory for save slots diff --git a/Core/SaveState.h b/Core/SaveState.h index f2c1a3a426..15a7c97462 100644 --- a/Core/SaveState.h +++ b/Core/SaveState.h @@ -93,4 +93,7 @@ namespace SaveState // Check if there's any save stating needing to be done. Normally called once per frame. void Process(); + + // Cleanup by triggering a restart if needed. + void Cleanup(); }; diff --git a/Core/Screenshot.cpp b/Core/Screenshot.cpp index bb8d109c10..8a06d54ab7 100644 --- a/Core/Screenshot.cpp +++ b/Core/Screenshot.cpp @@ -24,6 +24,7 @@ #include "Common/ColorConv.h" #include "Common/File/FileUtil.h" #include "Common/Log.h" +#include "Common/System/Display.h" #include "Core/Config.h" #include "Core/Screenshot.h" #include "Core/Core.h" @@ -295,6 +296,36 @@ const u8 *ConvertBufferToScreenshot(const GPUDebugBuffer &buf, bool alpha, u8 *& return temp ? temp : buffer; } +static GPUDebugBuffer ApplyRotation(const GPUDebugBuffer &buf, DisplayRotation rotation) { + GPUDebugBuffer rotated; + + // This is a simple but not terribly efficient rotation. + if (rotation == DisplayRotation::ROTATE_90) { + rotated.Allocate(buf.GetHeight(), buf.GetStride(), buf.GetFormat(), false); + for (u32 y = 0; y < buf.GetStride(); ++y) { + for (u32 x = 0; x < buf.GetHeight(); ++x) { + rotated.SetRawPixel(x, y, buf.GetRawPixel(buf.GetStride() - y - 1, x)); + } + } + } else if (rotation == DisplayRotation::ROTATE_180) { + rotated.Allocate(buf.GetStride(), buf.GetHeight(), buf.GetFormat(), false); + for (u32 y = 0; y < buf.GetHeight(); ++y) { + for (u32 x = 0; x < buf.GetStride(); ++x) { + rotated.SetRawPixel(x, y, buf.GetRawPixel(buf.GetStride() - x - 1, buf.GetHeight() - y - 1)); + } + } + } else { + rotated.Allocate(buf.GetHeight(), buf.GetStride(), buf.GetFormat(), false); + for (u32 y = 0; y < buf.GetStride(); ++y) { + for (u32 x = 0; x < buf.GetHeight(); ++x) { + rotated.SetRawPixel(x, y, buf.GetRawPixel(y, buf.GetHeight() - x - 1)); + } + } + } + + return rotated; +} + bool TakeGameScreenshot(const char *filename, ScreenshotFormat fmt, ScreenshotType type, int *width, int *height, int maxRes) { if (!gpuDebug) { ERROR_LOG(SYSTEM, "Can't take screenshots when GPU not running"); @@ -311,6 +342,12 @@ bool TakeGameScreenshot(const char *filename, ScreenshotFormat fmt, ScreenshotTy // Only crop to the top left when using a render screenshot. w = maxRes > 0 ? 480 * maxRes : PSP_CoreParameter().renderWidth; h = maxRes > 0 ? 272 * maxRes : PSP_CoreParameter().renderHeight; + } else if (g_display_rotation != DisplayRotation::ROTATE_0) { + GPUDebugBuffer temp; + success = gpuDebug->GetOutputFramebuffer(temp); + if (success) { + buf = ApplyRotation(temp, g_display_rotation); + } } else { success = gpuDebug->GetOutputFramebuffer(buf); } diff --git a/Core/System.cpp b/Core/System.cpp index 16ae2808de..96d6ae5f8a 100644 --- a/Core/System.cpp +++ b/Core/System.cpp @@ -230,7 +230,7 @@ bool CPU_Init() { std::string filename = coreParameter.fileToStart; loadedFile = ResolveFileLoaderTarget(ConstructFileLoader(filename)); -#ifdef _M_X64 +#if PPSSPP_ARCH(AMD64) if (g_Config.bCacheFullIsoInRam) { loadedFile = new RamCachingFileLoader(loadedFile); } @@ -245,51 +245,48 @@ bool CPU_Init() { MIPSAnalyst::Reset(); Replacement_Init(); - std::string discID; + bool allowPlugins = true; + std::string geDumpDiscID; switch (type) { case IdentifiedFileType::PSP_ISO: case IdentifiedFileType::PSP_ISO_NP: case IdentifiedFileType::PSP_DISC_DIRECTORY: InitMemoryForGameISO(loadedFile); - discID = g_paramSFO.GetDiscID(); break; case IdentifiedFileType::PSP_PBP: case IdentifiedFileType::PSP_PBP_DIRECTORY: // This is normal for homebrew. // ERROR_LOG(LOADER, "PBP directory resolution failed."); InitMemoryForGamePBP(loadedFile); - discID = g_paramSFO.GetDiscID(); break; case IdentifiedFileType::PSP_ELF: if (Memory::g_PSPModel != PSP_MODEL_FAT) { INFO_LOG(LOADER, "ELF, using full PSP-2000 memory access"); Memory::g_MemorySize = Memory::RAM_DOUBLE_SIZE; } - discID = g_paramSFO.GetDiscID(); break; case IdentifiedFileType::PPSSPP_GE_DUMP: - // Try to grab the disc ID from the filename, since unfortunately, we don't store - // it in the GE dump. This should probably be fixed, but as long as you don't rename the dumps, - // this will do the trick. - if (!DiscIDFromGEDumpPath(filename, loadedFile, &discID)) { - // Failed? Let the param SFO autogen a fake disc ID. - discID = g_paramSFO.GetDiscID(); + // Try to grab the disc ID from the filenameor GE dump. + if (DiscIDFromGEDumpPath(filename, loadedFile, &geDumpDiscID)) { + // Store in SFO, otherwise it'll generate a fake disc ID. + g_paramSFO.SetValue("DISC_ID", geDumpDiscID, 16); } + allowPlugins = false; break; default: - discID = g_paramSFO.GetDiscID(); break; } // Here we have read the PARAM.SFO, let's see if we need any compatibility overrides. // Homebrew usually has an empty discID, and even if they do have a disc id, it's not // likely to collide with any commercial ones. - coreParameter.compat.Load(discID); + coreParameter.compat.Load(g_paramSFO.GetDiscID()); InitVFPUSinCos(coreParameter.compat.flags().DoublePrecisionSinCos); - HLEPlugins::Init(); + if (allowPlugins) + HLEPlugins::Init(); if (!Memory::Init()) { // We're screwed. return false; @@ -395,9 +392,9 @@ bool PSP_InitStart(const CoreParameter &coreParam, std::string *error_string) { return false; } -#if defined(_WIN32) && defined(_M_X64) +#if defined(_WIN32) && PPSSPP_ARCH(AMD64) INFO_LOG(BOOT, "PPSSPP %s Windows 64 bit", PPSSPP_GIT_VERSION); -#elif defined(_WIN32) && !defined(_M_X64) +#elif defined(_WIN32) && !PPSSPP_ARCH(AMD64) INFO_LOG(BOOT, "PPSSPP %s Windows 32 bit", PPSSPP_GIT_VERSION); #else INFO_LOG(BOOT, "PPSSPP %s", PPSSPP_GIT_VERSION); @@ -415,6 +412,7 @@ bool PSP_InitStart(const CoreParameter &coreParam, std::string *error_string) { if (!CPU_Init()) { *error_string = "Failed initializing CPU/Memory"; + pspIsIniting = false; return false; } @@ -527,6 +525,7 @@ void PSP_EndHostFrame() { if (gpu) { gpu->EndHostFrame(); } + SaveState::Cleanup(); } void PSP_RunLoopWhileState() { diff --git a/Core/TextureReplacer.cpp b/Core/TextureReplacer.cpp index 3b9aeb99e4..8969267b67 100644 --- a/Core/TextureReplacer.cpp +++ b/Core/TextureReplacer.cpp @@ -15,9 +15,9 @@ // Official git repository and contact information can be found at // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. -#include - +#include "ppsspp_config.h" #include +#include #include "ext/xxhash.h" @@ -76,6 +76,7 @@ bool TextureReplacer::LoadIni() { hash_ = ReplacedTextureHash::QUICK; aliases_.clear(); hashranges_.clear(); + filtering_.clear(); allowVideo_ = false; ignoreAddress_ = false; @@ -183,6 +184,14 @@ bool TextureReplacer::LoadIniValues(IniFile &ini, bool isOverride) { } } + if (ini.HasSection("filtering")) { + auto filters = ini.GetOrCreateSection("filtering")->ToMap(); + // Format: hashname = nearest or linear + for (const auto &item : filters) { + ParseFiltering(item.first, item.second); + } + } + return true; } @@ -221,6 +230,23 @@ void TextureReplacer::ParseHashRange(const std::string &key, const std::string & hashranges_[rangeKey] = WidthHeightPair(toW, toH); } +void TextureReplacer::ParseFiltering(const std::string &key, const std::string &value) { + ReplacementCacheKey itemKey(0, 0); + if (sscanf(key.c_str(), "%16llx%8x", &itemKey.cachekey, &itemKey.hash) >= 1) { + if (!strcasecmp(value.c_str(), "nearest")) { + filtering_[itemKey] = TEX_FILTER_FORCE_NEAREST; + } else if (!strcasecmp(value.c_str(), "linear")) { + filtering_[itemKey] = TEX_FILTER_FORCE_LINEAR; + } else if (!strcasecmp(value.c_str(), "auto")) { + filtering_[itemKey] = TEX_FILTER_AUTO; + } else { + ERROR_LOG(G3D, "Unsupported syntax under [filtering]: %s", value.c_str()); + } + } else { + ERROR_LOG(G3D, "Unsupported syntax under [filtering]: %s", key.c_str()); + } +} + u32 TextureReplacer::ComputeHash(u32 addr, int bufw, int w, int h, GETextureFormat fmt, u16 maxSeenV) { _dbg_assert_msg_(enabled_, "Replacement not enabled"); @@ -503,45 +529,74 @@ void TextureReplacer::NotifyTextureDecoded(const ReplacedTextureDecodeInfo &repl savedCache_[replacementKey] = saved; } -std::string TextureReplacer::LookupHashFile(u64 cachekey, u32 hash, int level) { - ReplacementAliasKey key(cachekey, hash, level); - auto alias = aliases_.find(key); - if (alias == aliases_.end()) { - // Also check for a few more aliases with zeroed portions: - // Only clut hash (very dangerous in theory, in practice not more than missing "just" data hash) - key.cachekey = cachekey & 0xFFFFFFFFULL; +template +static typename std::unordered_map::const_iterator LookupWildcard(const std::unordered_map &map, Key &key, u64 cachekey, u32 hash, bool ignoreAddress) { + auto alias = map.find(key); + if (alias != map.end()) + return alias; + + // Also check for a few more aliases with zeroed portions: + // Only clut hash (very dangerous in theory, in practice not more than missing "just" data hash) + key.cachekey = cachekey & 0xFFFFFFFFULL; + key.hash = 0; + alias = map.find(key); + if (alias != map.end()) + return alias; + + if (!ignoreAddress) { + // No data hash. + key.cachekey = cachekey; key.hash = 0; - alias = aliases_.find(key); - - if (!ignoreAddress_ && alias == aliases_.end()) { - // No data hash. - key.cachekey = cachekey; - key.hash = 0; - alias = aliases_.find(key); - } - - if (alias == aliases_.end()) { - // No address. - key.cachekey = cachekey & 0xFFFFFFFFULL; - key.hash = hash; - alias = aliases_.find(key); - } - - if (!ignoreAddress_ && alias == aliases_.end()) { - // Address, but not clut hash (in case of garbage clut data.) - key.cachekey = cachekey & ~0xFFFFFFFFULL; - key.hash = hash; - alias = aliases_.find(key); - } - - if (alias == aliases_.end()) { - // Anything with this data hash (a little dangerous.) - key.cachekey = 0; - key.hash = hash; - alias = aliases_.find(key); - } + alias = map.find(key); + if (alias != map.end()) + return alias; } + // No address. + key.cachekey = cachekey & 0xFFFFFFFFULL; + key.hash = hash; + alias = map.find(key); + if (alias != map.end()) + return alias; + + if (!ignoreAddress) { + // Address, but not clut hash (in case of garbage clut data.) + key.cachekey = cachekey & ~0xFFFFFFFFULL; + key.hash = hash; + alias = map.find(key); + if (alias != map.end()) + return alias; + } + + // Anything with this data hash (a little dangerous.) + key.cachekey = 0; + key.hash = hash; + return map.find(key); +} + +bool TextureReplacer::FindFiltering(u64 cachekey, u32 hash, TextureFiltering *forceFiltering) { + if (!Enabled() || !g_Config.bReplaceTextures) { + return false; + } + + ReplacementCacheKey replacementKey(cachekey, hash); + auto filter = LookupWildcard(filtering_, replacementKey, cachekey, hash, ignoreAddress_); + if (filter == filtering_.end()) { + // Allow a global wildcard. + replacementKey.cachekey = 0; + replacementKey.hash = 0; + filter = filtering_.find(replacementKey); + } + if (filter != filtering_.end()) { + *forceFiltering = filter->second; + return true; + } + return false; +} + +std::string TextureReplacer::LookupHashFile(u64 cachekey, u32 hash, int level) { + ReplacementAliasKey key(cachekey, hash, level); + auto alias = LookupWildcard(aliases_, key, cachekey, hash, ignoreAddress_); if (alias != aliases_.end()) { // Note: this will be blank if explicitly ignored. return alias->second; @@ -649,13 +704,15 @@ bool TextureReplacer::GenerateIni(const std::string &gameID, std::string *genera fs << "[games]\n"; fs << "# Used to make it easier to install, and override settings for other regions.\n"; fs << "# Files still have to be copied to each TEXTURES folder."; - fs << gameID << " = textures.ini\n"; + fs << gameID << " = " << INI_FILENAME << "\n"; fs << "\n"; + fs << "[hashes]\n"; fs << "# Use / for folders not \\, avoid special characters, and stick to lowercase.\n"; fs << "# See wiki for more info.\n"; - fs << "[hashes]\n"; fs << "\n"; fs << "[hashranges]\n"; + fs << "\n"; + fs << "[filtering]\n"; fs.close(); } return File::Exists(texturesDirectory + INI_FILENAME); diff --git a/Core/TextureReplacer.h b/Core/TextureReplacer.h index d90f373c4e..46d67037ed 100644 --- a/Core/TextureReplacer.h +++ b/Core/TextureReplacer.h @@ -23,6 +23,7 @@ #include #include "Common/Common.h" #include "Common/MemoryUtil.h" +#include "GPU/Common/TextureDecoder.h" #include "GPU/ge_constants.h" class IniFile; @@ -183,6 +184,7 @@ public: u32 ComputeHash(u32 addr, int bufw, int w, int h, GETextureFormat fmt, u16 maxSeenV); ReplacedTexture &FindReplacement(u64 cachekey, u32 hash, int w, int h); + bool FindFiltering(u64 cachekey, u32 hash, TextureFiltering *forceFiltering); void NotifyTextureDecoded(const ReplacedTextureDecodeInfo &replacedInfo, const void *data, int pitch, int level, int w, int h); @@ -192,6 +194,7 @@ protected: bool LoadIni(); bool LoadIniValues(IniFile &ini, bool isOverride = false); void ParseHashRange(const std::string &key, const std::string &value); + void ParseFiltering(const std::string &key, const std::string &value); bool LookupHashRange(u32 addr, int &w, int &h); std::string LookupHashFile(u64 cachekey, u32 hash, int level); std::string HashName(u64 cachekey, u32 hash, int level); @@ -209,6 +212,7 @@ protected: typedef std::pair WidthHeightPair; std::unordered_map hashranges_; std::unordered_map aliases_; + std::unordered_map filtering_; ReplacedTexture none_; std::unordered_map cache_; diff --git a/Core/Util/AudioFormat.cpp b/Core/Util/AudioFormat.cpp index 54610949f3..e118c509b4 100644 --- a/Core/Util/AudioFormat.cpp +++ b/Core/Util/AudioFormat.cpp @@ -15,6 +15,7 @@ // Official git repository and contact information can be found at // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. +#include "ppsspp_config.h" #include "Common/Common.h" #include "Common/CPUDetect.h" #include "Core/Util/AudioFormat.h" diff --git a/Core/Util/BlockAllocator.cpp b/Core/Util/BlockAllocator.cpp index 7dfa4637ba..696a43513d 100644 --- a/Core/Util/BlockAllocator.cpp +++ b/Core/Util/BlockAllocator.cpp @@ -21,6 +21,7 @@ #include "Common/Serialize/Serializer.h" #include "Common/Serialize/SerializeFuncs.h" #include "Common/StringUtils.h" +#include "Core/Debugger/MemBlockInfo.h" #include "Core/Util/BlockAllocator.h" #include "Core/Reporting.h" @@ -35,14 +36,14 @@ BlockAllocator::~BlockAllocator() Shutdown(); } -void BlockAllocator::Init(u32 rangeStart, u32 rangeSize) -{ +void BlockAllocator::Init(u32 rangeStart, u32 rangeSize, bool suballoc) { Shutdown(); rangeStart_ = rangeStart; rangeSize_ = rangeSize; //Initial block, covering everything top_ = new Block(rangeStart_, rangeSize_, false, NULL, NULL); bottom_ = top_; + suballoc_ = suballoc; } void BlockAllocator::Shutdown() @@ -90,7 +91,7 @@ u32 BlockAllocator::AllocAligned(u32 &size, u32 sizeGrain, u32 grain, bool fromT if (offset >= grain_) InsertFreeBefore(&b, offset); b.taken = true; - b.SetTag(tag); + b.SetAllocated(tag, suballoc_); return b.start; } else @@ -99,7 +100,7 @@ u32 BlockAllocator::AllocAligned(u32 &size, u32 sizeGrain, u32 grain, bool fromT if (offset >= grain_) InsertFreeBefore(&b, offset); b.taken = true; - b.SetTag(tag); + b.SetAllocated(tag, suballoc_); return b.start; } } @@ -120,7 +121,7 @@ u32 BlockAllocator::AllocAligned(u32 &size, u32 sizeGrain, u32 grain, bool fromT if (offset >= grain_) InsertFreeAfter(&b, offset); b.taken = true; - b.SetTag(tag); + b.SetAllocated(tag, suballoc_); return b.start; } else @@ -129,7 +130,7 @@ u32 BlockAllocator::AllocAligned(u32 &size, u32 sizeGrain, u32 grain, bool fromT if (offset >= grain_) InsertFreeAfter(&b, offset); b.taken = true; - b.SetTag(tag); + b.SetAllocated(tag, suballoc_); return b.start; } } @@ -195,7 +196,7 @@ u32 BlockAllocator::AllocAt(u32 position, u32 size, const char *tag) if (b.size != alignedSize) InsertFreeAfter(&b, b.size - alignedSize); b.taken = true; - b.SetTag(tag); + b.SetAllocated(tag, suballoc_); CheckBlocks(); return position; } @@ -205,7 +206,7 @@ u32 BlockAllocator::AllocAt(u32 position, u32 size, const char *tag) if (b.size > alignedSize) InsertFreeAfter(&b, b.size - alignedSize); b.taken = true; - b.SetTag(tag); + b.SetAllocated(tag, suballoc_); return position; } @@ -268,6 +269,7 @@ bool BlockAllocator::Free(u32 position) Block *b = GetBlockFromAddress(position); if (b && b->taken) { + NotifyMemInfo(suballoc_ ? MemBlockFlags::SUB_FREE : MemBlockFlags::FREE, b->start, b->size, ""); b->taken = false; MergeFreeBlocks(b); return true; @@ -284,6 +286,7 @@ bool BlockAllocator::FreeExact(u32 position) Block *b = GetBlockFromAddress(position); if (b && b->taken && b->start == position) { + NotifyMemInfo(suballoc_ ? MemBlockFlags::SUB_FREE : MemBlockFlags::FREE, b->start, b->size, ""); b->taken = false; MergeFreeBlocks(b); return true; @@ -485,8 +488,8 @@ BlockAllocator::Block::Block(u32 _start, u32 _size, bool _taken, Block *_prev, B truncate_cpy(tag, "(untitled)"); } -void BlockAllocator::Block::SetTag(const char *_tag) -{ +void BlockAllocator::Block::SetAllocated(const char *_tag, bool suballoc) { + NotifyMemInfo(suballoc ? MemBlockFlags::SUB_ALLOC : MemBlockFlags::ALLOC, start, size, _tag ? _tag : ""); if (_tag) truncate_cpy(tag, _tag); else diff --git a/Core/Util/BlockAllocator.h b/Core/Util/BlockAllocator.h index 32bde54aac..91e9642462 100644 --- a/Core/Util/BlockAllocator.h +++ b/Core/Util/BlockAllocator.h @@ -27,7 +27,7 @@ public: BlockAllocator(int grain = 16); // 16 byte granularity by default. ~BlockAllocator(); - void Init(u32 _rangeStart, u32 _rangeSize); + void Init(u32 _rangeStart, u32 _rangeSize, bool suballoc); void Shutdown(); void ListBlocks() const; @@ -62,7 +62,7 @@ private: struct Block { Block(u32 _start, u32 _size, bool _taken, Block *_prev, Block *_next); - void SetTag(const char *_tag); + void SetAllocated(const char *_tag, bool suballoc); void DoState(PointerWrap &p); u32 start; u32 size; @@ -78,6 +78,7 @@ private: u32 rangeSize_; u32 grain_; + bool suballoc_; void MergeFreeBlocks(Block *fromBlock); Block *GetBlockFromAddress(u32 addr); diff --git a/Core/Util/DisArm64.cpp b/Core/Util/DisArm64.cpp index 651ffe35c9..5d1afbe786 100644 --- a/Core/Util/DisArm64.cpp +++ b/Core/Util/DisArm64.cpp @@ -85,7 +85,6 @@ int HighestSetBit(int value) { } int LowestSetBit(int value, int maximum = 32) { - int lowest = 0; for (int i = 0; i < maximum; i++) { if (value & (1 << i)) return i; diff --git a/Core/Util/PPGeDraw.cpp b/Core/Util/PPGeDraw.cpp index f7b608d737..ac0f04dee6 100644 --- a/Core/Util/PPGeDraw.cpp +++ b/Core/Util/PPGeDraw.cpp @@ -17,14 +17,14 @@ #include -#include "Common/Render/TextureAtlas.h" -#include "Common/Render/Text/draw_text.h" - +#include "ext/xxhash.h" #include "Common/Data/Color/RGBAUtil.h" #include "Common/File/VFS/VFS.h" #include "Common/Data/Format/ZIMLoad.h" #include "Common/Data/Format/PNGLoad.h" #include "Common/Data/Encoding/Utf8.h" +#include "Common/Render/TextureAtlas.h" +#include "Common/Render/Text/draw_text.h" #include "Common/Serialize/Serializer.h" #include "Common/Serialize/SerializeFuncs.h" #include "Common/StringUtils.h" @@ -41,12 +41,14 @@ #include "Core/MemMapHelpers.h" #include "Core/System.h" -Atlas g_ppge_atlas; -Draw::DrawContext *g_draw = nullptr; +static Atlas g_ppge_atlas; +static Draw::DrawContext *g_draw = nullptr; static u32 atlasPtr; static int atlasWidth; static int atlasHeight; +static uint64_t atlasHash; +static bool atlasRequiresReset; struct PPGeVertex { u16_le u, v; @@ -125,7 +127,7 @@ struct PPGeTextDrawerImage { TextStringEntry entry; u32 ptr; }; -std::map textDrawerImages; +static std::map textDrawerImages; void PPGeSetDrawContext(Draw::DrawContext *draw) { g_draw = draw; @@ -141,6 +143,8 @@ void PPGePrepareText(const char *text, float x, float y, PPGeAlign align, float // Clears the buffer and state when done. void PPGeDrawCurrentText(u32 color = 0xFFFFFFFF); +static void PPGeDecimateTextImages(int age = 97); + void PPGeSetTexture(u32 dataAddr, int width, int height); //only 0xFFFFFF of data is used @@ -195,11 +199,22 @@ static void EndVertexDataAndDraw(int prim) { WriteCmd(GE_CMD_PRIM, (prim << 16) | vertexCount); } +bool PPGeIsFontTextureAddress(u32 addr) { + return addr == atlasPtr; +} + static u32 __PPGeDoAlloc(u32 &size, bool fromTop, const char *name) { u32 ptr = kernelMemory.Alloc(size, fromTop, name); - // Didn't get it. - if (ptr == (u32)-1) - return 0; + // Didn't get it, try again after decimating images. + if (ptr == (u32)-1) { + PPGeDecimateTextImages(4); + PPGeImage::Decimate(4); + + ptr = kernelMemory.Alloc(size, fromTop, name); + if (ptr == (u32)-1) { + return 0; + } + } return ptr; } @@ -269,6 +284,7 @@ void __PPGeInit() { u8 cval = (a2 << 4) | a1; ramPtr[i] = cval; } + atlasHash = XXH3_64bits(ramPtr, atlasWidth * atlasHeight / 2); free(imageData[0]); @@ -278,13 +294,15 @@ void __PPGeInit() { textDrawer = nullptr; textDrawerImages.clear(); + atlasRequiresReset = false; + INFO_LOG(SCEGE, "PPGe drawing library initialized. DL: %08x Data: %08x Atlas: %08x (%i) Args: %08x", dlPtr, dataPtr, atlasPtr, atlasSize, listArgs.ptr); } void __PPGeDoState(PointerWrap &p) { - auto s = p.Section("PPGeDraw", 1, 3); + auto s = p.Section("PPGeDraw", 1, 4); if (!s) return; @@ -293,6 +311,20 @@ void __PPGeDoState(PointerWrap &p) Do(p, atlasHeight); Do(p, palette); + // If the atlas the save state was created with differs from the current one, reload. + uint64_t savedHash = atlasHash; + if (s >= 4) { + Do(p, savedHash); + } else { + // Memory was already updated by this point, so check directly. + if (atlasPtr != 0) { + savedHash = XXH3_64bits(Memory::GetPointer(atlasPtr), atlasWidth * atlasHeight / 2); + } else { + savedHash ^= 1; + } + } + atlasRequiresReset = savedHash != atlasHash; + Do(p, savedContextPtr); Do(p, savedContextSize); @@ -364,6 +396,10 @@ void __PPGeShutdown() delete textDrawer; textDrawer = nullptr; + + for (auto im : textDrawerImages) + kernelMemory.Free(im.second.ptr); + textDrawerImages.clear(); } void PPGeBegin() @@ -710,7 +746,7 @@ static bool HasTextDrawer() { return textDrawer != nullptr; } - // TODO: Should we pass a draw_? Yes! UWP requires it. + // Should we pass a draw_? Yes! UWP requires it. textDrawer = TextDrawer::Create(g_draw); if (textDrawer) { textDrawer->SetFontScale(1.0f, 1.0f); @@ -906,6 +942,18 @@ static void PPGeDrawTextImage(PPGeTextDrawerImage im, float x, float y, const PP PPGeSetDefaultTexture(); } +static void PPGeDecimateTextImages(int age) { + // Do this always, in case the platform has no TextDrawer but save state did. + for (auto it = textDrawerImages.begin(); it != textDrawerImages.end(); ) { + if (gpuStats.numFlips - it->second.entry.lastUsedFrame >= age) { + kernelMemory.Free(it->second.ptr); + it = textDrawerImages.erase(it); + } else { + ++it; + } + } +} + void PPGeDrawText(const char *text, float x, float y, const PPGeStyle &style) { if (!text || !strlen(text)) { return; @@ -913,8 +961,10 @@ void PPGeDrawText(const char *text, float x, float y, const PPGeStyle &style) { if (HasTextDrawer()) { PPGeTextDrawerImage im = PPGeGetTextImage(text, style, 480.0f - x, false); - PPGeDrawTextImage(im, x, y, style); - return; + if (im.ptr) { + PPGeDrawTextImage(im, x, y, style); + return; + } } if (style.hasShadow) { @@ -957,6 +1007,7 @@ void PPGeDrawTextWrapped(const char *text, float x, float y, float wrapWidth, fl } int zoom = (PSP_CoreParameter().pixelHeight + 479) / 480; + zoom = std::min(zoom, PSP_CoreParameter().renderScaleFactor); float maxScaleDown = zoom == 1 ? 1.3f : 2.0f; if (HasTextDrawer()) { @@ -987,8 +1038,10 @@ void PPGeDrawTextWrapped(const char *text, float x, float y, float wrapWidth, fl } PPGeTextDrawerImage im = PPGeGetTextImage(s.c_str(), adjustedStyle, wrapWidth <= 0 ? 480.0f - x : wrapWidth, true); - PPGeDrawTextImage(im, x, y, adjustedStyle); - return; + if (im.ptr) { + PPGeDrawTextImage(im, x, y, adjustedStyle); + return; + } } int sx = style.hasShadow ? 1 : 0; @@ -1230,8 +1283,8 @@ bool PPGeImage::Load() { return false; } - Memory::Memcpy(texture_, textureData, dataSize); - Memory::Memset(texture_ + dataSize, 0, texSize - dataSize); + Memory::Memcpy(texture_, textureData, dataSize, "PPGeTex"); + Memory::Memset(texture_ + dataSize, 0, texSize - dataSize, "PPGeTexClear"); free(textureData); lastFrame_ = gpuStats.numFlips; @@ -1268,9 +1321,8 @@ void PPGeImage::CompatLoad(u32 texture, int width, int height) { height_ = height; } -void PPGeImage::Decimate() { - static const int TOO_OLD_AGE = 30; - int tooOldFrame = gpuStats.numFlips - TOO_OLD_AGE; +void PPGeImage::Decimate(int age) { + int tooOldFrame = gpuStats.numFlips - age; for (size_t i = 0; i < loadedTextures_.size(); ++i) { if (loadedTextures_[i]->lastFrame_ < tooOldFrame) { loadedTextures_[i]->Free(); @@ -1299,15 +1351,11 @@ void PPGeNotifyFrame() { textDrawer->OncePerFrame(); } - // Do this always, in case the platform has no TextDrawer but save state did. - for (auto it = textDrawerImages.begin(); it != textDrawerImages.end(); ) { - if (it->second.entry.lastUsedFrame - gpuStats.numFlips >= 97) { - kernelMemory.Free(it->second.ptr); - it = textDrawerImages.erase(it); - } else { - ++it; - } - } - + PPGeDecimateTextImages(); PPGeImage::Decimate(); + + if (atlasRequiresReset) { + __PPGeShutdown(); + __PPGeInit(); + } } diff --git a/Core/Util/PPGeDraw.h b/Core/Util/PPGeDraw.h index 564f1e27ec..369d7a4bf4 100644 --- a/Core/Util/PPGeDraw.h +++ b/Core/Util/PPGeDraw.h @@ -108,6 +108,9 @@ void PPGeScissorReset(); void PPGeNotifyFrame(); +// Could have returned the address directly I guess, but nothing out side of PPGe should actually use it so.. +bool PPGeIsFontTextureAddress(u32 addr); + class PPGeImage { public: PPGeImage(const std::string &pspFilename); @@ -133,7 +136,7 @@ public: return height_; } - static void Decimate(); + static void Decimate(int age = 30); private: static std::vector loadedTextures_; diff --git a/Core/Util/PortManager.cpp b/Core/Util/PortManager.cpp index 2ea62371e8..a8ef97e034 100644 --- a/Core/Util/PortManager.cpp +++ b/Core/Util/PortManager.cpp @@ -68,7 +68,9 @@ PortManager::~PortManager() { void PortManager::Terminate() { VERBOSE_LOG(SCENET, "PortManager::Terminate()"); if (urls) { +#if !PPSSPP_PLATFORM(SWITCH) FreeUPNPUrls(urls); +#endif free(urls); urls = NULL; } @@ -86,6 +88,7 @@ void PortManager::Terminate() { } bool PortManager::Initialize(const unsigned int timeout) { +#if !PPSSPP_PLATFORM(SWITCH) // Windows: Assuming WSAStartup already called beforehand struct UPNPDev* devlist; struct UPNPDev* dev; @@ -181,6 +184,7 @@ bool PortManager::Initialize(const unsigned int timeout) { auto n = GetI18NCategory("Networking"); host->NotifyUserMessage(n->T("Unable to find UPnP device"), 2.0f, 0x0000ff); m_InitState = UPNP_INITSTATE_NONE; +#endif // !PPSSPP_PLATFORM(SWITCH) return false; } @@ -189,6 +193,7 @@ int PortManager::GetInitState() { } bool PortManager::Add(const char* protocol, unsigned short port, unsigned short intport) { +#if !PPSSPP_PLATFORM(SWITCH) char port_str[16]; char intport_str[16]; int r; @@ -235,9 +240,13 @@ bool PortManager::Add(const char* protocol, unsigned short port, unsigned short if (el_it != m_otherPortList.end()) el_it->taken = true; } return true; +#else + return false; +#endif // !PPSSPP_PLATFORM(SWITCH) } bool PortManager::Remove(const char* protocol, unsigned short port) { +#if !PPSSPP_PLATFORM(SWITCH) char port_str[16]; INFO_LOG(SCENET, "PortManager::Remove(%s, %d)", protocol, port); @@ -262,9 +271,13 @@ bool PortManager::Remove(const char* protocol, unsigned short port) { (it->first == port_str && it->second == protocol) ? it = m_portList.erase(it) : ++it; } return true; +#else + return false; +#endif // !PPSSPP_PLATFORM(SWITCH) } bool PortManager::Restore() { +#if !PPSSPP_PLATFORM(SWITCH) int r; VERBOSE_LOG(SCENET, "PortManager::Restore()"); if (urls == NULL || urls->controlURL == NULL || urls->controlURL[0] == '\0') @@ -304,9 +317,13 @@ bool PortManager::Restore() { } } return true; +#else + return false; +#endif // !PPSSPP_PLATFORM(SWITCH) } bool PortManager::Clear() { +#if !PPSSPP_PLATFORM(SWITCH) int r; int i = 0; char index[6]; @@ -358,9 +375,13 @@ bool PortManager::Clear() { i++; } while (r == 0); return true; +#else + return false; +#endif // !PPSSPP_PLATFORM(SWITCH) } bool PortManager::RefreshPortList() { +#if !PPSSPP_PLATFORM(SWITCH) int r; int i = 0; char index[6]; @@ -411,6 +432,9 @@ bool PortManager::RefreshPortList() { i++; } while (r == 0); return true; +#else + return false; +#endif // !PPSSPP_PLATFORM(SWITCH) } int upnpService(const unsigned int timeout) diff --git a/Core/WaveFile.h b/Core/WaveFile.h index 3ef3f09b4c..8198cecf50 100644 --- a/Core/WaveFile.h +++ b/Core/WaveFile.h @@ -36,7 +36,6 @@ private: File::IOFile file; bool skip_silence = false; uint32_t audio_size = 0; - std::array conv_buffer{}; void Write(uint32_t value); void Write4(const char* ptr); }; diff --git a/GPU/Common/DrawEngineCommon.cpp b/GPU/Common/DrawEngineCommon.cpp index 1784813175..2393431c76 100644 --- a/GPU/Common/DrawEngineCommon.cpp +++ b/GPU/Common/DrawEngineCommon.cpp @@ -270,8 +270,8 @@ bool DrawEngineCommon::GetCurrentSimpleVertices(int count, std::vector> GE_VTYPE_IDX_SHIFT) { // Decode the verts and apply morphing. Simple. dec_->DecodeVerts(dest + decodedVerts * (int)dec_->GetDecVtxFmt().stride, @@ -599,8 +598,9 @@ void DrawEngineCommon::DecodeVertsStep(u8 *dest, int &i, int &decodedVerts) { } inline u32 ComputeMiniHashRange(const void *ptr, size_t sz) { - // Switch to u32 units. - const u32 *p = (const u32 *)ptr; + // Switch to u32 units, and round up to avoid unaligned accesses. + // Probably doesn't matter if we skip the first few bytes in some cases. + const u32 *p = (const u32 *)(((uintptr_t)ptr + 3) & ~3); sz >>= 2; if (sz > 100) { diff --git a/GPU/Common/FragmentShaderGenerator.cpp b/GPU/Common/FragmentShaderGenerator.cpp index 430ed80669..2d8c52b594 100644 --- a/GPU/Common/FragmentShaderGenerator.cpp +++ b/GPU/Common/FragmentShaderGenerator.cpp @@ -78,7 +78,10 @@ bool GenerateFragmentShader(const FShaderID &id, char *buffer, const ShaderLangu bool enableColorDoubling = id.Bit(FS_BIT_COLOR_DOUBLE); bool doTextureProjection = id.Bit(FS_BIT_DO_TEXTURE_PROJ); bool doTextureAlpha = id.Bit(FS_BIT_TEXALPHA); - bool doFlatShading = id.Bit(FS_BIT_FLATSHADE) && !bugs.Has(Draw::Bugs::BROKEN_FLAT_IN_SHADER); + + bool flatBug = bugs.Has(Draw::Bugs::BROKEN_FLAT_IN_SHADER) && g_Config.bVendorBugChecksEnabled; + + bool doFlatShading = id.Bit(FS_BIT_FLATSHADE) && !flatBug; bool shaderDepal = id.Bit(FS_BIT_SHADER_DEPAL); bool bgraTexture = id.Bit(FS_BIT_BGRA_TEXTURE); bool colorWriteMask = id.Bit(FS_BIT_COLOR_WRITEMASK) && compat.bitwiseOps; @@ -154,10 +157,10 @@ bool GenerateFragmentShader(const FShaderID &id, char *buffer, const ShaderLangu } if (enableAlphaTest && !alphaTestAgainstZero) { - WRITE(p, "int roundAndScaleTo255i(in float x) { return int(floor(x * 255.0 + 0.5)); }\n"); + WRITE(p, "int roundAndScaleTo255i(in highp float x) { return int(floor(x * 255.0 + 0.5)); }\n"); } if (enableColorTest && !colorTestAgainstZero) { - WRITE(p, "ivec3 roundAndScaleTo255iv(in vec3 x) { return ivec3(floor(x * 255.0 + 0.5)); }\n"); + WRITE(p, "ivec3 roundAndScaleTo255iv(in highp vec3 x) { return ivec3(floor(x * 255.0 + 0.5)); }\n"); } WRITE(p, "layout (location = 0, index = 0) out vec4 fragColor0;\n"); @@ -359,9 +362,9 @@ bool GenerateFragmentShader(const FShaderID &id, char *buffer, const ShaderLangu WRITE(p, "uniform vec3 u_texenv;\n"); } - WRITE(p, "%s %s vec4 v_color0;\n", shading, compat.varying_fs); + WRITE(p, "%s %s lowp vec4 v_color0;\n", shading, compat.varying_fs); if (lmode) - WRITE(p, "%s %s vec3 v_color1;\n", shading, compat.varying_fs); + WRITE(p, "%s %s lowp vec3 v_color1;\n", shading, compat.varying_fs); if (enableFog) { *uniformMask |= DIRTY_FOGCOLOR; WRITE(p, "uniform vec3 u_fogcolor;\n"); @@ -420,7 +423,7 @@ bool GenerateFragmentShader(const FShaderID &id, char *buffer, const ShaderLangu // Provide implementations of packUnorm4x8 and unpackUnorm4x8 if not available. if (colorWriteMask && !hasPackUnorm4x8) { - WRITE(p, "uint packUnorm4x8(vec4 v) {\n"); + WRITE(p, "uint packUnorm4x8(%svec4 v) {\n", compat.shaderLanguage == GLSL_VULKAN ? "highp " : ""); WRITE(p, " highp vec4 f = clamp(v, 0.0, 1.0);\n"); WRITE(p, " uvec4 u = uvec4(255.0 * f);\n"); WRITE(p, " return u.x | (u.y << 8) | (u.z << 16) | (u.w << 24);\n"); diff --git a/GPU/Common/FramebufferManagerCommon.cpp b/GPU/Common/FramebufferManagerCommon.cpp index 9ca2df8890..a56ff5958d 100644 --- a/GPU/Common/FramebufferManagerCommon.cpp +++ b/GPU/Common/FramebufferManagerCommon.cpp @@ -28,7 +28,9 @@ #include "Core/ConfigValues.h" #include "Core/Core.h" #include "Core/CoreParameter.h" +#include "Core/Debugger/MemBlockInfo.h" #include "Core/Host.h" +#include "Core/MIPS/MIPS.h" #include "Core/Reporting.h" #include "GPU/Common/DrawEngineCommon.h" #include "GPU/Common/FramebufferManagerCommon.h" @@ -48,6 +50,8 @@ FramebufferManagerCommon::FramebufferManagerCommon(Draw::DrawContext *draw) } FramebufferManagerCommon::~FramebufferManagerCommon() { + DeviceLost(); + DecimateFBOs(); for (auto vfb : vfbs_) { DestroyFramebuf(vfb); @@ -519,6 +523,25 @@ void FramebufferManagerCommon::NotifyRenderFramebufferUpdated(VirtualFramebuffer } } +// Can't easily dynamically create these strings, we just pass along the pointer. +static const char *reinterpretStrings[3][3] = { + { + "self_reinterpret_565", + "reinterpret_565_to_5551", + "reinterpret_565_to_4444", + }, + { + "reinterpret_5551_to_565", + "self_reinterpret_5551", + "reinterpret_5551_to_4444", + }, + { + "reinterpret_4444_to_565", + "reinterpret_4444_to_5551", + "self_reinterpret_4444", + }, +}; + void FramebufferManagerCommon::ReinterpretFramebuffer(VirtualFramebuffer *vfb, GEBufferFormat oldFormat, GEBufferFormat newFormat) { if (!useBufferedRendering_ || !vfb->fbo) { return; @@ -532,6 +555,9 @@ void FramebufferManagerCommon::ReinterpretFramebuffer(VirtualFramebuffer *vfb, G bool doReinterpret = PSP_CoreParameter().compat.flags().ReinterpretFramebuffers && (lang == HLSL_D3D11 || lang == GLSL_VULKAN || lang == GLSL_3xx); + // Copy image required for now. + if (!gstate_c.Supports(GPU_SUPPORTS_COPY_IMAGE)) + doReinterpret = false; if (!doReinterpret) { // Fake reinterpret - just clear the way we always did on Vulkan. Just clear color and stencil. if (oldFormat == GE_FORMAT_565) { @@ -619,7 +645,7 @@ void FramebufferManagerCommon::ReinterpretFramebuffer(VirtualFramebuffer *vfb, G draw_->InvalidateCachedState(); draw_->CopyFramebufferImage(vfb->fbo, 0, 0, 0, 0, temp, 0, 0, 0, 0, vfb->renderWidth, vfb->renderHeight, 1, Draw::FBChannel::FB_COLOR_BIT, "reinterpret_prep"); - draw_->BindFramebufferAsRenderTarget(vfb->fbo, { Draw::RPAction::DONT_CARE, Draw::RPAction::DONT_CARE, Draw::RPAction::DONT_CARE }, "reinterpret"); + draw_->BindFramebufferAsRenderTarget(vfb->fbo, { Draw::RPAction::DONT_CARE, Draw::RPAction::DONT_CARE, Draw::RPAction::DONT_CARE }, reinterpretStrings[(int)oldFormat][(int)newFormat]); draw_->BindPipeline(pipeline); draw_->BindFramebufferAsTexture(temp, 0, Draw::FBChannel::FB_COLOR_BIT, 0); draw_->BindSamplerStates(0, 1, &reinterpretSampler_); @@ -681,20 +707,7 @@ void FramebufferManagerCommon::NotifyRenderFramebufferSwitched(VirtualFramebuffe if (useBufferedRendering_) { if (vfb->fbo) { shaderManager_->DirtyLastShader(); - if (g_Config.bClearFramebuffersOnFirstUseHack) { - // HACK: Some tiled mobile GPUs benefit IMMENSELY from clearing an FBO before rendering - // to it (or in Vulkan, clear during framebuffer load). This is a hack to force this - // the first time a framebuffer is bound for rendering in a frame. - // - // Quite unsafe as it might kill some feedback effects. - if (vfb->last_frame_render != gpuStats.numFlips) { - draw_->BindFramebufferAsRenderTarget(vfb->fbo, { Draw::RPAction::CLEAR, Draw::RPAction::CLEAR, Draw::RPAction::CLEAR }, "FramebufferSwitch"); - } else { - draw_->BindFramebufferAsRenderTarget(vfb->fbo, { Draw::RPAction::KEEP, Draw::RPAction::KEEP, Draw::RPAction::KEEP }, "FramebufferSwitch"); - } - } else { - draw_->BindFramebufferAsRenderTarget(vfb->fbo, { Draw::RPAction::KEEP, Draw::RPAction::KEEP, Draw::RPAction::KEEP }, "FramebufferSwitch"); - } + draw_->BindFramebufferAsRenderTarget(vfb->fbo, { Draw::RPAction::KEEP, Draw::RPAction::KEEP, Draw::RPAction::KEEP }, "FramebufferSwitch"); } else { // This should only happen very briefly when toggling useBufferedRendering_. ResizeFramebufFBO(vfb, vfb->width, vfb->height, true); @@ -892,6 +905,9 @@ void FramebufferManagerCommon::CopyFramebufferForColorTexture(VirtualFramebuffer x += gstate_c.curTextureXOffset; y += gstate_c.curTextureYOffset; } + + // We'll have to reapply these next time since we cropped to UV. + gstate_c.Dirty(DIRTY_TEXTURE_PARAMS); } if (x < src->drawnWidth && y < src->drawnHeight && w > 0 && h > 0) { @@ -1281,8 +1297,14 @@ void FramebufferManagerCommon::ResizeFramebufFBO(VirtualFramebuffer *vfb, int w, shaderManager_->DirtyLastShader(); char tag[256]; - snprintf(tag, sizeof(tag), "%08x_%08x_%dx%d_%s", vfb->fb_address, vfb->z_address, w, h, GeBufferFormatToString(vfb->format)); + snprintf(tag, sizeof(tag), "FB_%08x_%08x_%dx%d_%s", vfb->fb_address, vfb->z_address, w, h, GeBufferFormatToString(vfb->format)); vfb->fbo = draw_->CreateFramebuffer({ vfb->renderWidth, vfb->renderHeight, 1, 1, true, tag }); + if (Memory::IsVRAMAddress(vfb->fb_address) && vfb->fb_stride != 0) { + NotifyMemInfo(MemBlockFlags::ALLOC, vfb->fb_address, ColorBufferByteSize(vfb), tag); + } + if (Memory::IsVRAMAddress(vfb->z_address) && vfb->z_stride != 0) { + NotifyMemInfo(MemBlockFlags::ALLOC, vfb->z_address, vfb->fb_stride * vfb->height * sizeof(uint16_t), std::string("Z_") + tag); + } if (old.fbo) { INFO_LOG(FRAMEBUF, "Resizing FBO for %08x : %dx%dx%s", vfb->fb_address, w, h, GeBufferFormatToString(vfb->format)); if (vfb->fbo) { @@ -1648,8 +1670,11 @@ void FramebufferManagerCommon::ApplyClearToMemory(int x1, int y1, int x2, int y2 return; } } + if (!Memory::IsValidAddress(gstate.getFrameBufAddress())) { + return; + } - u8 *addr = Memory::GetPointer(gstate.getFrameBufAddress()); + u8 *addr = Memory::GetPointerUnchecked(gstate.getFrameBufAddress()); const int bpp = gstate.FrameBufFormat() == GE_FORMAT_8888 ? 4 : 2; u32 clearBits = clearColor; @@ -1668,10 +1693,14 @@ void FramebufferManagerCommon::ApplyClearToMemory(int x1, int y1, int x2, int y2 const int stride = gstate.FrameBufStride(); const int width = x2 - x1; + const int byteStride = stride * bpp; + const int byteWidth = width * bpp; + for (int y = y1; y < y2; ++y) { + NotifyMemInfo(MemBlockFlags::WRITE, gstate.getFrameBufAddress() + x1 * bpp + y * byteStride, byteWidth, "FramebufferClear"); + } + // Can use memset for simple cases. Often alpha is different and gums up the works. if (singleByteClear) { - const int byteStride = stride * bpp; - const int byteWidth = width * bpp; addr += x1 * bpp; for (int y = y1; y < y2; ++y) { memset(addr + y * byteStride, clearBits, byteWidth); @@ -1979,6 +2008,8 @@ bool FramebufferManagerCommon::GetFramebuffer(u32 fb_address, int fb_stride, GEB } if (!vfb) { + if (!Memory::IsValidAddress(fb_address)) + return false; // If there's no vfb and we're drawing there, must be memory? buffer = GPUDebugBuffer(Memory::GetPointer(fb_address), fb_stride, 512, format); return true; @@ -2034,6 +2065,8 @@ bool FramebufferManagerCommon::GetDepthbuffer(u32 fb_address, int fb_stride, u32 } if (!vfb) { + if (!Memory::IsValidAddress(z_address)) + return false; // If there's no vfb and we're drawing there, must be memory? buffer = GPUDebugBuffer(Memory::GetPointer(z_address), z_stride, 512, GPU_DBG_FORMAT_16BIT); return true; @@ -2069,6 +2102,8 @@ bool FramebufferManagerCommon::GetStencilbuffer(u32 fb_address, int fb_stride, G } if (!vfb) { + if (!Memory::IsValidAddress(fb_address)) + return false; // If there's no vfb and we're drawing there, must be memory? // TODO: Actually get the stencil. buffer = GPUDebugBuffer(Memory::GetPointer(fb_address), fb_stride, 512, GPU_DBG_FORMAT_8888); @@ -2129,9 +2164,10 @@ void FramebufferManagerCommon::PackFramebufferSync_(VirtualFramebuffer *vfb, int const int dstBpp = (int)DataFormatSizeInBytes(destFormat); const int dstByteOffset = (y * vfb->fb_stride + x) * dstBpp; + const int dstSize = (h * vfb->fb_stride + w - 1) * dstBpp; - if (!Memory::IsValidRange(fb_address + dstByteOffset, ((h - 1) * vfb->fb_stride + w) * dstBpp)) { - ERROR_LOG(G3D, "PackFramebufferSync_ would write outside of memory, ignoring"); + if (!Memory::IsValidRange(fb_address + dstByteOffset, dstSize)) { + ERROR_LOG_REPORT(G3D, "PackFramebufferSync_ would write outside of memory, ignoring"); return; } @@ -2143,6 +2179,7 @@ void FramebufferManagerCommon::PackFramebufferSync_(VirtualFramebuffer *vfb, int if (destPtr) { draw_->CopyFramebufferToMemorySync(vfb->fbo, Draw::FB_COLOR_BIT, x, y, w, h, destFormat, destPtr, vfb->fb_stride, "PackFramebufferSync_"); + NotifyMemInfo(MemBlockFlags::WRITE, fb_address + dstByteOffset, dstSize, "FramebufferPack"); } else { ERROR_LOG(G3D, "PackFramebufferSync_: Tried to readback to bad address %08x (stride = %d)", fb_address + dstByteOffset, vfb->fb_stride); } diff --git a/GPU/Common/GPUDebugInterface.cpp b/GPU/Common/GPUDebugInterface.cpp index 9fb5d2916c..e4ce10ea44 100644 --- a/GPU/Common/GPUDebugInterface.cpp +++ b/GPU/Common/GPUDebugInterface.cpp @@ -39,7 +39,7 @@ void GPUDebugBuffer::Allocate(u32 stride, u32 height, GPUDebugBufferFormat fmt, fmt_ = fmt; flipped_ = flipped; - u32 pixelSize = PixelSize(fmt); + u32 pixelSize = PixelSize(); data_ = new u8[pixelSize * stride * height]; } @@ -50,8 +50,8 @@ void GPUDebugBuffer::Free() { data_ = NULL; } -u32 GPUDebugBuffer::PixelSize(GPUDebugBufferFormat fmt) const { - switch (fmt) { +u32 GPUDebugBuffer::PixelSize() const { + switch (fmt_) { case GPU_DBG_FORMAT_8888: case GPU_DBG_FORMAT_8888_BGRA: case GPU_DBG_FORMAT_FLOAT: @@ -81,7 +81,7 @@ u32 GPUDebugBuffer::GetRawPixel(int x, int y) const { y = height_ - y - 1; } - u32 pixelSize = PixelSize(fmt_); + u32 pixelSize = PixelSize(); u32 byteOffset = pixelSize * (stride_ * y + x); const u8 *ptr = &data_[byteOffset]; @@ -98,3 +98,36 @@ u32 GPUDebugBuffer::GetRawPixel(int x, int y) const { return 0; } } + +void GPUDebugBuffer::SetRawPixel(int x, int y, u32 c) { + if (data_ == nullptr) { + return; + } + + if (flipped_) { + y = height_ - y - 1; + } + + u32 pixelSize = PixelSize(); + u32 byteOffset = pixelSize * (stride_ * y + x); + u8 *ptr = &data_[byteOffset]; + + switch (pixelSize) { + case 4: + *(u32 *)ptr = c; + break; + case 3: + ptr[0] = (c >> 0) & 0xFF; + ptr[1] = (c >> 8) & 0xFF; + ptr[2] = (c >> 16) & 0xFF; + break; + case 2: + *(u16 *)ptr = (u16)c; + break; + case 1: + *ptr = (u8)c; + break; + default: + break; + } +} diff --git a/GPU/Common/GPUDebugInterface.h b/GPU/Common/GPUDebugInterface.h index e56a349df1..b582a8d917 100644 --- a/GPU/Common/GPUDebugInterface.h +++ b/GPU/Common/GPUDebugInterface.h @@ -140,6 +140,7 @@ struct GPUDebugBuffer { } u32 GetRawPixel(int x, int y) const; + void SetRawPixel(int x, int y, u32 c); const u8 *GetData() const { return data_; @@ -161,9 +162,9 @@ struct GPUDebugBuffer { return fmt_; } -private: - u32 PixelSize(GPUDebugBufferFormat fmt) const; + u32 PixelSize() const; +private: bool alloc_ = false; u8 *data_ = nullptr; u32 stride_ = 0; diff --git a/GPU/Common/GPUStateUtils.cpp b/GPU/Common/GPUStateUtils.cpp index 2f366bf5b8..331436b02a 100644 --- a/GPU/Common/GPUStateUtils.cpp +++ b/GPU/Common/GPUStateUtils.cpp @@ -1303,8 +1303,6 @@ void ConvertBlendState(GenericBlendState &blendState, bool allowFramebufferRead) } static void ConvertStencilFunc5551(GenericStencilFuncState &state) { - state.writeMask = state.writeMask >= 0x80 ? 0xff : 0x00; - // Flaws: // - INVERT should convert 1, 5, 0xFF to 0. Currently it won't always. // - INCR twice shouldn't change the value. @@ -1436,13 +1434,19 @@ static void ConvertStencilFunc5551(GenericStencilFuncState &state) { } } -void ConvertStencilFuncState(GenericStencilFuncState &state) { - state.enabled = gstate.isStencilTestEnabled(); - if (!state.enabled) - return; +static void ConvertStencilMask5551(GenericStencilFuncState &state) { + state.writeMask = state.writeMask >= 0x80 ? 0xff : 0x00; +} - // The PSP's mask is reversed (bits not to write.) +void ConvertStencilFuncState(GenericStencilFuncState &state) { + // The PSP's mask is reversed (bits not to write.) Ignore enabled, used for clears too. state.writeMask = (~gstate.getStencilWriteMask()) & 0xFF; + state.enabled = gstate.isStencilTestEnabled(); + if (!state.enabled) { + if (gstate.FrameBufFormat() == GE_FORMAT_5551) + ConvertStencilMask5551(state); + return; + } state.sFail = gstate.getStencilOpSFail(); state.zFail = gstate.getStencilOpZFail(); @@ -1458,6 +1462,7 @@ void ConvertStencilFuncState(GenericStencilFuncState &state) { break; case GE_FORMAT_5551: + ConvertStencilMask5551(state); ConvertStencilFunc5551(state); break; diff --git a/GPU/Common/PostShader.cpp b/GPU/Common/PostShader.cpp index 9971650ba5..e415aa9226 100644 --- a/GPU/Common/PostShader.cpp +++ b/GPU/Common/PostShader.cpp @@ -161,6 +161,7 @@ void LoadPostShaderInfo(const std::vector &directories) { info.section = section.name(); section.Get("Name", &info.name, section.name().c_str()); section.Get("Compute", &temp, ""); + section.Get("MaxScale", &info.maxScale, 255); info.computeShaderFile = path + "/" + temp; appendTextureShader(info); diff --git a/GPU/Common/PostShader.h b/GPU/Common/PostShader.h index 42aa30d4bc..b5c1f562f9 100644 --- a/GPU/Common/PostShader.h +++ b/GPU/Common/PostShader.h @@ -70,6 +70,7 @@ struct TextureShaderInfo { std::string name; std::string computeShaderFile; + int maxScale; bool operator == (const std::string &other) { return name == other; diff --git a/GPU/Common/PresentationCommon.cpp b/GPU/Common/PresentationCommon.cpp index eb1e8d8cd9..2122980b66 100644 --- a/GPU/Common/PresentationCommon.cpp +++ b/GPU/Common/PresentationCommon.cpp @@ -471,7 +471,6 @@ void PresentationCommon::DestroyPostShader() { Draw::ShaderModule *PresentationCommon::CompileShaderModule(ShaderStage stage, ShaderLanguage lang, const std::string &src, std::string *errorString) { std::string translated = src; - bool translationFailed = false; if (lang != lang_) { // Gonna have to upconvert the shader. if (!TranslateShader(&translated, lang_, draw_->GetShaderLanguageDesc(), nullptr, src, lang, stage, errorString)) { diff --git a/GPU/Common/ReinterpretFramebuffer.cpp b/GPU/Common/ReinterpretFramebuffer.cpp index 4793278f03..15bd29939b 100644 --- a/GPU/Common/ReinterpretFramebuffer.cpp +++ b/GPU/Common/ReinterpretFramebuffer.cpp @@ -29,14 +29,14 @@ bool GenerateReinterpretFragmentShader(char *buffer, GEBufferFormat from, GEBuff switch (from) { case GE_FORMAT_4444: - writer.C(" uint color = uint(val.r * 15.99) | (uint(val.g * 15.99) << 4) | (uint(val.b * 15.99) << 8) | (uint(val.a * 15.99) << 12);\n"); + writer.C(" uint color = uint(val.r * 15.99) | (uint(val.g * 15.99) << 4u) | (uint(val.b * 15.99) << 8u) | (uint(val.a * 15.99) << 12u);\n"); break; case GE_FORMAT_5551: - writer.C(" uint color = uint(val.r * 31.99) | (uint(val.g * 31.99) << 5) | (uint(val.b * 31.99) << 10);\n"); + writer.C(" uint color = uint(val.r * 31.99) | (uint(val.g * 31.99) << 5u) | (uint(val.b * 31.99) << 10u);\n"); writer.C(" if (val.a >= 0.5) color |= 0x8000U;\n"); break; case GE_FORMAT_565: - writer.C(" uint color = uint(val.r * 31.99) | (uint(val.g * 63.99) << 5) | (uint(val.b * 31.99) << 11);\n"); + writer.C(" uint color = uint(val.r * 31.99) | (uint(val.g * 63.99) << 5u) | (uint(val.b * 31.99) << 11u);\n"); break; default: _assert_(false); @@ -45,16 +45,16 @@ bool GenerateReinterpretFragmentShader(char *buffer, GEBufferFormat from, GEBuff switch (to) { case GE_FORMAT_4444: - writer.C(" vec4 outColor = vec4(float(color & 0xFU), float((color >> 4) & 0xFU), float((color >> 8) & 0xFU), float((color >> 12) & 0xFU));\n"); + writer.C(" vec4 outColor = vec4(float(color & 0xFU), float((color >> 4u) & 0xFU), float((color >> 8u) & 0xFU), float((color >> 12u) & 0xFU));\n"); writer.C(" outColor *= 1.0 / 15.0;\n"); break; case GE_FORMAT_5551: - writer.C(" vec4 outColor = vec4(float(color & 0x1FU), float((color >> 5) & 0x1FU), float((color >> 10) & 0x1FU), 0.0);\n"); + writer.C(" vec4 outColor = vec4(float(color & 0x1FU), float((color >> 5u) & 0x1FU), float((color >> 10u) & 0x1FU), 0.0);\n"); writer.C(" outColor.rgb *= 1.0 / 31.0;\n"); writer.C(" outColor.a = float(color >> 15);\n"); break; case GE_FORMAT_565: - writer.C(" vec4 outColor = vec4(float(color & 0x1FU), float((color >> 5) & 0x3FU), float((color >> 11) & 0x1FU), 1.0);\n"); + writer.C(" vec4 outColor = vec4(float(color & 0x1FU), float((color >> 5u) & 0x3FU), float((color >> 11u) & 0x1FU), 1.0);\n"); writer.C(" outColor.rb *= 1.0 / 31.0;\n"); writer.C(" outColor.g *= 1.0 / 63.0;\n"); break; diff --git a/GPU/Common/ShaderUniforms.cpp b/GPU/Common/ShaderUniforms.cpp index 7860ed9ca8..8ca4bacff6 100644 --- a/GPU/Common/ShaderUniforms.cpp +++ b/GPU/Common/ShaderUniforms.cpp @@ -251,7 +251,6 @@ void BaseUpdateUniforms(UB_VS_FS_Base *ub, uint64_t dirtyUniforms, bool flipView viewZInvScale = 0.0; } - float data[4] = { viewZScale, viewZCenter, viewZCenter, viewZInvScale }; ub->depthRange[0] = viewZScale; ub->depthRange[1] = viewZCenter; ub->depthRange[2] = viewZCenter; diff --git a/GPU/Common/SoftwareTransformCommon.cpp b/GPU/Common/SoftwareTransformCommon.cpp index 89a4606450..ccadf1f188 100644 --- a/GPU/Common/SoftwareTransformCommon.cpp +++ b/GPU/Common/SoftwareTransformCommon.cpp @@ -17,6 +17,7 @@ #include #include +#include "Common/CPUDetect.h" #include "Common/Math/math_util.h" #include "Common/GPU/OpenGL/GLFeatures.h" @@ -266,7 +267,7 @@ void SoftwareTransform::Decode(int prim, u32 vertType, const DecVtxFormat &decVt normal = -normal; } Norm3ByMatrix43(worldnormal.AsArray(), normal.AsArray(), gstate.worldMatrix); - worldnormal = worldnormal.Normalized(); + worldnormal = worldnormal.NormalizedOr001(cpu_info.bSSE4_1); } } else { float weights[8]; @@ -298,7 +299,7 @@ void SoftwareTransform::Decode(int prim, u32 vertType, const DecVtxFormat &decVt normal = -normal; } Norm3ByMatrix43(worldnormal.AsArray(), normal.AsArray(), gstate.worldMatrix); - worldnormal = worldnormal.Normalized(); + worldnormal = worldnormal.NormalizedOr001(cpu_info.bSSE4_1); } } @@ -358,7 +359,7 @@ void SoftwareTransform::Decode(int prim, u32 vertType, const DecVtxFormat &decVt break; case GE_PROJMAP_NORMALIZED_NORMAL: // Use normalized normal as source - source = normal.Normalized(); + source = normal.NormalizedOr001(cpu_info.bSSE4_1); if (!reader.hasNormal()) { ERROR_LOG_REPORT(G3D, "Normal projection mapping without normal?"); } @@ -391,11 +392,7 @@ void SoftwareTransform::Decode(int prim, u32 vertType, const DecVtxFormat &decVt }; auto calcShadingLPos = [&](int l) { Vec3f pos = getLPos(l); - if (pos.Length2() == 0.0f) { - return Vec3f(0.0f, 0.0f, 1.0f); - } else { - return pos.Normalized(); - } + return pos.NormalizedOr001(cpu_info.bSSE4_1); }; // Might not have lighting enabled, so don't use lighter. Vec3f lightpos0 = calcShadingLPos(gstate.getUVLS0()); diff --git a/GPU/Common/SplineCommon.cpp b/GPU/Common/SplineCommon.cpp index c4aa0d2c49..86226907f0 100644 --- a/GPU/Common/SplineCommon.cpp +++ b/GPU/Common/SplineCommon.cpp @@ -434,6 +434,9 @@ void SoftwareTessellation(OutputBuffers &output, const Surface &surface, u32 ori SubdivisionSurface::Tessellate(output, surface, points, weights, origVertType); } +template void SoftwareTessellation(OutputBuffers &output, const BezierSurface &surface, u32 origVertType, const ControlPoints &points); +template void SoftwareTessellation(OutputBuffers &output, const SplineSurface &surface, u32 origVertType, const ControlPoints &points); + template static void HardwareTessellation(OutputBuffers &output, const Surface &surface, u32 origVertType, const SimpleVertex *const *points, TessellationDataTransfer *tessDataTransfer) { diff --git a/GPU/Common/TextureCacheCommon.cpp b/GPU/Common/TextureCacheCommon.cpp index fbd3155ffa..5fdf5d18a6 100644 --- a/GPU/Common/TextureCacheCommon.cpp +++ b/GPU/Common/TextureCacheCommon.cpp @@ -23,6 +23,7 @@ #include "Common/MemoryUtil.h" #include "Common/StringUtils.h" #include "Core/Config.h" +#include "Core/Debugger/MemBlockInfo.h" #include "Core/Reporting.h" #include "Core/System.h" #include "GPU/Common/FramebufferManagerCommon.h" @@ -34,6 +35,7 @@ #include "GPU/GPUCommon.h" #include "GPU/GPUInterface.h" #include "GPU/GPUState.h" +#include "Core/Util/PPGeDraw.h" #if defined(_M_SSE) #include @@ -101,9 +103,6 @@ inline int dimHeight(u16 dim) { // TODO TextureCacheCommon::TextureCacheCommon(Draw::DrawContext *draw) : draw_(draw), - texelsScaledThisFrame_(0), - cacheSizeEstimate_(0), - secondCacheSizeEstimate_(0), clutLastFormat_(0xFFFFFFFF), clutTotalBytes_(0), clutMaxBytes_(0), @@ -148,7 +147,7 @@ static int TexLog2(float delta) { return useful - 127 * 256; } -SamplerCacheKey TextureCacheCommon::GetSamplingParams(int maxLevel, u32 texAddr) { +SamplerCacheKey TextureCacheCommon::GetSamplingParams(int maxLevel, const TexCacheEntry *entry) { SamplerCacheKey key; int minFilt = gstate.texfilter & 0x7; @@ -215,39 +214,49 @@ SamplerCacheKey TextureCacheCommon::GetSamplingParams(int maxLevel, u32 texAddr) } // Video bilinear override - if (!key.magFilt && texAddr != 0) { - if (videos_.find(texAddr & 0x3FFFFFFF) != videos_.end()) { - // Enforce bilinear filtering on magnification. - key.magFilt = 1; + if (!key.magFilt && entry != nullptr && IsVideo(entry->addr)) { + // Enforce bilinear filtering on magnification. + key.magFilt = 1; + } + + // Filtering overrides from replacements or settings. + TextureFiltering forceFiltering = TEX_FILTER_AUTO; + u64 cachekey = replacer_.Enabled() ? (entry ? entry->CacheKey() : 0) : 0; + if (!replacer_.Enabled() || entry == nullptr || !replacer_.FindFiltering(cachekey, entry->fullhash, &forceFiltering)) { + switch (g_Config.iTexFiltering) { + case TEX_FILTER_AUTO: + // Follow what the game wants. We just do a single heuristic change to avoid bleeding of wacky color test colors + // in higher resolution (used by some games for sprites, and they accidentally have linear filter on). + if (gstate.isModeThrough() && g_Config.iInternalResolution != 1) { + bool uglyColorTest = gstate.isColorTestEnabled() && !IsColorTestTriviallyTrue() && gstate.getColorTestRef() != 0; + if (uglyColorTest) + forceFiltering = TEX_FILTER_FORCE_NEAREST; + } + break; + case TEX_FILTER_FORCE_LINEAR: + // Override to linear filtering if there's no alpha or color testing going on. + if ((!gstate.isColorTestEnabled() || IsColorTestTriviallyTrue()) && + (!gstate.isAlphaTestEnabled() || IsAlphaTestTriviallyTrue())) { + forceFiltering = TEX_FILTER_FORCE_LINEAR; + } + break; + case TEX_FILTER_FORCE_NEAREST: + default: + // Just force to nearest without checks. Safe (but ugly). + forceFiltering = TEX_FILTER_FORCE_NEAREST; + break; } } - // Filtering overrides - switch (g_Config.iTexFiltering) { + switch (forceFiltering) { case TEX_FILTER_AUTO: - // Follow what the game wants. We just do a single heuristic change to avoid bleeding of wacky color test colors - // in higher resolution (used by some games for sprites, and they accidentally have linear filter on). - if (gstate.isModeThrough() && g_Config.iInternalResolution != 1) { - bool uglyColorTest = gstate.isColorTestEnabled() && !IsColorTestTriviallyTrue() && gstate.getColorTestRef() != 0; - if (uglyColorTest) { - // Force to nearest. - key.magFilt = 0; - key.minFilt = 0; - } - } break; case TEX_FILTER_FORCE_LINEAR: - // Override to linear filtering if there's no alpha or color testing going on. - if ((!gstate.isColorTestEnabled() || IsColorTestTriviallyTrue()) && - (!gstate.isAlphaTestEnabled() || IsAlphaTestTriviallyTrue())) { - key.magFilt = 1; - key.minFilt = 1; - key.mipFilt = 1; - } + key.magFilt = 1; + key.minFilt = 1; + key.mipFilt = 1; break; case TEX_FILTER_FORCE_NEAREST: - default: - // Just force to nearest without checks. Safe (but ugly). key.magFilt = 0; key.minFilt = 0; break; @@ -257,7 +266,7 @@ SamplerCacheKey TextureCacheCommon::GetSamplingParams(int maxLevel, u32 texAddr) } SamplerCacheKey TextureCacheCommon::GetFramebufferSamplingParams(u16 bufferWidth, u16 bufferHeight) { - SamplerCacheKey key = GetSamplingParams(0, 0); + SamplerCacheKey key = GetSamplingParams(0, nullptr); // Kill any mipmapping settings. key.mipEnable = false; @@ -430,6 +439,7 @@ TexCacheEntry *TextureCacheCommon::SetTexture() { if (texhash != entry->hash) { match = false; + reason = "minihash"; } else if (entry->GetHashStatus() == TexCacheEntry::STATUS_RELIABLE) { rehash = false; } @@ -506,7 +516,7 @@ TexCacheEntry *TextureCacheCommon::SetTexture() { WARN_LOG_REPORT_ONCE(clutUseRender, G3D, "Using texture with rendered CLUT: texfmt=%d, clutfmt=%d", gstate.getTextureFormat(), gstate.getClutPaletteFormat()); } - if (Memory::IsKernelAndNotVolatileAddress(texaddr)) { + if (PPGeIsFontTextureAddress(texaddr)) { // It's the builtin font texture. entry->status = TexCacheEntry::STATUS_RELIABLE; } else if (g_Config.bTextureBackoffCache) { @@ -565,7 +575,6 @@ TexCacheEntry *TextureCacheCommon::SetTexture() { std::vector TextureCacheCommon::GetFramebufferCandidates(const TextureDefinition &entry, u32 texAddrOffset) { gpuStats.numFramebufferEvaluations++; - bool success = false; std::vector candidates; @@ -694,17 +703,28 @@ void TextureCacheCommon::Decimate(bool forcePressure) { } void TextureCacheCommon::DecimateVideos() { - if (!videos_.empty()) { - for (auto iter = videos_.begin(); iter != videos_.end(); ) { - if (iter->second + VIDEO_DECIMATE_AGE < gpuStats.numFlips) { - videos_.erase(iter++); - } else { - ++iter; - } + for (auto iter = videos_.begin(); iter != videos_.end(); ) { + if (iter->flips + VIDEO_DECIMATE_AGE < gpuStats.numFlips) { + iter = videos_.erase(iter++); + } else { + ++iter; } } } +bool TextureCacheCommon::IsVideo(u32 texaddr) { + texaddr &= 0x3FFFFFFF; + for (auto info : videos_) { + if (texaddr < info.addr) { + continue; + } + if (texaddr < info.addr + info.size) { + return true; + } + } + return false; +} + void TextureCacheCommon::HandleTextureChange(TexCacheEntry *const entry, const char *reason, bool initialMatch, bool doDelete) { cacheSizeEstimate_ -= EstimateTexMemoryUsage(entry); entry->numInvalidated++; @@ -885,8 +905,6 @@ FramebufferMatchInfo TextureCacheCommon::MatchFramebuffer( (channel == NOTIFY_FB_COLOR && framebuffer->format == GE_FORMAT_8888 && entry.format == GE_TFMT_CLUT32) || (channel == NOTIFY_FB_COLOR && framebuffer->format != GE_FORMAT_8888 && entry.format == GE_TFMT_CLUT16); - const bool clutFormat = IsClutFormat((GETextureFormat)(entry.format)); - // To avoid ruining git blame, kept the same name as the old struct. FramebufferMatchInfo fbInfo{ FramebufferMatch::VALID }; @@ -941,8 +959,8 @@ FramebufferMatchInfo TextureCacheCommon::MatchFramebuffer( } // This is either normal or we failed to generate a shader to depalettize - if (framebuffer->format == entry.format || matchingClutFormat) { - if (framebuffer->format != entry.format) { + if ((int)framebuffer->format == (int)entry.format || matchingClutFormat) { + if ((int)framebuffer->format != (int)entry.format) { WARN_LOG_ONCE(diffFormat2, G3D, "Texturing from framebuffer with different formats %s != %s at %08x", GeTextureFormatToString(entry.format), GeBufferFormatToString(framebuffer->format), fb_address); return fbInfo; @@ -1082,7 +1100,7 @@ void TextureCacheCommon::NotifyConfigChanged() { void TextureCacheCommon::NotifyVideoUpload(u32 addr, int size, int width, GEBufferFormat fmt) { addr &= 0x3FFFFFFF; - videos_[addr] = gpuStats.numFlips; + videos_.push_back({ addr, (u32)size, gpuStats.numFlips }); } void TextureCacheCommon::LoadClut(u32 clutAddr, u32 loadBytes) { @@ -1117,6 +1135,8 @@ void TextureCacheCommon::LoadClut(u32 clutAddr, u32 loadBytes) { } } } + + NotifyMemInfo(MemBlockFlags::ALLOC, clutAddr, loadBytes, "CLUT"); } // It's possible for a game to (successfully) access outside valid memory. @@ -1301,6 +1321,9 @@ void TextureCacheCommon::DecodeTextureLevel(u8 *out, int outPitch, GETextureForm int w = gstate.getTextureWidth(level); int h = gstate.getTextureHeight(level); const u8 *texptr = Memory::GetPointer(texaddr); + const uint32_t byteSize = (textureBitsPerPixel[format] * bufw * h) / 8; + + NotifyMemInfo(MemBlockFlags::TEXTURE, texaddr, byteSize, StringFromFormat("Texture_%08x_%dx%d_%s", texaddr, w, h, GeTextureFormatToString(format, clutformat))); switch (format) { case GE_TFMT_CLUT4: @@ -1626,8 +1649,7 @@ void TextureCacheCommon::ApplyTexture() { if (nextNeedsRebuild_) { // Regardless of hash fails or otherwise, if this is a video, mark it frequently changing. // This prevents temporary scaling perf hits on the first second of video. - bool isVideo = videos_.find(entry->addr & 0x3FFFFFFF) != videos_.end(); - if (isVideo) { + if (IsVideo(entry->addr)) { entry->status |= TexCacheEntry::STATUS_CHANGE_FREQUENT; } @@ -1796,7 +1818,7 @@ void TextureCacheCommon::Invalidate(u32 addr, int size, GPUInvalidationType type } // If we're hashing every use, without backoff, then this isn't needed. - if (!g_Config.bTextureBackoffCache) { + if (!g_Config.bTextureBackoffCache && type != GPU_INVALIDATE_FORCE) { return; } @@ -1807,28 +1829,34 @@ void TextureCacheCommon::Invalidate(u32 addr, int size, GPUInvalidationType type } for (TexCache::iterator iter = cache_.lower_bound(startKey), end = cache_.upper_bound(endKey); iter != end; ++iter) { - u32 texAddr = iter->second->addr; - u32 texEnd = iter->second->addr + iter->second->sizeInRAM; + auto &entry = iter->second; + u32 texAddr = entry->addr; + u32 texEnd = entry->addr + entry->sizeInRAM; // Quick check for overlap. Yes the check is right. if (addr < texEnd && addr_end > texAddr) { - if (iter->second->GetHashStatus() == TexCacheEntry::STATUS_RELIABLE) { - iter->second->SetHashStatus(TexCacheEntry::STATUS_HASHING); + if (entry->GetHashStatus() == TexCacheEntry::STATUS_RELIABLE) { + entry->SetHashStatus(TexCacheEntry::STATUS_HASHING); + } + if (type == GPU_INVALIDATE_FORCE) { + // Just random values to force the hash not to match. + entry->fullhash = (entry->fullhash ^ 0x12345678) + 13; + entry->hash = (entry->hash ^ 0x89ABCDEF) + 89; } if (type != GPU_INVALIDATE_ALL) { gpuStats.numTextureInvalidations++; // Start it over from 0 (unless it's safe.) - iter->second->numFrames = type == GPU_INVALIDATE_SAFE ? 256 : 0; + entry->numFrames = type == GPU_INVALIDATE_SAFE ? 256 : 0; if (type == GPU_INVALIDATE_SAFE) { - u32 diff = gpuStats.numFlips - iter->second->lastFrame; + u32 diff = gpuStats.numFlips - entry->lastFrame; // We still need to mark if the texture is frequently changing, even if it's safely changing. if (diff < TEXCACHE_FRAME_CHANGE_FREQUENT) { - iter->second->status |= TexCacheEntry::STATUS_CHANGE_FREQUENT; + entry->status |= TexCacheEntry::STATUS_CHANGE_FREQUENT; } } - iter->second->framesUntilNextFullHash = 0; + entry->framesUntilNextFullHash = 0; } else { - iter->second->invalidHint++; + entry->invalidHint++; } } } diff --git a/GPU/Common/TextureCacheCommon.h b/GPU/Common/TextureCacheCommon.h index beb1a6b6e0..cbafece711 100644 --- a/GPU/Common/TextureCacheCommon.h +++ b/GPU/Common/TextureCacheCommon.h @@ -28,12 +28,6 @@ #include "GPU/Common/GPUDebugInterface.h" #include "GPU/Common/TextureDecoder.h" -enum TextureFiltering { - TEX_FILTER_AUTO = 1, - TEX_FILTER_FORCE_NEAREST = 2, - TEX_FILTER_FORCE_LINEAR = 3, -}; - enum FramebufferNotification { NOTIFY_FB_CREATED, NOTIFY_FB_UPDATED, @@ -53,6 +47,7 @@ enum FramebufferNotificationChannel { #define TEXCACHE_MAX_TEXELS_SCALED (256*256) // Per frame struct VirtualFramebuffer; +class TextureReplacer; namespace Draw { class DrawContext; @@ -291,7 +286,7 @@ protected: u32 EstimateTexMemoryUsage(const TexCacheEntry *entry); - SamplerCacheKey GetSamplingParams(int maxLevel, u32 texAddr); + SamplerCacheKey GetSamplingParams(int maxLevel, const TexCacheEntry *entry); SamplerCacheKey GetFramebufferSamplingParams(u16 bufferWidth, u16 bufferHeight); void UpdateMaxSeenV(TexCacheEntry *entry, bool throughMode); @@ -303,6 +298,7 @@ protected: void SetTextureFramebuffer(const AttachCandidate &candidate); void DecimateVideos(); + bool IsVideo(u32 texaddr); inline u32 QuickTexHash(TextureReplacer &replacer, u32 addr, int bufw, int w, int h, GETextureFormat format, TexCacheEntry *entry) const { if (replacer.Enabled()) { @@ -337,16 +333,21 @@ protected: bool lowMemoryMode_ = false; int decimationCounter_; - int texelsScaledThisFrame_; - int timesInvalidatedAllThisFrame_; + int texelsScaledThisFrame_ = 0; + int timesInvalidatedAllThisFrame_ = 0; TexCache cache_; - u32 cacheSizeEstimate_; + u32 cacheSizeEstimate_ = 0; TexCache secondCache_; - u32 secondCacheSizeEstimate_; + u32 secondCacheSizeEstimate_ = 0; - std::map videos_; + struct VideoInfo { + u32 addr; + u32 size; + int flips; + }; + std::vector videos_; SimpleBuf tmpTexBuf32_; SimpleBuf tmpTexBufRearrange_; diff --git a/GPU/Common/TextureDecoder.cpp b/GPU/Common/TextureDecoder.cpp index f02094bfcd..2e10ac1f36 100644 --- a/GPU/Common/TextureDecoder.cpp +++ b/GPU/Common/TextureDecoder.cpp @@ -15,6 +15,7 @@ // Official git repository and contact information can be found at // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. +#include "ppsspp_config.h" #include "ext/xxhash.h" #include "Common/CPUDetect.h" #include "Common/ColorConv.h" diff --git a/GPU/Common/TextureDecoder.h b/GPU/Common/TextureDecoder.h index 02e7939cdc..b00958a296 100644 --- a/GPU/Common/TextureDecoder.h +++ b/GPU/Common/TextureDecoder.h @@ -31,6 +31,12 @@ enum CheckAlphaResult { #include "GPU/Common/TextureDecoderNEON.h" #include "GPU/GPUState.h" +enum TextureFiltering { + TEX_FILTER_AUTO = 1, + TEX_FILTER_FORCE_NEAREST = 2, + TEX_FILTER_FORCE_LINEAR = 3, +}; + void SetupTextureDecoder(); // Pitch must be aligned to 16 bits (as is the case on a PSP) diff --git a/GPU/Common/TextureDecoderNEON.cpp b/GPU/Common/TextureDecoderNEON.cpp index 8e96223877..e0a427e65a 100644 --- a/GPU/Common/TextureDecoderNEON.cpp +++ b/GPU/Common/TextureDecoderNEON.cpp @@ -41,7 +41,7 @@ u32 QuickTexHashNEON(const void *checkp, u32 size) { __builtin_prefetch(checkp, 0, 0); if (((intptr_t)checkp & 0xf) == 0 && (size & 0x3f) == 0) { -#if defined(IOS) || PPSSPP_ARCH(ARM64) || defined(_MSC_VER) || !PPSSPP_ARCH(ARMV7) +#if PPSSPP_PLATFORM(IOS) || PPSSPP_ARCH(ARM64) || defined(_MSC_VER) || !PPSSPP_ARCH(ARMV7) uint32x4_t cursor = vdupq_n_u32(0); uint16x8_t cursor2 = vld1q_u16(QuickTexHashInitial); uint16x8_t update = vdupq_n_u16(0x2455U); diff --git a/GPU/Common/TransformCommon.cpp b/GPU/Common/TransformCommon.cpp index 7e74f321a7..85031c8e0e 100644 --- a/GPU/Common/TransformCommon.cpp +++ b/GPU/Common/TransformCommon.cpp @@ -17,6 +17,7 @@ #include +#include "Common/CPUDetect.h" #include "GPU/GPUState.h" #include "GPU/Common/TransformCommon.h" @@ -140,7 +141,7 @@ void Lighter::Light(float colorOut0[4], float colorOut1[4], const float colorIn[ case GE_LIGHTTYPE_SPOT: case GE_LIGHTTYPE_UNKNOWN: lightDir = Vec3Packedf(&ldir[l * 3]); - angle = Dot(toLight.Normalized(), lightDir.Normalized()); + angle = Dot(toLight.NormalizedOr001(cpu_info.bSSE4_1), lightDir.NormalizedOr001(cpu_info.bSSE4_1)); if (angle >= lcutoff[l]) lightScale = clamp(1.0f / (latt[l * 3] + latt[l * 3 + 1] * distanceToLight + latt[l * 3 + 2] * distanceToLight*distanceToLight), 0.0f, 1.0f) * powf(angle, lconv[l]); break; @@ -155,11 +156,10 @@ void Lighter::Light(float colorOut0[4], float colorOut1[4], const float colorIn[ // Real PSP specular Vec3f toViewer(0, 0, 1); // Better specular - // Vec3f toViewer = (viewer - pos).Normalized(); + // Vec3f toViewer = (viewer - pos).NormalizedOr001(cpu_info.bSSE4_1); if (doSpecular) { - Vec3f halfVec = (toLight + toViewer); - halfVec.Normalize(); + Vec3f halfVec = (toLight + toViewer).NormalizedOr001(cpu_info.bSSE4_1); dot = Dot(halfVec, norm); if (dot > 0.0f) { diff --git a/GPU/Common/VertexDecoderArm.cpp b/GPU/Common/VertexDecoderArm.cpp index 0feb2df100..434c691364 100644 --- a/GPU/Common/VertexDecoderArm.cpp +++ b/GPU/Common/VertexDecoderArm.cpp @@ -231,7 +231,6 @@ JittedVertexDecoder VertexDecoderJitCache::Compile(const VertexDecoder &dec, int // Add code to convert matrices to 4x4. // Later we might want to do this when the matrices are loaded instead. - int boneCount = 0; if (NEONSkinning && dec.weighttype && g_Config.bSoftwareSkinning) { // Copying from R3 to R4 MOVP2R(R3, gstate.boneMatrix); diff --git a/GPU/Common/VertexDecoderArm64.cpp b/GPU/Common/VertexDecoderArm64.cpp index c1ec8770bc..305ba3ed90 100644 --- a/GPU/Common/VertexDecoderArm64.cpp +++ b/GPU/Common/VertexDecoderArm64.cpp @@ -190,7 +190,6 @@ JittedVertexDecoder VertexDecoderJitCache::Compile(const VertexDecoder &dec, int // Add code to convert matrices to 4x4. // Later we might want to do this when the matrices are loaded instead. - int boneCount = 0; if (dec.weighttype && g_Config.bSoftwareSkinning) { // Copying from R3 to R4 MOVP2R(X3, gstate.boneMatrix); diff --git a/GPU/Common/VertexDecoderCommon.cpp b/GPU/Common/VertexDecoderCommon.cpp index 0f46ea522c..920083577f 100644 --- a/GPU/Common/VertexDecoderCommon.cpp +++ b/GPU/Common/VertexDecoderCommon.cpp @@ -119,7 +119,7 @@ void GetIndexBounds(const void *inds, int count, u32 vertType, u16 *indexLowerBo lowerBound = value; } } else if (idx == GE_VTYPE_IDX_16BIT) { - const u16 *ind16 = (const u16 *)inds; + const u16_le *ind16 = (const u16_le *)inds; for (int i = 0; i < count; i++) { u16 value = ind16[i]; if (value > upperBound) @@ -129,7 +129,7 @@ void GetIndexBounds(const void *inds, int count, u32 vertType, u16 *indexLowerBo } } else if (idx == GE_VTYPE_IDX_32BIT) { WARN_LOG_REPORT_ONCE(indexBounds32, G3D, "GetIndexBounds: Decoding 32-bit indexes"); - const u32 *ind32 = (const u32 *)inds; + const u32_le *ind32 = (const u32_le *)inds; for (int i = 0; i < count; i++) { u16 value = (u16)ind32[i]; // These aren't documented and should be rare. Let's bounds check each one. @@ -193,7 +193,7 @@ void VertexDecoder::Step_WeightsU8() const void VertexDecoder::Step_WeightsU16() const { u16 *wt = (u16 *)(decoded_ + decFmt.w0off); - const u16 *wdata = (const u16*)(ptr_); + const u16_le *wdata = (const u16_le *)(ptr_); int j; for (j = 0; j < nweights; j++) wt[j] = wdata[j]; @@ -216,7 +216,7 @@ void VertexDecoder::Step_WeightsU8ToFloat() const void VertexDecoder::Step_WeightsU16ToFloat() const { float *wt = (float *)(decoded_ + decFmt.w0off); - const u16 *wdata = (const u16*)(ptr_); + const u16_le *wdata = (const u16_le *)(ptr_); int j; for (j = 0; j < nweights; j++) { wt[j] = (float)wdata[j] * (1.0f / 32768.0f); @@ -231,7 +231,7 @@ void VertexDecoder::Step_WeightsU16ToFloat() const void VertexDecoder::Step_WeightsFloat() const { float *wt = (float *)(decoded_ + decFmt.w0off); - const float *wdata = (const float*)(ptr_); + const float_le *wdata = (const float_le *)(ptr_); int j; for (j = 0; j < nweights; j++) { wt[j] = wdata[j]; @@ -261,7 +261,7 @@ void VertexDecoder::Step_WeightsU8Skin() const { } void VertexDecoder::Step_WeightsU16Skin() const { - const u16 *wdata = (const u16*)(ptr_); + const u16_le *wdata = (const u16_le *)(ptr_); float weights[8]; for (int j = 0; j < nweights; j++) weights[j] = wdata[j] * (1.0f / 32768.0f); @@ -269,7 +269,7 @@ void VertexDecoder::Step_WeightsU16Skin() const { } void VertexDecoder::Step_WeightsFloatSkin() const { - const float *wdata = (const float*)(ptr_); + const float_le *wdata = (const float_le *)(ptr_); ComputeSkinMatrix(wdata); } @@ -285,7 +285,7 @@ void VertexDecoder::Step_TcU8ToFloat() const void VertexDecoder::Step_TcU16ToFloat() const { float *uv = (float *)(decoded_ + decFmt.uvoff); - const u16 *uvdata = (const u16_le*)(ptr_ + tcoff); + const u16_le *uvdata = (const u16_le *)(ptr_ + tcoff); uv[0] = uvdata[0] * (1.0f / 32768.0f); uv[1] = uvdata[1] * (1.0f / 32768.0f); } @@ -293,7 +293,7 @@ void VertexDecoder::Step_TcU16ToFloat() const void VertexDecoder::Step_TcU16DoubleToFloat() const { float *uv = (float*)(decoded_ + decFmt.uvoff); - const u16 *uvdata = (const u16_le*)(ptr_ + tcoff); + const u16_le *uvdata = (const u16_le *)(ptr_ + tcoff); uv[0] = uvdata[0] * (1.0f / 16384.0f); uv[1] = uvdata[1] * (1.0f / 16384.0f); } @@ -301,20 +301,20 @@ void VertexDecoder::Step_TcU16DoubleToFloat() const void VertexDecoder::Step_TcU16ThroughToFloat() const { float *uv = (float *)(decoded_ + decFmt.uvoff); - const u16 *uvdata = (const u16_le*)(ptr_ + tcoff); + const u16_le *uvdata = (const u16_le *)(ptr_ + tcoff); uv[0] = uvdata[0]; uv[1] = uvdata[1]; - gstate_c.vertBounds.minU = std::min(gstate_c.vertBounds.minU, uvdata[0]); - gstate_c.vertBounds.maxU = std::max(gstate_c.vertBounds.maxU, uvdata[0]); - gstate_c.vertBounds.minV = std::min(gstate_c.vertBounds.minV, uvdata[1]); - gstate_c.vertBounds.maxV = std::max(gstate_c.vertBounds.maxV, uvdata[1]); + gstate_c.vertBounds.minU = std::min(gstate_c.vertBounds.minU, (u16)uvdata[0]); + gstate_c.vertBounds.maxU = std::max(gstate_c.vertBounds.maxU, (u16)uvdata[0]); + gstate_c.vertBounds.minV = std::min(gstate_c.vertBounds.minV, (u16)uvdata[1]); + gstate_c.vertBounds.maxV = std::max(gstate_c.vertBounds.maxV, (u16)uvdata[1]); } void VertexDecoder::Step_TcU16ThroughDoubleToFloat() const { float *uv = (float *)(decoded_ + decFmt.uvoff); - const u16 *uvdata = (const u16_le*)(ptr_ + tcoff); + const u16_le *uvdata = (const u16_le *)(ptr_ + tcoff); uv[0] = uvdata[0] * 2; uv[1] = uvdata[1] * 2; } @@ -322,7 +322,7 @@ void VertexDecoder::Step_TcU16ThroughDoubleToFloat() const void VertexDecoder::Step_TcFloat() const { float *uv = (float *)(decoded_ + decFmt.uvoff); - const float *uvdata = (const float*)(ptr_ + tcoff); + const float_le *uvdata = (const float_le *)(ptr_ + tcoff); uv[0] = uvdata[0]; uv[1] = uvdata[1]; } @@ -330,7 +330,7 @@ void VertexDecoder::Step_TcFloat() const void VertexDecoder::Step_TcFloatThrough() const { float *uv = (float *)(decoded_ + decFmt.uvoff); - const float *uvdata = (const float*)(ptr_ + tcoff); + const float_le *uvdata = (const float_le *)(ptr_ + tcoff); uv[0] = uvdata[0]; uv[1] = uvdata[1]; @@ -363,7 +363,7 @@ void VertexDecoder::Step_TcU16DoublePrescale() const { void VertexDecoder::Step_TcFloatPrescale() const { float *uv = (float *)(decoded_ + decFmt.uvoff); - const float *uvdata = (const float*)(ptr_ + tcoff); + 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; } @@ -496,7 +496,7 @@ void VertexDecoder::Step_ColorInvalid() const void VertexDecoder::Step_Color565() const { u8 *c = decoded_ + decFmt.c0off; - u16 cdata = *(u16_le *)(ptr_ + coloff); + u16 cdata = *(const u16_le *)(ptr_ + coloff); c[0] = Convert5To8(cdata & 0x1f); c[1] = Convert6To8((cdata >> 5) & 0x3f); c[2] = Convert5To8((cdata >> 11) & 0x1f); @@ -507,7 +507,7 @@ void VertexDecoder::Step_Color565() const void VertexDecoder::Step_Color5551() const { u8 *c = decoded_ + decFmt.c0off; - u16 cdata = *(u16_le *)(ptr_ + coloff); + u16 cdata = *(const u16_le *)(ptr_ + coloff); gstate_c.vertexFullAlpha = gstate_c.vertexFullAlpha && (cdata >> 15) != 0; c[0] = Convert5To8(cdata & 0x1f); c[1] = Convert5To8((cdata >> 5) & 0x1f); @@ -518,7 +518,7 @@ void VertexDecoder::Step_Color5551() const void VertexDecoder::Step_Color4444() const { u8 *c = decoded_ + decFmt.c0off; - u16 cdata = *(u16_le *)(ptr_ + coloff); + u16 cdata = *(const u16_le *)(ptr_ + coloff); gstate_c.vertexFullAlpha = gstate_c.vertexFullAlpha && (cdata >> 12) == 0xF; for (int j = 0; j < 4; j++) c[j] = Convert4To8((cdata >> (j * 4)) & 0xF); @@ -537,7 +537,7 @@ void VertexDecoder::Step_Color565Morph() const float col[3] = { 0 }; for (int n = 0; n < morphcount; n++) { float w = gstate_c.morphWeights[n]; - u16 cdata = *(u16_le *)(ptr_ + onesize_*n + coloff); + u16 cdata = *(const u16_le *)(ptr_ + onesize_*n + coloff); col[0] += w * (cdata & 0x1f) * (255.0f / 31.0f); col[1] += w * ((cdata >> 5) & 0x3f) * (255.0f / 63.0f); col[2] += w * ((cdata >> 11) & 0x1f) * (255.0f / 31.0f); @@ -555,7 +555,7 @@ void VertexDecoder::Step_Color5551Morph() const float col[4] = { 0 }; for (int n = 0; n < morphcount; n++) { float w = gstate_c.morphWeights[n]; - u16 cdata = *(u16_le *)(ptr_ + onesize_*n + coloff); + u16 cdata = *(const u16_le *)(ptr_ + onesize_*n + coloff); col[0] += w * (cdata & 0x1f) * (255.0f / 31.0f); col[1] += w * ((cdata >> 5) & 0x1f) * (255.0f / 31.0f); col[2] += w * ((cdata >> 10) & 0x1f) * (255.0f / 31.0f); @@ -573,7 +573,7 @@ void VertexDecoder::Step_Color4444Morph() const float col[4] = { 0 }; for (int n = 0; n < morphcount; n++) { float w = gstate_c.morphWeights[n]; - u16 cdata = *(u16_le *)(ptr_ + onesize_*n + coloff); + u16 cdata = *(const u16_le *)(ptr_ + onesize_*n + coloff); for (int j = 0; j < 4; j++) col[j] += w * ((cdata >> (j * 4)) & 0xF) * (255.0f / 15.0f); } @@ -621,7 +621,7 @@ void VertexDecoder::Step_NormalS8ToFloat() const void VertexDecoder::Step_NormalS16() const { s16 *normal = (s16 *)(decoded_ + decFmt.nrmoff); - const s16 *sv = (const s16_le*)(ptr_ + nrmoff); + const s16_le *sv = (const s16_le *)(ptr_ + nrmoff); for (int j = 0; j < 3; j++) normal[j] = sv[j]; normal[3] = 0; @@ -630,7 +630,7 @@ void VertexDecoder::Step_NormalS16() const void VertexDecoder::Step_NormalFloat() const { u32 *normal = (u32 *)(decoded_ + decFmt.nrmoff); - const u32 *fv = (const u32_le*)(ptr_ + nrmoff); + const u32_le *fv = (const u32_le *)(ptr_ + nrmoff); for (int j = 0; j < 3; j++) normal[j] = fv[j]; } @@ -646,7 +646,7 @@ void VertexDecoder::Step_NormalS8Skin() const void VertexDecoder::Step_NormalS16Skin() const { float *normal = (float *)(decoded_ + decFmt.nrmoff); - const s16 *sv = (const s16_le*)(ptr_ + nrmoff); + const s16_le *sv = (const s16_le *)(ptr_ + nrmoff); const float fn[3] = { sv[0] * (1.0f / 32768.0f), sv[1] * (1.0f / 32768.0f), sv[2] * (1.0f / 32768.0f) }; Norm3ByMatrix43(normal, fn, skinMatrix); } @@ -654,7 +654,7 @@ void VertexDecoder::Step_NormalS16Skin() const void VertexDecoder::Step_NormalFloatSkin() const { float *normal = (float *)(decoded_ + decFmt.nrmoff); - const float *fn = (const float *)(ptr_ + nrmoff); + const float_le *fn = (const float_le *)(ptr_ + nrmoff); Norm3ByMatrix43(normal, fn, skinMatrix); } @@ -675,7 +675,7 @@ void VertexDecoder::Step_NormalS16Morph() const float *normal = (float *)(decoded_ + decFmt.nrmoff); memset(normal, 0, sizeof(float) * 3); for (int n = 0; n < morphcount; n++) { - const s16 *sv = (const s16_le *)(ptr_ + onesize_*n + nrmoff); + const s16_le *sv = (const s16_le *)(ptr_ + onesize_*n + nrmoff); const float multiplier = gstate_c.morphWeights[n] * (1.0f / 32768.0f); for (int j = 0; j < 3; j++) normal[j] += sv[j] * multiplier; @@ -688,7 +688,7 @@ void VertexDecoder::Step_NormalFloatMorph() const memset(normal, 0, sizeof(float) * 3); for (int n = 0; n < morphcount; n++) { float multiplier = gstate_c.morphWeights[n]; - const float *fv = (const float*)(ptr_ + onesize_*n + nrmoff); + const float_le *fv = (const float_le *)(ptr_ + onesize_*n + nrmoff); for (int j = 0; j < 3; j++) normal[j] += fv[j] * multiplier; } @@ -710,7 +710,7 @@ void VertexDecoder::Step_NormalS16MorphSkin() const { float *normal = (float *)(decoded_ + decFmt.nrmoff); float nrm[3]{}; for (int n = 0; n < morphcount; n++) { - const s16 *sv = (const s16_le *)(ptr_ + onesize_ * n + nrmoff); + const s16_le *sv = (const s16_le *)(ptr_ + onesize_ * n + nrmoff); const float multiplier = gstate_c.morphWeights[n] * (1.0f / 32768.0f); for (int j = 0; j < 3; j++) nrm[j] += sv[j] * multiplier; @@ -723,7 +723,7 @@ void VertexDecoder::Step_NormalFloatMorphSkin() const { float nrm[3]{}; for (int n = 0; n < morphcount; n++) { float multiplier = gstate_c.morphWeights[n]; - const float *fv = (const float*)(ptr_ + onesize_ * n + nrmoff); + const float_le *fv = (const float_le *)(ptr_ + onesize_ * n + nrmoff); for (int j = 0; j < 3; j++) nrm[j] += fv[j] * multiplier; } @@ -741,7 +741,7 @@ void VertexDecoder::Step_PosS8() const void VertexDecoder::Step_PosS16() const { float *pos = (float *)(decoded_ + decFmt.posoff); - const s16 *sv = (const s16_le *)(ptr_ + posoff); + const s16_le *sv = (const s16_le *)(ptr_ + posoff); for (int j = 0; j < 3; j++) pos[j] = sv[j] * (1.0f / 32768.0f); } @@ -772,7 +772,7 @@ void VertexDecoder::Step_PosS16Skin() const void VertexDecoder::Step_PosFloatSkin() const { float *pos = (float *)(decoded_ + decFmt.posoff); - const float *fn = (const float *)(ptr_ + posoff); + const float_le *fn = (const float_le *)(ptr_ + posoff); Vec3ByMatrix43(pos, fn, skinMatrix); } @@ -820,7 +820,7 @@ void VertexDecoder::Step_PosS16Morph() const memset(v, 0, sizeof(float) * 3); for (int n = 0; n < morphcount; n++) { const float multiplier = 1.0f / 32768.0f; - const s16 *sv = (const s16*)(ptr_ + onesize_*n + posoff); + const s16_le *sv = (const s16_le *)(ptr_ + onesize_*n + posoff); for (int j = 0; j < 3; j++) v[j] += (float)sv[j] * (multiplier * gstate_c.morphWeights[n]); } @@ -831,7 +831,7 @@ void VertexDecoder::Step_PosFloatMorph() const float *v = (float *)(decoded_ + decFmt.posoff); memset(v, 0, sizeof(float) * 3); for (int n = 0; n < morphcount; n++) { - const float *fv = (const float*)(ptr_ + onesize_*n + posoff); + const float_le *fv = (const float_le *)(ptr_ + onesize_*n + posoff); for (int j = 0; j < 3; j++) v[j] += fv[j] * gstate_c.morphWeights[n]; } @@ -854,7 +854,7 @@ void VertexDecoder::Step_PosS16MorphSkin() const { float pos[3]{}; for (int n = 0; n < morphcount; n++) { const float multiplier = 1.0f / 32768.0f; - const s16 *sv = (const s16*)(ptr_ + onesize_ * n + posoff); + const s16_le *sv = (const s16_le *)(ptr_ + onesize_ * n + posoff); for (int j = 0; j < 3; j++) pos[j] += (float)sv[j] * (multiplier * gstate_c.morphWeights[n]); } @@ -865,7 +865,7 @@ void VertexDecoder::Step_PosFloatMorphSkin() const { float *v = (float *)(decoded_ + decFmt.posoff); float pos[3]{}; for (int n = 0; n < morphcount; n++) { - const float *fv = (const float*)(ptr_ + onesize_ * n + posoff); + const float_le *fv = (const float_le *)(ptr_ + onesize_ * n + posoff); for (int j = 0; j < 3; j++) pos[j] += fv[j] * gstate_c.morphWeights[n]; } @@ -1377,13 +1377,13 @@ VertexDecoderJitCache::VertexDecoderJitCache() AllocCodeSpace(1024 * 64 * 4); // Add some random code to "help" MSVC's buggy disassembler :( -#if defined(_WIN32) && (defined(_M_IX86) || defined(_M_X64)) +#if defined(_WIN32) && (PPSSPP_ARCH(X86) || PPSSPP_ARCH(AMD64)) using namespace Gen; for (int i = 0; i < 100; i++) { MOV(32, R(EAX), R(EBX)); RET(); } -#elif defined(ARM) +#elif PPSSPP_ARCH(ARM) BKPT(0); BKPT(0); #endif diff --git a/GPU/Common/VertexDecoderCommon.h b/GPU/Common/VertexDecoderCommon.h index 6013b1a356..9377b36119 100644 --- a/GPU/Common/VertexDecoderCommon.h +++ b/GPU/Common/VertexDecoderCommon.h @@ -93,8 +93,8 @@ private: union { const void *indices; const u8 *indices8; - const u16 *indices16; - const u32 *indices32; + const u16_le *indices16; + const u32_le *indices32; }; u32 indexType; diff --git a/GPU/Common/VertexDecoderFake.cpp b/GPU/Common/VertexDecoderFake.cpp index af27e69561..6e170f93b9 100644 --- a/GPU/Common/VertexDecoderFake.cpp +++ b/GPU/Common/VertexDecoderFake.cpp @@ -18,6 +18,7 @@ //TODO: Doesn't build, FIXME! #if 0 +#include "ppsspp_config.h" #include "Common/CPUDetect.h" #include "Core/Config.h" #include "Core/Reporting.h" @@ -28,7 +29,7 @@ static const float by128 = 1.0f / 128.0f; static const float by16384 = 1.0f / 16384.0f; static const float by32768 = 1.0f / 32768.0f; -#ifdef MIPS +#if PPSSPP_ARCH(MIPS) using namespace MIPSGen; #else using namespace FakeGen; diff --git a/GPU/Common/VertexDecoderX86.cpp b/GPU/Common/VertexDecoderX86.cpp index 6fb69c99df..ac4fcd175d 100644 --- a/GPU/Common/VertexDecoderX86.cpp +++ b/GPU/Common/VertexDecoderX86.cpp @@ -53,7 +53,7 @@ alignas(16) static const float by16384[4] = { 1.0f / 16384.0f, 1.0f / 16384.0f, 1.0f / 16384.0f, 1.0f / 16384.0f, }; -#ifdef _M_X64 +#if PPSSPP_ARCH(AMD64) #ifdef _WIN32 static const X64Reg tempReg1 = RAX; static const X64Reg tempReg2 = R9; @@ -185,7 +185,7 @@ JittedVertexDecoder VertexDecoderJitCache::Compile(const VertexDecoder &dec, int // Parameters automatically fall into place. // This will align the stack properly to 16 bytes (the call of this function pushed RIP, which is 8 bytes). - const uint8_t STACK_FIXED_ALLOC = 64 + 8; + const uint8_t STACK_FIXED_ALLOC = 96 + 8; #endif // Allocate temporary storage on the stack. @@ -197,6 +197,10 @@ JittedVertexDecoder VertexDecoderJitCache::Compile(const VertexDecoder &dec, int MOVUPS(MDisp(ESP, 16), XMM5); MOVUPS(MDisp(ESP, 32), XMM6); MOVUPS(MDisp(ESP, 48), XMM7); +#if PPSSPP_ARCH(AMD64) + MOVUPS(MDisp(ESP, 64), XMM8); + MOVUPS(MDisp(ESP, 80), XMM9); +#endif bool prescaleStep = false; // Look for prescaled texcoord steps @@ -215,7 +219,6 @@ JittedVertexDecoder VertexDecoderJitCache::Compile(const VertexDecoder &dec, int // Add code to convert matrices to 4x4. // Later we might want to do this when the matrices are loaded instead. - int boneCount = 0; if (dec.weighttype && g_Config.bSoftwareSkinning) { MOV(PTRBITS, R(tempReg1), ImmPtr(&threeMasks)); MOVAPS(XMM4, MatR(tempReg1)); @@ -273,9 +276,13 @@ JittedVertexDecoder VertexDecoderJitCache::Compile(const VertexDecoder &dec, int MOVUPS(XMM5, MDisp(ESP, 16)); MOVUPS(XMM6, MDisp(ESP, 32)); MOVUPS(XMM7, MDisp(ESP, 48)); +#if PPSSPP_ARCH(AMD64) + MOVUPS(XMM8, MDisp(ESP, 64)); + MOVUPS(XMM9, MDisp(ESP, 80)); +#endif ADD(PTRBITS, R(ESP), Imm8(STACK_FIXED_ALLOC)); -#ifdef _M_IX86 +#if PPSSPP_ARCH(X86) // Restore register values POP(EBP); POP(EBX); @@ -462,7 +469,7 @@ void VertexDecoderJitCache::Jit_WeightsFloat() { void VertexDecoderJitCache::Jit_WeightsU8Skin() { MOV(PTRBITS, R(tempReg2), ImmPtr(&bones)); -#ifdef _M_X64 +#if PPSSPP_ARCH(AMD64) if (dec_->nweights > 4) { // This reads 8 bytes, we split the top 4 so we can expand each set of 4. MOVQ_xmm(XMM8, MDisp(srcReg, dec_->weightoff)); @@ -514,7 +521,7 @@ void VertexDecoderJitCache::Jit_WeightsU8Skin() { for (int j = 0; j < dec_->nweights; j++) { X64Reg weight = XMM1; -#ifdef _M_X64 +#if PPSSPP_ARCH(AMD64) X64Reg weightSrc = j < 4 ? XMM8 : XMM9; if (j == 3 || j == dec_->nweights - 1) { // In the previous iteration, we already spread this value to all lanes. @@ -572,7 +579,7 @@ void VertexDecoderJitCache::Jit_WeightsU8Skin() { void VertexDecoderJitCache::Jit_WeightsU16Skin() { MOV(PTRBITS, R(tempReg2), ImmPtr(&bones)); -#ifdef _M_X64 +#if PPSSPP_ARCH(AMD64) if (dec_->nweights > 6) { // Since this is probably not aligned, two MOVQs are better than one MOVDQU. MOVQ_xmm(XMM8, MDisp(srcReg, dec_->weightoff)); @@ -628,7 +635,7 @@ void VertexDecoderJitCache::Jit_WeightsU16Skin() { for (int j = 0; j < dec_->nweights; j++) { X64Reg weight = XMM1; -#ifdef _M_X64 +#if PPSSPP_ARCH(AMD64) X64Reg weightSrc = j < 4 ? XMM8 : XMM9; if (j == 3 || j == dec_->nweights - 1) { // In the previous iteration, we already spread this value to all lanes. @@ -726,7 +733,7 @@ void VertexDecoderJitCache::Jit_TcU16ToFloat() { } void VertexDecoderJitCache::Jit_TcFloat() { -#ifdef _M_X64 +#if PPSSPP_ARCH(AMD64) MOV(64, R(tempReg1), MDisp(srcReg, dec_->tcoff)); MOV(64, MDisp(dstReg, dec_->decFmt.uvoff), R(tempReg1)); #else @@ -907,7 +914,7 @@ void VertexDecoderJitCache::Jit_TcU16ThroughToFloat() { } void VertexDecoderJitCache::Jit_TcFloatThrough() { -#ifdef _M_X64 +#if PPSSPP_ARCH(AMD64) MOV(64, R(tempReg1), MDisp(srcReg, dec_->tcoff)); MOV(64, MDisp(dstReg, dec_->decFmt.uvoff), R(tempReg1)); #else diff --git a/GPU/Common/VertexShaderGenerator.cpp b/GPU/Common/VertexShaderGenerator.cpp index b0d5cb6463..f2d3152ec5 100644 --- a/GPU/Common/VertexShaderGenerator.cpp +++ b/GPU/Common/VertexShaderGenerator.cpp @@ -152,7 +152,9 @@ bool GenerateVertexShader(const VShaderID &id, char *buffer, const ShaderLanguag // this is only valid for some settings of uvGenMode GETexProjMapMode uvProjMode = static_cast(id.Bits(VS_BIT_UVPROJ_MODE, 2)); bool doShadeMapping = uvGenMode == GE_TEXMAP_ENVIRONMENT_MAP; - bool doFlatShading = id.Bit(VS_BIT_FLATSHADE) && !bugs.Has(Draw::Bugs::BROKEN_FLAT_IN_SHADER); + + bool flatBug = bugs.Has(Draw::Bugs::BROKEN_FLAT_IN_SHADER) && g_Config.bVendorBugChecksEnabled; + bool doFlatShading = id.Bit(VS_BIT_FLATSHADE) && !flatBug; bool useHWTransform = id.Bit(VS_BIT_USE_HW_TRANSFORM); bool hasColor = id.Bit(VS_BIT_HAS_COLOR) || !useHWTransform; @@ -720,6 +722,12 @@ bool GenerateVertexShader(const VShaderID &id, char *buffer, const ShaderLanguag WRITE(p, "}\n"); } + if (useHWTransform) { + WRITE(p, "vec3 normalizeOr001(vec3 v) {\n"); + WRITE(p, " return length(v) == 0.0 ? vec3(0.0, 0.0, 1.0) : normalize(v);\n"); + WRITE(p, "}\n"); + } + if (ShaderLanguageIsOpenGL(compat.shaderLanguage) || compat.shaderLanguage == GLSL_VULKAN) { WRITE(p, "void main() {\n"); } else if (compat.shaderLanguage == HLSL_D3D9 || compat.shaderLanguage == HLSL_D3D11) { @@ -796,7 +804,7 @@ bool GenerateVertexShader(const VShaderID &id, char *buffer, const ShaderLanguag WRITE(p, " vec3 worldpos = mul(vec4(tess.pos.xyz, 1.0), u_world).xyz;\n"); if (hasNormalTess) { - WRITE(p, " mediump vec3 worldnormal = normalize(mul(vec4(%stess.nrm, 0.0), u_world).xyz);\n", flipNormalTess ? "-" : ""); + WRITE(p, " mediump vec3 worldnormal = normalizeOr001(mul(vec4(%stess.nrm, 0.0), u_world).xyz);\n", flipNormalTess ? "-" : ""); } else { WRITE(p, " mediump vec3 worldnormal = vec3(0.0, 0.0, 1.0);\n"); } @@ -804,7 +812,7 @@ bool GenerateVertexShader(const VShaderID &id, char *buffer, const ShaderLanguag // No skinning, just standard T&L. WRITE(p, " vec3 worldpos = mul(vec4(position, 1.0), u_world).xyz;\n"); if (hasNormal) - WRITE(p, " mediump vec3 worldnormal = normalize(mul(vec4(%snormal, 0.0), u_world).xyz);\n", flipNormal ? "-" : ""); + WRITE(p, " mediump vec3 worldnormal = normalizeOr001(mul(vec4(%snormal, 0.0), u_world).xyz);\n", flipNormal ? "-" : ""); else WRITE(p, " mediump vec3 worldnormal = vec3(0.0, 0.0, 1.0);\n"); } @@ -845,7 +853,7 @@ bool GenerateVertexShader(const VShaderID &id, char *buffer, const ShaderLanguag } else { WRITE(p, " mediump vec3 skinnednormal = mul(vec4(0.0, 0.0, %s1.0, 0.0), skinMatrix).xyz%s;\n", flipNormal ? "-" : "", factor); } - WRITE(p, " mediump vec3 worldnormal = normalize(mul(vec4(skinnednormal, 0.0), u_world).xyz);\n"); + WRITE(p, " mediump vec3 worldnormal = normalizeOr001(mul(vec4(skinnednormal, 0.0), u_world).xyz);\n"); } WRITE(p, " vec4 viewPos = vec4(mul(vec4(worldpos, 1.0), u_view).xyz, 1.0);\n"); @@ -1054,7 +1062,7 @@ bool GenerateVertexShader(const VShaderID &id, char *buffer, const ShaderLanguag break; case GE_PROJMAP_NORMALIZED_NORMAL: // Use normalized transformed normal as source if (hasNormal) - temp_tc = flipNormal ? "vec4(normalize(-normal), 1.0)" : "vec4(normalize(normal), 1.0)"; + temp_tc = StringFromFormat("length(normal) == 0.0 ? vec4(0.0, 0.0, 1.0, 1.0) : vec4(normalize(%snormal), 1.0)", flipNormal ? "-" : ""); else temp_tc = "vec4(0.0, 0.0, 1.0, 1.0)"; break; diff --git a/GPU/D3D11/DrawEngineD3D11.cpp b/GPU/D3D11/DrawEngineD3D11.cpp index 05537d2eea..6dc47766fb 100644 --- a/GPU/D3D11/DrawEngineD3D11.cpp +++ b/GPU/D3D11/DrawEngineD3D11.cpp @@ -317,10 +317,6 @@ VertexArrayInfoD3D11::~VertexArrayInfoD3D11() { ebo->Release(); } -static uint32_t SwapRB(uint32_t c) { - return (c & 0xFF00FF00) | ((c >> 16) & 0xFF) | ((c << 16) & 0xFF0000); -} - // The inline wrapper in the header checks for numDrawCalls == 0 void DrawEngineD3D11::DoFlush() { gpuStats.numFlushes++; @@ -603,8 +599,6 @@ rotateVBO: framebufferManager_->SetSafeSize(result.safeWidth, result.safeHeight); if (result.action == SW_DRAW_PRIMITIVES) { - const int vertexSize = sizeof(transformed[0]); - ApplyDrawStateLate(result.setStencil, result.stencilValue); D3D11VertexShader *vshader; diff --git a/GPU/D3D11/FramebufferManagerD3D11.cpp b/GPU/D3D11/FramebufferManagerD3D11.cpp index fbafe04285..0828b68a1d 100644 --- a/GPU/D3D11/FramebufferManagerD3D11.cpp +++ b/GPU/D3D11/FramebufferManagerD3D11.cpp @@ -184,8 +184,6 @@ void FramebufferManagerD3D11::DrawActiveTexture(float x, float y, float w, float {{x, y + h, 0}, u0, v1}, }; - static const short indices[4] = { 0, 1, 3, 2 }; - if (uvRotation != ROTATION_LOCKED_HORIZONTAL) { float temp[8]; int rotation = 0; diff --git a/GPU/D3D11/ShaderManagerD3D11.cpp b/GPU/D3D11/ShaderManagerD3D11.cpp index 8d612e2fc9..1c615e2b71 100644 --- a/GPU/D3D11/ShaderManagerD3D11.cpp +++ b/GPU/D3D11/ShaderManagerD3D11.cpp @@ -18,7 +18,7 @@ #include "ppsspp_config.h" #include -#include +#include #include @@ -39,7 +39,7 @@ #include "GPU/D3D11/D3D11Util.h" D3D11FragmentShader::D3D11FragmentShader(ID3D11Device *device, D3D_FEATURE_LEVEL featureLevel, FShaderID id, const char *code, bool useHWTransform) - : device_(device), id_(id), failed_(false), useHWTransform_(useHWTransform), module_(0) { + : device_(device), useHWTransform_(useHWTransform), id_(id) { source_ = code; module_ = CreatePixelShaderD3D11(device, code, strlen(code), featureLevel); @@ -64,7 +64,7 @@ std::string D3D11FragmentShader::GetShaderString(DebugShaderStringType type) con } D3D11VertexShader::D3D11VertexShader(ID3D11Device *device, D3D_FEATURE_LEVEL featureLevel, VShaderID id, const char *code, int vertType, bool useHWTransform) - : device_(device), id_(id), failed_(false), useHWTransform_(useHWTransform), module_(nullptr) { + : device_(device), useHWTransform_(useHWTransform), id_(id) { source_ = code; module_ = CreateVertexShaderD3D11(device, code, strlen(code), &bytecode_, featureLevel); diff --git a/GPU/D3D11/ShaderManagerD3D11.h b/GPU/D3D11/ShaderManagerD3D11.h index e0ac257283..ca517d2fa6 100644 --- a/GPU/D3D11/ShaderManagerD3D11.h +++ b/GPU/D3D11/ShaderManagerD3D11.h @@ -44,11 +44,11 @@ public: ID3D11PixelShader *GetShader() const { return module_; } protected: - ID3D11PixelShader *module_; + ID3D11PixelShader *module_ = nullptr; ID3D11Device *device_; std::string source_; - bool failed_; + bool failed_ = false; bool useHWTransform_; FShaderID id_; }; @@ -67,13 +67,13 @@ public: ID3D11VertexShader *GetShader() const { return module_; } protected: - ID3D11VertexShader *module_; + ID3D11VertexShader *module_ = nullptr; ID3D11Device *device_; std::string source_; std::vector bytecode_; - bool failed_; + bool failed_ = false; bool useHWTransform_; VShaderID id_; }; diff --git a/GPU/D3D11/StateMappingD3D11.cpp b/GPU/D3D11/StateMappingD3D11.cpp index 5b0bda982f..5e5d8877b9 100644 --- a/GPU/D3D11/StateMappingD3D11.cpp +++ b/GPU/D3D11/StateMappingD3D11.cpp @@ -284,6 +284,9 @@ void DrawEngineD3D11::ApplyDrawState(int prim) { } if (gstate_c.IsDirty(DIRTY_DEPTHSTENCIL_STATE)) { + GenericStencilFuncState stencilState; + ConvertStencilFuncState(stencilState); + if (gstate.isModeClear()) { keys_.depthStencil.value = 0; keys_.depthStencil.depthTestEnable = true; @@ -307,7 +310,7 @@ void DrawEngineD3D11::ApplyDrawState(int prim) { // We override this value in the pipeline from software transform for clear rectangles. dynState_.stencilRef = 0xFF; // But we still apply the stencil write mask. - keys_.depthStencil.stencilWriteMask = (~gstate.getStencilWriteMask()) & 0xFF; + keys_.depthStencil.stencilWriteMask = stencilState.writeMask; } else { keys_.depthStencil.stencilTestEnable = false; dynState_.useStencil = false; @@ -329,9 +332,6 @@ void DrawEngineD3D11::ApplyDrawState(int prim) { keys_.depthStencil.depthCompareOp = D3D11_COMPARISON_ALWAYS; } - GenericStencilFuncState stencilState; - ConvertStencilFuncState(stencilState); - // Stencil Test if (stencilState.enabled) { keys_.depthStencil.stencilTestEnable = true; diff --git a/GPU/D3D11/TextureCacheD3D11.cpp b/GPU/D3D11/TextureCacheD3D11.cpp index 4d556da5c8..f915420164 100644 --- a/GPU/D3D11/TextureCacheD3D11.cpp +++ b/GPU/D3D11/TextureCacheD3D11.cpp @@ -225,7 +225,7 @@ void TextureCacheD3D11::BindTexture(TexCacheEntry *entry) { lastBoundTexture = textureView; } int maxLevel = (entry->status & TexCacheEntry::STATUS_BAD_MIPS) ? 0 : entry->maxLevel; - SamplerCacheKey samplerKey = GetSamplingParams(maxLevel, entry->addr); + SamplerCacheKey samplerKey = GetSamplingParams(maxLevel, entry); ID3D11SamplerState *state = samplerCache_.GetOrCreateSampler(device_, samplerKey); context_->PSSetSamplers(0, 1, &state); } @@ -703,7 +703,7 @@ void TextureCacheD3D11::LoadTextureLevel(TexCacheEntry &entry, ReplacedTexture & size_t bufSize = sizeof(u32) * (mapRowPitch / bpp) * h; mapData = (u32 *)AllocateAlignedMemory(bufSize, 16); if (!mapData) { - ERROR_LOG(G3D, "Ran out of RAM trying to allocate a temporary texture upload buffer (alloc size: %d, %dx%d)", bufSize, mapRowPitch / sizeof(u32), h); + ERROR_LOG(G3D, "Ran out of RAM trying to allocate a temporary texture upload buffer (alloc size: %lld, %dx%d)", (unsigned long long)bufSize, mapRowPitch / (int)sizeof(u32), h); return; } pixelData = (u32 *)mapData; @@ -747,7 +747,7 @@ void TextureCacheD3D11::LoadTextureLevel(TexCacheEntry &entry, ReplacedTexture & replacedInfo.cachekey = entry.CacheKey(); replacedInfo.hash = entry.fullhash; replacedInfo.addr = entry.addr; - replacedInfo.isVideo = videos_.find(entry.addr & 0x3FFFFFFF) != videos_.end(); + replacedInfo.isVideo = IsVideo(entry.addr); replacedInfo.isFinal = (entry.status & TexCacheEntry::STATUS_TO_SCALE) == 0; replacedInfo.scaleFactor = scaleFactor; replacedInfo.fmt = FromD3D11Format(dstFmt); diff --git a/GPU/D3D11/TextureCacheD3D11.h b/GPU/D3D11/TextureCacheD3D11.h index b1611936a5..f72da416ec 100644 --- a/GPU/D3D11/TextureCacheD3D11.h +++ b/GPU/D3D11/TextureCacheD3D11.h @@ -93,10 +93,6 @@ private: ID3D11ShaderResourceView *lastBoundTexture; ID3D11Buffer *depalConstants_; - int decimationCounter_; - int texelsScaledThisFrame_; - int timesInvalidatedAllThisFrame_; - FramebufferManagerD3D11 *framebufferManagerD3D11_; DepalShaderCacheD3D11 *depalShaderCache_; ShaderManagerD3D11 *shaderManager_; diff --git a/GPU/Debugger/Playback.cpp b/GPU/Debugger/Playback.cpp index 4a7fb9ca00..9009632b33 100644 --- a/GPU/Debugger/Playback.cpp +++ b/GPU/Debugger/Playback.cpp @@ -330,7 +330,7 @@ void DumpExecute::SyncStall() { bool DumpExecute::SubmitCmds(const void *p, u32 sz) { if (execListBuf == 0) { u32 allocSize = LIST_BUF_SIZE; - execListBuf = userMemory.Alloc(allocSize, "List buf"); + execListBuf = userMemory.Alloc(allocSize, true, "List buf"); if (execListBuf == -1) { execListBuf = 0; } diff --git a/GPU/Directx9/DrawEngineDX9.cpp b/GPU/Directx9/DrawEngineDX9.cpp index 0f7603e8de..377d2164f4 100644 --- a/GPU/Directx9/DrawEngineDX9.cpp +++ b/GPU/Directx9/DrawEngineDX9.cpp @@ -553,7 +553,7 @@ rotateVBO: params.fbman = framebufferManager_; params.texCache = textureCache_; params.allowClear = true; - params.allowSeparateAlphaClear = true; + params.allowSeparateAlphaClear = false; params.provokeFlatFirst = true; int maxIndex = indexGen.MaxIndex(); @@ -578,9 +578,6 @@ rotateVBO: // TODO: Add a post-transform cache here for multi-RECTANGLES only. // Might help for text drawing. - // these spam the gDebugger log. - const int vertexSize = sizeof(transformed[0]); - device_->SetVertexDeclaration(transformedVertexDecl_); if (result.drawIndexed) { device_->DrawIndexedPrimitiveUP(d3d_prim[prim], 0, maxIndex, D3DPrimCount(d3d_prim[prim], result.drawNumTrans), inds, D3DFMT_INDEX16, result.drawBuffer, sizeof(TransformedVertex)); @@ -602,7 +599,6 @@ rotateVBO: framebufferManager_->SetColorUpdated(gstate_c.skipDrawReason); } - dxstate.colorMask.set((mask & D3DCLEAR_TARGET) != 0, (mask & D3DCLEAR_TARGET) != 0, (mask & D3DCLEAR_TARGET) != 0, (mask & D3DCLEAR_STENCIL) != 0); device_->Clear(0, NULL, mask, SwapRB(clearColor), clearDepth, clearColor >> 24); if ((gstate_c.featureFlags & GPU_USE_CLEAR_RAM_HACK) && gstate.isClearModeColorMask() && (gstate.isClearModeAlphaMask() || gstate.FrameBufFormat() == GE_FORMAT_565)) { diff --git a/GPU/Directx9/FramebufferManagerDX9.cpp b/GPU/Directx9/FramebufferManagerDX9.cpp index f47678456c..08c40135b3 100644 --- a/GPU/Directx9/FramebufferManagerDX9.cpp +++ b/GPU/Directx9/FramebufferManagerDX9.cpp @@ -164,8 +164,6 @@ static const D3DVERTEXELEMENT9 g_FramebufferVertexElements[] = { x,y+h,0, u0,v1, }; - static const short indices[4] = { 0, 1, 3, 2 }; - if (uvRotation != ROTATION_LOCKED_HORIZONTAL) { float temp[8]; int rotation = 0; @@ -213,7 +211,7 @@ static const D3DVERTEXELEMENT9 g_FramebufferVertexElements[] = { dxstate.stencilMask.set(0xFF); HRESULT hr = device_->DrawPrimitiveUP(D3DPT_TRIANGLEFAN, 2, coord, 5 * sizeof(float)); if (FAILED(hr)) { - ERROR_LOG_REPORT(G3D, "DrawActiveTexture() failed: %08x", hr); + ERROR_LOG_REPORT(G3D, "DrawActiveTexture() failed: %08x", (uint32_t)hr); } } @@ -377,7 +375,7 @@ static const D3DVERTEXELEMENT9 g_FramebufferVertexElements[] = { ConvertFromBGRA8888(Memory::GetPointer(fb_address + dstByteOffset), (u8 *)locked.pBits, vfb->fb_stride, locked.Pitch / 4, w, h, vfb->format); offscreen->UnlockRect(); } else { - ERROR_LOG_REPORT(G3D, "Unable to lock rect from %08x: %d,%d %dx%d of %dx%d", fb_address, rect.left, rect.top, rect.right, rect.bottom, vfb->renderWidth, vfb->renderHeight); + ERROR_LOG_REPORT(G3D, "Unable to lock rect from %08x: %d,%d %dx%d of %dx%d", fb_address, (int)rect.left, (int)rect.top, (int)rect.right, (int)rect.bottom, vfb->renderWidth, vfb->renderHeight); } } else { ERROR_LOG_REPORT(G3D, "Unable to download render target data from %08x", fb_address); @@ -405,7 +403,6 @@ static const D3DVERTEXELEMENT9 g_FramebufferVertexElements[] = { HRESULT hr = tex->LockRect(0, &locked, &rect, D3DLOCK_READONLY); if (SUCCEEDED(hr)) { - const int dstByteOffset = y * vfb->fb_stride * sizeof(s16); const u32 *packed = (const u32 *)locked.pBits; u16 *depth = (u16 *)Memory::GetPointer(z_address); @@ -428,7 +425,7 @@ static const D3DVERTEXELEMENT9 g_FramebufferVertexElements[] = { tex->UnlockRect(0); } else { - ERROR_LOG_REPORT(G3D, "Unable to lock rect from depth %08x: %d,%d %dx%d of %dx%d", vfb->fb_address, rect.left, rect.top, rect.right, rect.bottom, vfb->renderWidth, vfb->renderHeight); + ERROR_LOG_REPORT(G3D, "Unable to lock rect from depth %08x: %d,%d %dx%d of %dx%d", vfb->fb_address, (int)rect.left, (int)rect.top, (int)rect.right, (int)rect.bottom, vfb->renderWidth, vfb->renderHeight); } } else { ERROR_LOG_REPORT(G3D, "Unable to download render target depth from %08x", vfb->fb_address); @@ -467,6 +464,8 @@ static const D3DVERTEXELEMENT9 g_FramebufferVertexElements[] = { } if (!vfb) { + if (!Memory::IsValidAddress(fb_address)) + return false; // If there's no vfb and we're drawing there, must be memory? buffer = GPUDebugBuffer(Memory::GetPointer(fb_address), fb_stride, 512, fb_format); return true; diff --git a/GPU/Directx9/FramebufferManagerDX9.h b/GPU/Directx9/FramebufferManagerDX9.h index 385caf47c0..897e3e2aa9 100644 --- a/GPU/Directx9/FramebufferManagerDX9.h +++ b/GPU/Directx9/FramebufferManagerDX9.h @@ -44,13 +44,13 @@ public: void SetDrawEngine(DrawEngineDX9 *td); void DrawActiveTexture(float x, float y, float w, float h, float destW, float destH, float u0, float v0, float u1, float v1, int uvRotation, int flags) override; - void DestroyAllFBOs(); + void DestroyAllFBOs() override; void EndFrame(); virtual bool NotifyStencilUpload(u32 addr, int size, StencilUpload flags = StencilUpload::NEEDS_CLEAR) override; - bool GetFramebuffer(u32 fb_address, int fb_stride, GEBufferFormat format, GPUDebugBuffer &buffer, int maxRes); + bool GetFramebuffer(u32 fb_address, int fb_stride, GEBufferFormat format, GPUDebugBuffer &buffer, int maxRes) override; bool GetDepthbuffer(u32 fb_address, int fb_stride, u32 z_address, int z_stride, GPUDebugBuffer &buffer) override; bool GetStencilbuffer(u32 fb_address, int fb_stride, GPUDebugBuffer &buffer) override; bool GetOutputFramebuffer(GPUDebugBuffer &buffer) override; diff --git a/GPU/Directx9/ShaderManagerDX9.cpp b/GPU/Directx9/ShaderManagerDX9.cpp index bb34814fcb..38c597f62c 100644 --- a/GPU/Directx9/ShaderManagerDX9.cpp +++ b/GPU/Directx9/ShaderManagerDX9.cpp @@ -50,7 +50,7 @@ using namespace Lin; namespace DX9 { -PSShader::PSShader(LPDIRECT3DDEVICE9 device, FShaderID id, const char *code) : id_(id), shader(nullptr), failed_(false) { +PSShader::PSShader(LPDIRECT3DDEVICE9 device, FShaderID id, const char *code) : id_(id) { source_ = code; #ifdef SHADERLOG OutputDebugString(ConvertUTF8ToWString(code).c_str()); @@ -100,7 +100,7 @@ std::string PSShader::GetShaderString(DebugShaderStringType type) const { } } -VSShader::VSShader(LPDIRECT3DDEVICE9 device, VShaderID id, const char *code, bool useHWTransform) : id_(id), shader(nullptr), failed_(false), useHWTransform_(useHWTransform) { +VSShader::VSShader(LPDIRECT3DDEVICE9 device, VShaderID id, const char *code, bool useHWTransform) : useHWTransform_(useHWTransform), id_(id) { source_ = code; #ifdef SHADERLOG OutputDebugString(ConvertUTF8ToWString(code).c_str()); diff --git a/GPU/Directx9/ShaderManagerDX9.h b/GPU/Directx9/ShaderManagerDX9.h index a524912d50..7c4a39be52 100644 --- a/GPU/Directx9/ShaderManagerDX9.h +++ b/GPU/Directx9/ShaderManagerDX9.h @@ -45,11 +45,11 @@ public: std::string GetShaderString(DebugShaderStringType type) const; - LPDIRECT3DPIXELSHADER9 shader; + LPDIRECT3DPIXELSHADER9 shader = nullptr; protected: std::string source_; - bool failed_; + bool failed_ = false; FShaderID id_; }; @@ -65,11 +65,11 @@ public: std::string GetShaderString(DebugShaderStringType type) const; - LPDIRECT3DVERTEXSHADER9 shader; + LPDIRECT3DVERTEXSHADER9 shader = nullptr; protected: std::string source_; - bool failed_; + bool failed_ = false; bool useHWTransform_; VShaderID id_; }; diff --git a/GPU/Directx9/StateMappingDX9.cpp b/GPU/Directx9/StateMappingDX9.cpp index dfb5c5a754..b4fc44aa1d 100644 --- a/GPU/Directx9/StateMappingDX9.cpp +++ b/GPU/Directx9/StateMappingDX9.cpp @@ -187,6 +187,9 @@ void DrawEngineDX9::ApplyDrawState(int prim) { if (gstate_c.IsDirty(DIRTY_DEPTHSTENCIL_STATE)) { gstate_c.Clean(DIRTY_DEPTHSTENCIL_STATE); + GenericStencilFuncState stencilState; + ConvertStencilFuncState(stencilState); + // Set Stencil/Depth if (gstate.isModeClear()) { // Depth Test @@ -203,7 +206,7 @@ void DrawEngineDX9::ApplyDrawState(int prim) { dxstate.stencilTest.enable(); dxstate.stencilOp.set(D3DSTENCILOP_REPLACE, D3DSTENCILOP_REPLACE, D3DSTENCILOP_REPLACE); dxstate.stencilFunc.set(D3DCMP_ALWAYS, 255, 0xFF); - dxstate.stencilMask.set((~gstate.getStencilWriteMask()) & 0xFF); + dxstate.stencilMask.set(stencilState.writeMask); } else { dxstate.stencilTest.disable(); } @@ -221,9 +224,6 @@ void DrawEngineDX9::ApplyDrawState(int prim) { dxstate.depthTest.disable(); } - GenericStencilFuncState stencilState; - ConvertStencilFuncState(stencilState); - // Stencil Test if (stencilState.enabled) { dxstate.stencilTest.enable(); diff --git a/GPU/Directx9/StencilBufferDX9.cpp b/GPU/Directx9/StencilBufferDX9.cpp index d8e740836b..5da225ae2c 100644 --- a/GPU/Directx9/StencilBufferDX9.cpp +++ b/GPU/Directx9/StencilBufferDX9.cpp @@ -244,7 +244,7 @@ bool FramebufferManagerDX9::NotifyStencilUpload(u32 addr, int size, StencilUploa } HRESULT hr = device_->DrawPrimitiveUP(D3DPT_TRIANGLESTRIP, 2, coord, 5 * sizeof(float)); if (FAILED(hr)) { - ERROR_LOG_REPORT(G3D, "Failed to draw stencil bit %x: %08x", i, hr); + ERROR_LOG_REPORT(G3D, "Failed to draw stencil bit %x: %08x", i, (uint32_t)hr); } } diff --git a/GPU/Directx9/TextureCacheDX9.cpp b/GPU/Directx9/TextureCacheDX9.cpp index 61ed400351..56e9b78dd0 100644 --- a/GPU/Directx9/TextureCacheDX9.cpp +++ b/GPU/Directx9/TextureCacheDX9.cpp @@ -196,7 +196,7 @@ void TextureCacheDX9::BindTexture(TexCacheEntry *entry) { lastBoundTexture = texture; } int maxLevel = (entry->status & TexCacheEntry::STATUS_BAD_MIPS) ? 0 : entry->maxLevel; - SamplerCacheKey samplerKey = GetSamplingParams(maxLevel, entry->addr); + SamplerCacheKey samplerKey = GetSamplingParams(maxLevel, entry); ApplySamplingParams(samplerKey); } @@ -315,7 +315,7 @@ public: device_->SetViewport(&vp); HRESULT hr = device_->DrawPrimitiveUP(D3DPT_TRIANGLESTRIP, 2, verts_, (3 + 2) * sizeof(float)); if (FAILED(hr)) { - ERROR_LOG_REPORT(G3D, "Depal render failed: %08x", hr); + ERROR_LOG_REPORT(G3D, "Depal render failed: %08x", (uint32_t)hr); } dxstate.Restore(); @@ -674,7 +674,7 @@ void TextureCacheDX9::LoadTextureLevel(TexCacheEntry &entry, ReplacedTexture &re replacedInfo.cachekey = entry.CacheKey(); replacedInfo.hash = entry.fullhash; replacedInfo.addr = entry.addr; - replacedInfo.isVideo = videos_.find(entry.addr & 0x3FFFFFFF) != videos_.end(); + replacedInfo.isVideo = IsVideo(entry.addr); replacedInfo.isFinal = (entry.status & TexCacheEntry::STATUS_TO_SCALE) == 0; replacedInfo.scaleFactor = scaleFactor; replacedInfo.fmt = FromD3D9Format(dstFmt); @@ -692,8 +692,6 @@ void TextureCacheDX9::LoadTextureLevel(TexCacheEntry &entry, ReplacedTexture &re bool TextureCacheDX9::GetCurrentTextureDebug(GPUDebugBuffer &buffer, int level) { SetTexture(); ApplyTexture(); - int w = gstate.getTextureWidth(level); - int h = gstate.getTextureHeight(level); LPDIRECT3DBASETEXTURE9 baseTex; LPDIRECT3DTEXTURE9 tex; diff --git a/GPU/Directx9/TextureCacheDX9.h b/GPU/Directx9/TextureCacheDX9.h index 7f27019bfa..d99dfd7ebc 100644 --- a/GPU/Directx9/TextureCacheDX9.h +++ b/GPU/Directx9/TextureCacheDX9.h @@ -86,10 +86,6 @@ private: LPDIRECT3DTEXTURE9 lastBoundTexture; float maxAnisotropyLevel; - int decimationCounter_; - int texelsScaledThisFrame_; - int timesInvalidatedAllThisFrame_; - FramebufferManagerDX9 *framebufferManagerDX9_; DepalShaderCacheDX9 *depalShaderCache_; ShaderManagerDX9 *shaderManager_; diff --git a/GPU/GLES/DrawEngineGLES.cpp b/GPU/GLES/DrawEngineGLES.cpp index a15a179244..1bd602e328 100644 --- a/GPU/GLES/DrawEngineGLES.cpp +++ b/GPU/GLES/DrawEngineGLES.cpp @@ -68,7 +68,7 @@ enum { enum { VAI_KILL_AGE = 120, VAI_UNRELIABLE_KILL_AGE = 240, VAI_UNRELIABLE_KILL_MAX = 4 }; -DrawEngineGLES::DrawEngineGLES(Draw::DrawContext *draw) : vai_(256), draw_(draw), inputLayoutMap_(16) { +DrawEngineGLES::DrawEngineGLES(Draw::DrawContext *draw) : vai_(256), inputLayoutMap_(16), draw_(draw) { render_ = (GLRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER); decOptions_.expandAllWeightsToFloat = false; @@ -594,13 +594,9 @@ void DrawEngineGLES::DoFlush() { ApplyDrawStateLate(result.setStencil, result.stencilValue); - LinkedShader *program = shaderManager_->ApplyFragmentShader(vsid, vshader, lastVType_, framebufferManager_->UseBufferedRendering()); + shaderManager_->ApplyFragmentShader(vsid, vshader, lastVType_, framebufferManager_->UseBufferedRendering()); if (result.action == SW_DRAW_PRIMITIVES) { - const int vertexSize = sizeof(transformed[0]); - - bool doTextureProjection = gstate.getUVGenMode() == GE_TEXMAP_TEXTURE_MATRIX; - if (result.drawIndexed) { vertexBufferOffset = (uint32_t)frameData.pushVertex->Push(result.drawBuffer, maxIndex * sizeof(TransformedVertex), &vertexBuffer); indexBufferOffset = (uint32_t)frameData.pushIndex->Push(inds, sizeof(uint16_t) * result.drawNumTrans, &indexBuffer); diff --git a/GPU/GLES/FramebufferManagerGLES.cpp b/GPU/GLES/FramebufferManagerGLES.cpp index ecd9ce5a53..be01c06702 100644 --- a/GPU/GLES/FramebufferManagerGLES.cpp +++ b/GPU/GLES/FramebufferManagerGLES.cpp @@ -103,9 +103,8 @@ FramebufferManagerGLES::FramebufferManagerGLES(Draw::DrawContext *draw, GLRender { needBackBufferYSwap_ = true; needGLESRebinds_ = true; - CreateDeviceObjects(); - render_ = (GLRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER); presentation_->SetLanguage(draw_->GetShaderLanguageDesc().shaderLanguage); + CreateDeviceObjects(); } void FramebufferManagerGLES::Init() { @@ -173,8 +172,6 @@ void FramebufferManagerGLES::DrawActiveTexture(float x, float y, float w, float u0,v1, }; - static const GLushort indices[4] = { 0,1,3,2 }; - if (uvRotation != ROTATION_LOCKED_HORIZONTAL) { float temp[8]; int rotation = 0; @@ -344,8 +341,8 @@ void FramebufferManagerGLES::DeviceLost() { void FramebufferManagerGLES::DeviceRestore(Draw::DrawContext *draw) { FramebufferManagerCommon::DeviceRestore(draw); - CreateDeviceObjects(); render_ = (GLRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER); + CreateDeviceObjects(); } void FramebufferManagerGLES::Resized() { diff --git a/GPU/GLES/GPU_GLES.cpp b/GPU/GLES/GPU_GLES.cpp index 136701027d..88799d5120 100644 --- a/GPU/GLES/GPU_GLES.cpp +++ b/GPU/GLES/GPU_GLES.cpp @@ -53,7 +53,7 @@ #endif GPU_GLES::GPU_GLES(GraphicsContext *gfxCtx, Draw::DrawContext *draw) -: GPUCommon(gfxCtx, draw), drawEngine_(draw), fragmentTestCache_(draw), depalShaderCache_(draw) { +: GPUCommon(gfxCtx, draw), depalShaderCache_(draw), drawEngine_(draw), fragmentTestCache_(draw) { UpdateVsyncInterval(true); CheckGPUFeatures(); diff --git a/GPU/GLES/StateMappingGLES.cpp b/GPU/GLES/StateMappingGLES.cpp index cc89eda9b7..992695e4a5 100644 --- a/GPU/GLES/StateMappingGLES.cpp +++ b/GPU/GLES/StateMappingGLES.cpp @@ -19,7 +19,7 @@ // Alpha/stencil is a convoluted mess. Some good comments are here: // https://github.com/hrydgard/ppsspp/issues/3768 - +#include "ppsspp_config.h" #include "StateMappingGLES.h" #include "Common/Profiler/Profiler.h" #include "Common/GPU/OpenGL/GLDebugLog.h" @@ -58,7 +58,7 @@ static const GLushort glBlendFactorLookup[(size_t)BlendFactor::COUNT] = { GL_ONE_MINUS_SRC1_COLOR, GL_SRC1_ALPHA, GL_ONE_MINUS_SRC1_ALPHA, -#elif !defined(IOS) +#elif !PPSSPP_PLATFORM(IOS) GL_SRC1_COLOR_EXT, GL_ONE_MINUS_SRC1_COLOR_EXT, GL_SRC1_ALPHA_EXT, @@ -235,13 +235,16 @@ void DrawEngineGLES::ApplyDrawState(int prim) { if (gstate_c.IsDirty(DIRTY_DEPTHSTENCIL_STATE)) { gstate_c.Clean(DIRTY_DEPTHSTENCIL_STATE); + GenericStencilFuncState stencilState; + ConvertStencilFuncState(stencilState); + if (gstate.isModeClear()) { // Depth Test if (gstate.isClearModeDepthMask()) { framebufferManager_->SetDepthUpdated(); } renderManager->SetStencilFunc(gstate.isClearModeAlphaMask(), GL_ALWAYS, 0xFF, 0xFF); - renderManager->SetStencilOp((~gstate.getStencilWriteMask()) & 0xFF, GL_REPLACE, GL_REPLACE, GL_REPLACE); + renderManager->SetStencilOp(stencilState.writeMask, GL_REPLACE, GL_REPLACE, GL_REPLACE); renderManager->SetDepth(true, gstate.isClearModeDepthMask() ? true : false, GL_ALWAYS); } else { // Depth Test @@ -250,8 +253,6 @@ void DrawEngineGLES::ApplyDrawState(int prim) { framebufferManager_->SetDepthUpdated(); } - GenericStencilFuncState stencilState; - ConvertStencilFuncState(stencilState); // Stencil Test if (stencilState.enabled) { renderManager->SetStencilFunc(stencilState.enabled, compareOps[stencilState.testFunc], stencilState.testRef, stencilState.testMask); diff --git a/GPU/GLES/TextureCacheGLES.cpp b/GPU/GLES/TextureCacheGLES.cpp index 7075950324..2b153eb8fe 100644 --- a/GPU/GLES/TextureCacheGLES.cpp +++ b/GPU/GLES/TextureCacheGLES.cpp @@ -45,7 +45,6 @@ TextureCacheGLES::TextureCacheGLES(Draw::DrawContext *draw) : TextureCacheCommon(draw) { - timesInvalidatedAllThisFrame_ = 0; render_ = (GLRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER); SetupTextureDecoder(); @@ -229,7 +228,7 @@ void TextureCacheGLES::BindTexture(TexCacheEntry *entry) { lastBoundTexture = entry->textureName; } int maxLevel = (entry->status & TexCacheEntry::STATUS_BAD_MIPS) ? 0 : entry->maxLevel; - SamplerCacheKey samplerKey = GetSamplingParams(maxLevel, entry->addr); + SamplerCacheKey samplerKey = GetSamplingParams(maxLevel, entry); ApplySamplingParams(samplerKey); gstate_c.SetUseShaderDepal(false); } @@ -327,7 +326,6 @@ public: } void Shade(GLRenderManager *render) { - static const GLubyte indices[4] = { 0, 1, 3, 2 }; render->SetViewport(GLRViewport{ 0, 0, (float)renderW_, (float)renderH_, 0.0f, 1.0f }); render->Draw(GL_TRIANGLE_STRIP, 0, 4); } @@ -705,7 +703,7 @@ void TextureCacheGLES::LoadTextureLevel(TexCacheEntry &entry, ReplacedTexture &r replacedInfo.cachekey = entry.CacheKey(); replacedInfo.hash = entry.fullhash; replacedInfo.addr = entry.addr; - replacedInfo.isVideo = videos_.find(entry.addr & 0x3FFFFFFF) != videos_.end(); + replacedInfo.isVideo = IsVideo(entry.addr); replacedInfo.isFinal = (entry.status & TexCacheEntry::STATUS_TO_SCALE) == 0; replacedInfo.scaleFactor = scaleFactor; replacedInfo.fmt = FromDataFormat(dstFmt); diff --git a/GPU/GPUCommon.cpp b/GPU/GPUCommon.cpp index eb94384b3e..e08ee51b51 100644 --- a/GPU/GPUCommon.cpp +++ b/GPU/GPUCommon.cpp @@ -1,3 +1,4 @@ +#include "ppsspp_config.h" #include #include #include @@ -17,6 +18,7 @@ #include "GPU/GPUState.h" #include "Core/Config.h" #include "Core/CoreTiming.h" +#include "Core/Debugger/MemBlockInfo.h" #include "Core/MemMap.h" #include "Core/Host.h" #include "Core/Reporting.h" @@ -25,7 +27,6 @@ #include "Core/HLE/sceKernelInterrupt.h" #include "Core/HLE/sceKernelThread.h" #include "Core/HLE/sceGe.h" -#include "Core/Debugger/Breakpoints.h" #include "Core/MemMapHelpers.h" #include "Core/Util/PPGeDraw.h" #include "GPU/Common/DrawEngineCommon.h" @@ -367,10 +368,6 @@ void GPUCommon::Flush() { } GPUCommon::GPUCommon(GraphicsContext *gfxCtx, Draw::DrawContext *draw) : - dumpNextFrame_(false), - dumpThisFrame_(false), - framebufferManager_(nullptr), - resized_(false), gfxCtx_(gfxCtx), draw_(draw) { @@ -675,7 +672,7 @@ int GPUCommon::GetStack(int index, u32 stackPtr) { } if (index >= 0) { - auto stack = PSPPointer::Create(stackPtr); + auto stack = PSPPointer::Create(stackPtr); if (stack.IsValid()) { auto entry = currentList->stack[index]; // Not really sure what most of these values are. @@ -706,7 +703,7 @@ u32 GPUCommon::EnqueueList(u32 listpc, u32 stall, int subIntrBase, PSPPointersize >= 16 ? args->stackAddr : 0; + u32 stackAddr = args.IsValid() && args->size >= 16 ? (u32)args->stackAddr : 0; // Check compatibility if (sceKernelGetCompiledSdkVersion() > 0x01FFFFFF) { //numStacks = 0; @@ -957,7 +954,9 @@ void GPUCommon::NotifySteppingExit() { if (timeSteppingStarted_ <= 0.0) { ERROR_LOG(G3D, "Mismatched stepping enter/exit."); } - timeSpentStepping_ += time_now_d() - timeSteppingStarted_; + double total = time_now_d() - timeSteppingStarted_; + _dbg_assert_msg_(total >= 0.0, "Time spent stepping became negative"); + timeSpentStepping_ += total; timeSteppingStarted_ = 0.0; } } @@ -1034,6 +1033,7 @@ bool GPUCommon::InterpretList(DisplayList &list) { if (coreCollectDebugStats) { double total = time_now_d() - start - timeSpentStepping_; + _dbg_assert_msg_(total >= 0.0, "Time spent DL processing became negative"); hleSetSteppingTime(timeSpentStepping_); timeSpentStepping_ = 0.0; gpuStats.msProcessingDisplayLists += total; @@ -1048,7 +1048,7 @@ void GPUCommon::FastRunLoop(DisplayList &list) { int dc = downcount; for (; dc > 0; --dc) { // We know that display list PCs have the upper nibble == 0 - no need to mask the pointer - const u32 op = *(const u32 *)(Memory::base + list.pc); + const u32 op = *(const u32_le *)(Memory::base + list.pc); const u32 cmd = op >> 24; const CommandInfo &info = cmdInfo[cmd]; const u32 diff = op ^ gstate.cmdmem[cmd]; @@ -1486,6 +1486,14 @@ void GPUCommon::Execute_End(u32 op, u32 diff) { default: currentList->subIntrToken = prev & 0xFFFF; UpdateState(GPUSTATE_DONE); + // Since we marked done, we have to restore the context now before the next list runs. + if (currentList->started && currentList->context.IsValid()) { + gstate.Restore(currentList->context); + ReapplyGfxState(); + // Don't restore the context again. + currentList->started = false; + } + if (currentList->interruptsEnabled && __GeTriggerInterrupt(currentList->id, currentList->pc, startingTicks + cyclesExecuted)) { currentList->pendingInterrupt = true; } else { @@ -1493,10 +1501,6 @@ void GPUCommon::Execute_End(u32 op, u32 diff) { currentList->waitTicks = startingTicks + cyclesExecuted; busyTicks = std::max(busyTicks, currentList->waitTicks); __GeTriggerSync(GPU_SYNC_LIST, currentList->id, currentList->waitTicks); - if (currentList->started && currentList->context.IsValid()) { - gstate.Restore(currentList->context); - ReapplyGfxState(); - } } break; } @@ -1639,8 +1643,8 @@ void GPUCommon::Execute_Prim(u32 op, u32 diff) { int totalVertCount = count; // PRIMs are often followed by more PRIMs. Save some work and submit them immediately. - const u32 *src = (const u32 *)Memory::GetPointerUnchecked(currentList->pc + 4); - const u32 *stall = currentList->stall ? (const u32 *)Memory::GetPointerUnchecked(currentList->stall) : 0; + const u32_le *src = (const u32_le *)Memory::GetPointerUnchecked(currentList->pc + 4); + const u32_le *stall = currentList->stall ? (const u32_le *)Memory::GetPointerUnchecked(currentList->stall) : 0; int cmdCount = 0; // Optimized submission of sequences of PRIM. Allows us to avoid going through all the mess @@ -2247,7 +2251,6 @@ void GPUCommon::Execute_ImmVertexAlphaPrim(u32 op, u32 diff) { return; } - uint32_t data = op & 0xFFFFFF; TransformedVertex &v = immBuffer_[immCount_++]; // Formula deduced from ThrillVille's clear. @@ -2724,8 +2727,8 @@ void GPUCommon::DoBlockTransfer(u32 skipDrawReason) { framebufferManager_->NotifyBlockTransferAfter(dstBasePtr, dstStride, dstX, dstY, srcBasePtr, srcStride, srcX, srcY, width, height, bpp, skipDrawReason); } - CBreakPoints::ExecMemCheck(srcBasePtr + (srcY * srcStride + srcX) * bpp, false, height * srcStride * bpp, currentMIPS->pc); - CBreakPoints::ExecMemCheck(dstBasePtr + (dstY * dstStride + dstX) * bpp, true, height * dstStride * bpp, currentMIPS->pc); + NotifyMemInfo(MemBlockFlags::READ, srcBasePtr + (srcY * srcStride + srcX) * bpp, height * srcStride * bpp, "GPUBlockTransfer"); + NotifyMemInfo(MemBlockFlags::WRITE, dstBasePtr + (dstY * dstStride + dstX) * bpp, height * dstStride * bpp, "GPUBlockTransfer"); // TODO: Correct timing appears to be 1.9, but erring a bit low since some of our other timing is inaccurate. cyclesExecuted += ((height * width * bpp) * 16) / 10; @@ -2735,9 +2738,7 @@ bool GPUCommon::PerformMemoryCopy(u32 dest, u32 src, int size) { // Track stray copies of a framebuffer in RAM. MotoGP does this. if (framebufferManager_->MayIntersectFramebuffer(src) || framebufferManager_->MayIntersectFramebuffer(dest)) { if (!framebufferManager_->NotifyFramebufferCopy(src, dest, size, false, gstate_c.skipDrawReason)) { - // TODO: What? Why would a game copy between the mirrors? This check seems entirely - // superfluous. - // We use a little hack for Download/Upload using a VRAM mirror. + // We use a little hack for PerformMemoryDownload/PerformMemoryUpload using a VRAM mirror. // Since they're identical we don't need to copy. if (!Memory::IsVRAMAddress(dest) || (dest ^ 0x00400000) != src) { Memory::Memcpy(dest, src, size); @@ -2747,6 +2748,8 @@ bool GPUCommon::PerformMemoryCopy(u32 dest, u32 src, int size) { return true; } + NotifyMemInfo(MemBlockFlags::READ, src, size, "GPUMemcpy"); + NotifyMemInfo(MemBlockFlags::WRITE, dest, size, "GPUMemcpy"); InvalidateCache(dest, size, GPU_INVALIDATE_HINT); GPURecord::NotifyMemcpy(dest, src, size); return false; @@ -2755,13 +2758,14 @@ bool GPUCommon::PerformMemoryCopy(u32 dest, u32 src, int size) { bool GPUCommon::PerformMemorySet(u32 dest, u8 v, int size) { // This may indicate a memset, usually to 0, of a framebuffer. if (framebufferManager_->MayIntersectFramebuffer(dest)) { - Memory::Memset(dest, v, size); + Memory::Memset(dest, v, size, "GPUMemset"); if (!framebufferManager_->NotifyFramebufferCopy(dest, dest, size, true, gstate_c.skipDrawReason)) { InvalidateCache(dest, size, GPU_INVALIDATE_HINT); } return true; } + NotifyMemInfo(MemBlockFlags::WRITE, dest, size, "GPUMemset"); // Or perhaps a texture, let's invalidate. InvalidateCache(dest, size, GPU_INVALIDATE_HINT); GPURecord::NotifyMemset(dest, v, size); diff --git a/GPU/GPUCommon.h b/GPU/GPUCommon.h index 533f027542..b58f3cafd7 100644 --- a/GPU/GPUCommon.h +++ b/GPU/GPUCommon.h @@ -1,5 +1,6 @@ #pragma once +#include "ppsspp_config.h" #include "Common/Common.h" #include "Common/MemoryUtil.h" #include "GPU/GPUInterface.h" @@ -342,8 +343,8 @@ protected: u32 cycleLastPC; int cyclesExecuted; - bool dumpNextFrame_; - bool dumpThisFrame_; + bool dumpNextFrame_ = false; + bool dumpThisFrame_ = false; bool debugRecording_; bool interruptsEnabled_; bool resized_ = false; diff --git a/GPU/GPUInterface.h b/GPU/GPUInterface.h index ebc569f9d2..19c2cc72ab 100644 --- a/GPU/GPUInterface.h +++ b/GPU/GPUInterface.h @@ -153,6 +153,8 @@ enum GPUInvalidationType { GPU_INVALIDATE_HINT, // Reliable invalidation (where any hashing, etc. is unneeded, it'll always invalidate.) GPU_INVALIDATE_SAFE, + // Forced invalidation for when the texture hash may not catch changes. + GPU_INVALIDATE_FORCE, }; namespace Draw { diff --git a/GPU/GPUState.cpp b/GPU/GPUState.cpp index bc62a3661b..1aa5050d6b 100644 --- a/GPU/GPUState.cpp +++ b/GPU/GPUState.cpp @@ -15,6 +15,7 @@ // Official git repository and contact information can be found at // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. +#include "ppsspp_config.h" #include "GPU/ge_constants.h" #include "GPU/GPUState.h" diff --git a/GPU/GeConstants.cpp b/GPU/GeConstants.cpp index a8a27685cb..ab627ee473 100644 --- a/GPU/GeConstants.cpp +++ b/GPU/GeConstants.cpp @@ -10,6 +10,16 @@ const char *GeBufferFormatToString(GEBufferFormat fmt) { } } +const char *GEPaletteFormatToString(GEPaletteFormat pfmt) { + switch (pfmt) { + case GE_CMODE_16BIT_BGR5650: return "565"; + case GE_CMODE_16BIT_ABGR5551: return "5551"; + case GE_CMODE_16BIT_ABGR4444: return "4444"; + case GE_CMODE_32BIT_ABGR8888: return "8888"; + default: return "N/A"; + } +} + const char *GeTextureFormatToString(GETextureFormat fmt) { switch (fmt) { case GE_TFMT_5650: return "565"; @@ -26,3 +36,41 @@ const char *GeTextureFormatToString(GETextureFormat fmt) { default: return "N/A"; } } + +const char *GeTextureFormatToString(GETextureFormat tfmt, GEPaletteFormat pfmt) { + switch (tfmt) { + case GE_TFMT_CLUT4: + switch (pfmt) { + case GE_CMODE_16BIT_BGR5650: return "CLUT4_565"; + case GE_CMODE_16BIT_ABGR5551: return "CLUT4_5551"; + case GE_CMODE_16BIT_ABGR4444: return "CLUT4_4444"; + case GE_CMODE_32BIT_ABGR8888: return "CLUT4_8888"; + default: return "N/A"; + } + case GE_TFMT_CLUT8: + switch (pfmt) { + case GE_CMODE_16BIT_BGR5650: return "CLUT8_565"; + case GE_CMODE_16BIT_ABGR5551: return "CLUT8_5551"; + case GE_CMODE_16BIT_ABGR4444: return "CLUT8_4444"; + case GE_CMODE_32BIT_ABGR8888: return "CLUT8_8888"; + default: return "N/A"; + } + case GE_TFMT_CLUT16: + switch (pfmt) { + case GE_CMODE_16BIT_BGR5650: return "CLUT16_565"; + case GE_CMODE_16BIT_ABGR5551: return "CLUT16_5551"; + case GE_CMODE_16BIT_ABGR4444: return "CLUT16_4444"; + case GE_CMODE_32BIT_ABGR8888: return "CLUT16_8888"; + default: return "N/A"; + } + case GE_TFMT_CLUT32: + switch (pfmt) { + case GE_CMODE_16BIT_BGR5650: return "CLUT32_565"; + case GE_CMODE_16BIT_ABGR5551: return "CLUT32_5551"; + case GE_CMODE_16BIT_ABGR4444: return "CLUT32_4444"; + case GE_CMODE_32BIT_ABGR8888: return "CLUT32_8888"; + default: return "N/A"; + } + default: return GeTextureFormatToString(tfmt); + } +} diff --git a/GPU/Math3D.cpp b/GPU/Math3D.cpp index c46a4e520d..811fad4923 100644 --- a/GPU/Math3D.cpp +++ b/GPU/Math3D.cpp @@ -138,12 +138,31 @@ Vec3 Vec3::Normalized(bool useSSE4) const const __m128 normalize = SSENormalizeMultiplier(useSSE4, vec); return _mm_mul_ps(normalize, vec); } + +template<> +Vec3 Vec3::NormalizedOr001(bool useSSE4) const { + const __m128 normalize = SSENormalizeMultiplier(useSSE4, vec); + const __m128 result = _mm_mul_ps(normalize, vec); + const __m128 mask = _mm_cmpunord_ps(result, vec); + const __m128 replace = _mm_and_ps(_mm_set_ps(0.0f, 1.0f, 0.0f, 0.0f), mask); + // Replace with the constant if the mask matched. + return _mm_or_ps(_mm_andnot_ps(mask, result), replace); +} #else template<> Vec3 Vec3::Normalized(bool useSSE4) const { return (*this) / Length(); } + +template<> +Vec3 Vec3::NormalizedOr001(bool useSSE4) const { + float len = Length(); + if (len == 0.0f) { + return Vec3(0.0f, 0.0f, 1.0f); + } + return *this / len; +} #endif template<> @@ -154,6 +173,17 @@ float Vec3::Normalize() return len; } +template<> +float Vec3::NormalizeOr001() { + float len = Length(); + if (len == 0.0f) { + z = 1.0f; + } else { + *this /= len; + } + return len; +} + template<> Vec3Packed Vec3Packed::FromRGB(unsigned int rgb) { diff --git a/GPU/Math3D.h b/GPU/Math3D.h index 873119613d..62b017d15d 100644 --- a/GPU/Math3D.h +++ b/GPU/Math3D.h @@ -297,7 +297,9 @@ public: Vec3 WithLength(const float l) const; float Distance2To(Vec3 &other); Vec3 Normalized(bool useSSE4 = false) const; + Vec3 NormalizedOr001(bool useSSE4 = false) const; float Normalize(); // returns the previous length, which is often useful + float NormalizeOr001(); T& operator [] (int i) //allow vector[2] = 3 (vector.z=3) { diff --git a/GPU/Software/Clipper.cpp b/GPU/Software/Clipper.cpp index 66db9a08e4..ee6f7ff3b9 100644 --- a/GPU/Software/Clipper.cpp +++ b/GPU/Software/Clipper.cpp @@ -105,7 +105,6 @@ inline float clip_dotprod(const VertexData &vert, float A, float B, float C, flo if (mask & PLANE_BIT) { \ float dp0 = clip_dotprod(*Vertices[0], A, B, C, D ); \ float dp1 = clip_dotprod(*Vertices[1], A, B, C, D ); \ - int numVertices = 0; \ \ if (mask0 & PLANE_BIT) { \ if (dp0 < 0) { \ diff --git a/GPU/Software/Lighting.cpp b/GPU/Software/Lighting.cpp index 4bc4fe1576..5c60bd66dc 100644 --- a/GPU/Software/Lighting.cpp +++ b/GPU/Software/Lighting.cpp @@ -15,9 +15,9 @@ // Official git repository and contact information can be found at // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. -#include "../GPUState.h" - -#include "Lighting.h" +#include "Common/CPUDetect.h" +#include "GPU/GPUState.h" +#include "GPU/Software/Lighting.h" namespace Lighting { @@ -53,7 +53,7 @@ void Process(VertexData& vertex, bool hasColor) { if (gstate.getUVGenMode() == GE_TEXMAP_ENVIRONMENT_MAP) { Vec3 L = GetLightVec(gstate.lpos, light); // In other words, L.Length2() == 0.0f means Dot({0, 0, 1}, worldnormal). - float diffuse_factor = L.Length2() == 0.0f ? vertex.worldnormal.z : Dot(L.Normalized(), vertex.worldnormal); + float diffuse_factor = Dot(L.NormalizedOr001(cpu_info.bSSE4_1), vertex.worldnormal); if (gstate.getUVLS0() == (int)light) vertex.texturecoords.s() = (diffuse_factor + 1.f) / 2.f; @@ -77,7 +77,7 @@ void Process(VertexData& vertex, bool hasColor) { L -= vertex.worldpos; } // TODO: Should this normalize (0, 0, 0) to (0, 0, 1)? - float d = L.Normalize(); + float d = L.NormalizeOr001(); float att = 1.f; if (!gstate.isDirectionalLight(light)) { @@ -89,7 +89,7 @@ void Process(VertexData& vertex, bool hasColor) { float spot = 1.f; if (gstate.isSpotLight(light)) { Vec3 dir = GetLightVec(gstate.ldir, light); - float rawSpot = dir.Length2() == 0.0f ? 0.0f : Dot(dir.Normalized(), L); + float rawSpot = Dot(dir.NormalizedOr001(cpu_info.bSSE4_1), L); float cutoff = getFloat24(gstate.lcutoff[light]); if (rawSpot >= cutoff) { float conv = getFloat24(gstate.lconv[light]); @@ -123,7 +123,7 @@ void Process(VertexData& vertex, bool hasColor) { Vec3 lsc = Vec3::FromRGB(gstate.getSpecularColor(light)); Vec3 msc = (materialupdate & 4) ? vcol0 : Vec3::FromRGB(gstate.getMaterialSpecular()); - float specular_factor = Dot(H.Normalized(), vertex.worldnormal); + float specular_factor = Dot(H.NormalizedOr001(cpu_info.bSSE4_1), vertex.worldnormal); float k = gstate.getMaterialSpecularCoef(); specular_factor = pspLightPow(specular_factor, k); diff --git a/GPU/Software/Rasterizer.cpp b/GPU/Software/Rasterizer.cpp index feaeca1e89..943b3f5656 100644 --- a/GPU/Software/Rasterizer.cpp +++ b/GPU/Software/Rasterizer.cpp @@ -15,6 +15,7 @@ // Official git repository and contact information can be found at // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. +#include "ppsspp_config.h" #include #include @@ -27,7 +28,6 @@ #include "Core/Reporting.h" #include "GPU/GPUState.h" -#include "GPU/Common/TextureCacheCommon.h" #include "GPU/Common/TextureDecoder.h" #include "GPU/Software/SoftGpu.h" #include "GPU/Software/Rasterizer.h" @@ -40,7 +40,7 @@ namespace Rasterizer { // Only OK on x64 where our stack is aligned -#if defined(_M_SSE) && !defined(_M_IX86) +#if defined(_M_SSE) && !PPSSPP_ARCH(X86) static inline __m128 Interpolate(const __m128 &c0, const __m128 &c1, const __m128 &c2, int w0, int w1, int w2, float wsum) { __m128 v = _mm_mul_ps(c0, _mm_cvtepi32_ps(_mm_set1_epi32(w0))); v = _mm_add_ps(v, _mm_mul_ps(c1, _mm_cvtepi32_ps(_mm_set1_epi32(w1)))); @@ -57,7 +57,7 @@ static inline __m128i Interpolate(const __m128i &c0, const __m128i &c1, const __ // Not sure if that should be regarded as a bug or if casting to float is a valid fix. static inline Vec4 Interpolate(const Vec4 &c0, const Vec4 &c1, const Vec4 &c2, int w0, int w1, int w2, float wsum) { -#if defined(_M_SSE) && !defined(_M_IX86) +#if defined(_M_SSE) && !PPSSPP_ARCH(X86) return Vec4(Interpolate(c0.ivec, c1.ivec, c2.ivec, w0, w1, w2, wsum)); #else return ((c0.Cast() * w0 + c1.Cast() * w1 + c2.Cast() * w2) * wsum).Cast(); @@ -65,7 +65,7 @@ static inline Vec4 Interpolate(const Vec4 &c0, const Vec4 &c1, co } static inline Vec3 Interpolate(const Vec3 &c0, const Vec3 &c1, const Vec3 &c2, int w0, int w1, int w2, float wsum) { -#if defined(_M_SSE) && !defined(_M_IX86) +#if defined(_M_SSE) && !PPSSPP_ARCH(X86) return Vec3(Interpolate(c0.ivec, c1.ivec, c2.ivec, w0, w1, w2, wsum)); #else return ((c0.Cast() * w0 + c1.Cast() * w1 + c2.Cast() * w2) * wsum).Cast(); @@ -73,7 +73,7 @@ static inline Vec3 Interpolate(const Vec3 &c0, const Vec3 &c1, co } static inline Vec2 Interpolate(const Vec2 &c0, const Vec2 &c1, const Vec2 &c2, int w0, int w1, int w2, float wsum) { -#if defined(_M_SSE) && !defined(_M_IX86) +#if defined(_M_SSE) && !PPSSPP_ARCH(X86) return Vec2(Interpolate(c0.vec, c1.vec, c2.vec, w0, w1, w2, wsum)); #else return (c0 * w0 + c1 * w1 + c2 * w2) * wsum; @@ -81,7 +81,7 @@ static inline Vec2 Interpolate(const Vec2 &c0, const Vec2 & } static inline Vec4 Interpolate(const float &c0, const float &c1, const float &c2, const Vec4 &w0, const Vec4 &w1, const Vec4 &w2, const Vec4 &wsum_recip) { -#if defined(_M_SSE) && !defined(_M_IX86) +#if defined(_M_SSE) && !PPSSPP_ARCH(X86) __m128 v = _mm_mul_ps(w0.vec, _mm_set1_ps(c0)); v = _mm_add_ps(v, _mm_mul_ps(w1.vec, _mm_set1_ps(c1))); v = _mm_add_ps(v, _mm_mul_ps(w2.vec, _mm_set1_ps(c2))); @@ -1084,7 +1084,7 @@ Vec4 TriangleEdge::Start(const ScreenCoords &v0, const ScreenCoords &v1, co } inline Vec4 TriangleEdge::StepX(const Vec4 &w) { -#if defined(_M_SSE) && !defined(_M_IX86) +#if defined(_M_SSE) && !PPSSPP_ARCH(X86) return _mm_add_epi32(w.ivec, stepX.ivec); #else return w + stepX; @@ -1092,7 +1092,7 @@ inline Vec4 TriangleEdge::StepX(const Vec4 &w) { } inline Vec4 TriangleEdge::StepY(const Vec4 &w) { -#if defined(_M_SSE) && !defined(_M_IX86) +#if defined(_M_SSE) && !PPSSPP_ARCH(X86) return _mm_add_epi32(w.ivec, stepY.ivec); #else return w + stepY; @@ -1100,7 +1100,7 @@ inline Vec4 TriangleEdge::StepY(const Vec4 &w) { } static inline Vec4 MakeMask(const Vec4 &w0, const Vec4 &w1, const Vec4 &w2, const Vec4 &bias0, const Vec4 &bias1, const Vec4 &bias2, const Vec4 &scissor) { -#if defined(_M_SSE) && !defined(_M_IX86) +#if defined(_M_SSE) && !PPSSPP_ARCH(X86) __m128i biased0 = _mm_add_epi32(w0.ivec, bias0.ivec); __m128i biased1 = _mm_add_epi32(w1.ivec, bias1.ivec); __m128i biased2 = _mm_add_epi32(w2.ivec, bias2.ivec); @@ -1112,7 +1112,7 @@ static inline Vec4 MakeMask(const Vec4 &w0, const Vec4 &w1, const } static inline bool AnyMask(const Vec4 &mask) { -#if defined(_M_SSE) && !defined(_M_IX86) +#if defined(_M_SSE) && !PPSSPP_ARCH(X86) // In other words: !(mask.x < 0 && mask.y < 0 && mask.z < 0 && mask.w < 0) __m128i low2 = _mm_and_si128(mask.ivec, _mm_shuffle_epi32(mask.ivec, _MM_SHUFFLE(3, 2, 3, 2))); __m128i low1 = _mm_and_si128(low2, _mm_shuffle_epi32(low2, _MM_SHUFFLE(1, 1, 1, 1))); @@ -1124,7 +1124,7 @@ static inline bool AnyMask(const Vec4 &mask) { } static inline Vec4 EdgeRecip(const Vec4 &w0, const Vec4 &w1, const Vec4 &w2) { -#if defined(_M_SSE) && !defined(_M_IX86) +#if defined(_M_SSE) && !PPSSPP_ARCH(X86) __m128i wsum = _mm_add_epi32(w0.ivec, _mm_add_epi32(w1.ivec, w2.ivec)); // _mm_rcp_ps loses too much precision. return _mm_div_ps(_mm_set1_ps(1.0f), _mm_cvtepi32_ps(wsum)); @@ -1661,7 +1661,6 @@ void DrawLine(const VertexData &v0, const VertexData &v1) if (gstate.isAntiAliasEnabled()) { // TODO: This is a niave and wrong implementation. DrawingCoords p0 = TransformUnit::ScreenToDrawing(ScreenCoords((int)x, (int)y, (int)z)); - DrawingCoords p1 = TransformUnit::ScreenToDrawing(ScreenCoords((int)(x + xinc), (int)(y + yinc), (int)(z + zinc))); s = ((float)p0.x + xinc / 32.0f) / 512.0f; t = ((float)p0.y + yinc / 32.0f) / 512.0f; diff --git a/GPU/Software/Sampler.cpp b/GPU/Software/Sampler.cpp index 63a0f79b0d..2fccf208b2 100644 --- a/GPU/Software/Sampler.cpp +++ b/GPU/Software/Sampler.cpp @@ -89,13 +89,13 @@ SamplerJitCache::SamplerJitCache() AllocCodeSpace(1024 * 64 * 4); // Add some random code to "help" MSVC's buggy disassembler :( -#if defined(_WIN32) && (defined(_M_IX86) || defined(_M_X64)) && !PPSSPP_PLATFORM(UWP) +#if defined(_WIN32) && (PPSSPP_ARCH(X86) || PPSSPP_ARCH(AMD64)) && !PPSSPP_PLATFORM(UWP) using namespace Gen; for (int i = 0; i < 100; i++) { MOV(32, R(EAX), R(EBX)); RET(); } -#elif defined(ARM) +#elif PPSSPP_ARCH(ARM) BKPT(0); BKPT(0); #endif @@ -215,7 +215,7 @@ NearestFunc SamplerJitCache::GetNearest(const SamplerID &id) { Clear(); } -#if defined(_M_X64) && !PPSSPP_PLATFORM(UWP) +#if PPSSPP_ARCH(AMD64) && !PPSSPP_PLATFORM(UWP) addresses_[id] = GetCodePointer(); NearestFunc func = Compile(id); cache_[id] = func; @@ -238,8 +238,7 @@ LinearFunc SamplerJitCache::GetLinear(const SamplerID &id) { Clear(); } - // TODO -#if defined(_M_X64) && !PPSSPP_PLATFORM(UWP) +#if PPSSPP_ARCH(AMD64) && !PPSSPP_PLATFORM(UWP) addresses_[id] = GetCodePointer(); LinearFunc func = CompileLinear(id); cache_[id] = (NearestFunc)func; diff --git a/GPU/Software/SoftGpu.cpp b/GPU/Software/SoftGpu.cpp index bff4f56be6..a04369a723 100644 --- a/GPU/Software/SoftGpu.cpp +++ b/GPU/Software/SoftGpu.cpp @@ -26,7 +26,7 @@ #include "Core/Config.h" #include "Core/ConfigValues.h" #include "Core/Core.h" -#include "Core/Debugger/Breakpoints.h" +#include "Core/Debugger/MemBlockInfo.h" #include "Core/MemMap.h" #include "Core/HLE/sceKernelInterrupt.h" #include "Core/HLE/sceGe.h" @@ -653,8 +653,8 @@ void SoftGPU::ExecuteOp(u32 op, u32 diff) { memcpy(dst, src, width * bpp); } - CBreakPoints::ExecMemCheck(srcBasePtr + (srcY * srcStride + srcX) * bpp, false, height * srcStride * bpp, currentMIPS->pc); - CBreakPoints::ExecMemCheck(dstBasePtr + (srcY * dstStride + srcX) * bpp, true, height * dstStride * bpp, currentMIPS->pc); + NotifyMemInfo(MemBlockFlags::READ, srcBasePtr + (srcY * srcStride + srcX) * bpp, height * srcStride * bpp, "GPUBlockTransfer"); + NotifyMemInfo(MemBlockFlags::WRITE, dstBasePtr + (dstY * dstStride + dstX) * bpp, height * dstStride * bpp, "GPUBlockTransfer"); // TODO: Correct timing appears to be 1.9, but erring a bit low since some of our other timing is inaccurate. cyclesExecuted += ((height * width * bpp) * 16) / 10; diff --git a/GPU/Software/TransformUnit.cpp b/GPU/Software/TransformUnit.cpp index 501e369e52..65baa6f363 100644 --- a/GPU/Software/TransformUnit.cpp +++ b/GPU/Software/TransformUnit.cpp @@ -18,6 +18,7 @@ #include #include +#include "Common/CPUDetect.h" #include "Common/Math/math_util.h" #include "Common/MemoryUtil.h" #include "Core/Config.h" @@ -256,7 +257,7 @@ VertexData TransformUnit::ReadVertex(VertexReader& vreader) break; case GE_PROJMAP_NORMALIZED_NORMAL: - source = vertex.normal.Normalized(); + source = vertex.normal.NormalizedOr001(cpu_info.bSSE4_1); break; case GE_PROJMAP_NORMAL: @@ -567,8 +568,8 @@ bool TransformUnit::GetCurrentSimpleVertices(int count, std::vector 0 && (gstate.vertType & GE_VTYPE_IDX_MASK) != GE_VTYPE_IDX_NONE) { const u8 *inds = Memory::GetPointer(gstate_c.indexAddr); - const u16 *inds16 = (const u16 *)inds; - const u32 *inds32 = (const u32 *)inds; + const u16_le *inds16 = (const u16_le *)inds; + const u32_le *inds32 = (const u32_le *)inds; if (inds) { GetIndexBounds(inds, count, gstate.vertType, &indexLowerBound, &indexUpperBound); diff --git a/GPU/Vulkan/DebugVisVulkan.cpp b/GPU/Vulkan/DebugVisVulkan.cpp index 9d1365bb5f..83433ecbdf 100644 --- a/GPU/Vulkan/DebugVisVulkan.cpp +++ b/GPU/Vulkan/DebugVisVulkan.cpp @@ -116,7 +116,6 @@ void DrawGPUProfilerVis(UIContext *ui, GPUInterface *gpu) { std::string text = gpuVulkan->GetGpuProfileString(); - Draw::DrawContext *draw = ui->GetDrawContext(); ui->SetFontScale(0.4f, 0.4f); ui->DrawTextShadow(text.c_str(), x, y, 0xFFFFFFFF, FLAG_DYNAMIC_ASCII); ui->SetFontScale(1.0f, 1.0f); diff --git a/GPU/Vulkan/DrawEngineVulkan.cpp b/GPU/Vulkan/DrawEngineVulkan.cpp index 12897153a9..54ebc209ca 100644 --- a/GPU/Vulkan/DrawEngineVulkan.cpp +++ b/GPU/Vulkan/DrawEngineVulkan.cpp @@ -79,7 +79,6 @@ enum { DrawEngineVulkan::DrawEngineVulkan(VulkanContext *vulkan, Draw::DrawContext *draw) : vulkan_(vulkan), draw_(draw), - stats_{}, vai_(1024) { decOptions_.expandAllWeightsToFloat = false; decOptions_.expand8BitNormalsToFloat = false; @@ -866,8 +865,6 @@ void DrawEngineVulkan::DoFlush() { baseUBOOffset, lightUBOOffset, boneUBOOffset, }; - int stride = dec_->GetDecVtxFmt().stride; - if (useElements) { if (!ibuf) { ibOffset = (uint32_t)frame->pushIndex->Push(decIndex, sizeof(uint16_t) * indexGen.VertexCount(), &ibuf); @@ -996,12 +993,10 @@ void DrawEngineVulkan::DoFlush() { VkBuffer vbuf, ibuf; vbOffset = (uint32_t)frame->pushVertex->Push(result.drawBuffer, maxIndex * sizeof(TransformedVertex), &vbuf); ibOffset = (uint32_t)frame->pushIndex->Push(inds, sizeof(short) * result.drawNumTrans, &ibuf); - VkDeviceSize offsets[1] = { vbOffset }; renderManager->DrawIndexed(pipelineLayout_, ds, ARRAY_SIZE(dynamicUBOOffsets), dynamicUBOOffsets, vbuf, vbOffset, ibuf, ibOffset, result.drawNumTrans, 1, VK_INDEX_TYPE_UINT16); } else { VkBuffer vbuf; vbOffset = (uint32_t)frame->pushVertex->Push(result.drawBuffer, result.drawNumTrans * sizeof(TransformedVertex), &vbuf); - VkDeviceSize offsets[1] = { vbOffset }; renderManager->Draw(pipelineLayout_, ds, ARRAY_SIZE(dynamicUBOOffsets), dynamicUBOOffsets, vbuf, vbOffset, result.drawNumTrans); } } else if (result.action == SW_CLEAR) { diff --git a/GPU/Vulkan/DrawEngineVulkan.h b/GPU/Vulkan/DrawEngineVulkan.h index 0e36cbcc16..d72e852cc7 100644 --- a/GPU/Vulkan/DrawEngineVulkan.h +++ b/GPU/Vulkan/DrawEngineVulkan.h @@ -228,7 +228,6 @@ private: PrehashMap vai_; VulkanPushBuffer *vertexCache_; - int decimationCounter_ = 0; int descDecimationCounter_ = 0; struct DescriptorSetKey { @@ -282,7 +281,7 @@ private: // For null texture VkSampler nullSampler_ = VK_NULL_HANDLE; - DrawEngineVulkanStats stats_; + DrawEngineVulkanStats stats_{}; VulkanPipelineRasterStateKey pipelineKey_{}; VulkanDynamicState dynState_{}; diff --git a/GPU/Vulkan/GPU_Vulkan.cpp b/GPU/Vulkan/GPU_Vulkan.cpp index 2858224f05..cb627e1567 100644 --- a/GPU/Vulkan/GPU_Vulkan.cpp +++ b/GPU/Vulkan/GPU_Vulkan.cpp @@ -54,8 +54,8 @@ GPU_Vulkan::GPU_Vulkan(GraphicsContext *gfxCtx, Draw::DrawContext *draw) : GPUCommon(gfxCtx, draw), vulkan_((VulkanContext *)gfxCtx->GetAPIContext()), - drawEngine_(vulkan_, draw), depalShaderCache_(draw, vulkan_), + drawEngine_(vulkan_, draw), vulkan2D_(vulkan_) { CheckGPUFeatures(); diff --git a/GPU/Vulkan/PipelineManagerVulkan.cpp b/GPU/Vulkan/PipelineManagerVulkan.cpp index 8533e10b7d..5c707baa9f 100644 --- a/GPU/Vulkan/PipelineManagerVulkan.cpp +++ b/GPU/Vulkan/PipelineManagerVulkan.cpp @@ -16,7 +16,7 @@ #include "Common/GPU/Vulkan/VulkanRenderManager.h" #include "Common/GPU/Vulkan/VulkanQueueRunner.h" -PipelineManagerVulkan::PipelineManagerVulkan(VulkanContext *vulkan) : vulkan_(vulkan), pipelines_(256) { +PipelineManagerVulkan::PipelineManagerVulkan(VulkanContext *vulkan) : pipelines_(256), vulkan_(vulkan) { // The pipeline cache is created on demand (or explicitly through Load). } @@ -138,6 +138,26 @@ static bool UsesBlendConstant(int factor) { } } +static std::string CutFromMain(std::string str) { + std::vector lines; + SplitString(str, '\n', lines); + + std::string rebuilt; + bool foundStart = false; + int c = 0; + for (const std::string &str : lines) { + if (startsWith(str, "void main")) { + foundStart = true; + rebuilt += StringFromFormat("... (cut %d lines)\n", c); + } + if (foundStart) { + rebuilt += str + "\n"; + } + c++; + } + return rebuilt; +} + static VulkanPipeline *CreateVulkanPipeline(VkDevice device, VkPipelineCache pipelineCache, VkPipelineLayout layout, VkRenderPass renderPass, const VulkanPipelineRasterStateKey &key, const DecVtxFormat *decFmt, VulkanVertexShader *vs, VulkanFragmentShader *fs, bool useHwTransform, float lineWidth) { @@ -246,7 +266,6 @@ static VulkanPipeline *CreateVulkanPipeline(VkDevice device, VkPipelineCache pip inputAssembly.primitiveRestartEnable = false; int vertexStride = 0; - int offset = 0; VkVertexInputAttributeDescription attrs[8]; int attributeCount; if (useHwTransform) { @@ -304,13 +323,15 @@ static VulkanPipeline *CreateVulkanPipeline(VkDevice device, VkPipelineCache pip VkPipeline pipeline; VkResult result = vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipe, nullptr, &pipeline); if (result != VK_SUCCESS) { + ERROR_LOG(G3D, "Failed creating graphics pipeline! result='%s'", VulkanResultToString(result)); if (result == VK_INCOMPLETE) { - // Bad return value seen on Adreno in Burnout :( Try to ignore? - // TODO: Log all the information we can here! + // Typical Adreno return value. It'll usually also log something vague, like "Failed to link shaders". + // Let's log some stuff and try to stumble along. + ERROR_LOG(G3D, "VS source code:\n%s\n", CutFromMain(vs->GetShaderString(SHADER_STRING_SOURCE_CODE)).c_str()); + ERROR_LOG(G3D, "FS source code:\n%s\n", CutFromMain(fs->GetShaderString(SHADER_STRING_SOURCE_CODE)).c_str()); } else { _dbg_assert_msg_(false, "Failed creating graphics pipeline! result='%s'", VulkanResultToString(result)); } - ERROR_LOG(G3D, "Failed creating graphics pipeline! result='%s'", VulkanResultToString(result)); // Create a placeholder to avoid creating over and over if something is broken. VulkanPipeline *nullPipeline = new VulkanPipeline(); nullPipeline->pipeline = VK_NULL_HANDLE; @@ -616,7 +637,6 @@ void PipelineManagerVulkan::SaveCache(FILE *file, bool saveRawPipelineCache, Sha bool failed = false; bool writeFailed = false; - int count = 0; // Since we don't include the full pipeline key, there can be duplicates, // caused by things like switching from buffered to non-buffered rendering. // Make sure the set of pipelines we write is "unique". @@ -724,6 +744,9 @@ bool PipelineManagerVulkan::LoadCache(FILE *file, bool loadRawPipelineCache, Sha if (!pipelineCache_) { VkPipelineCacheCreateInfo pc{ VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO }; VkResult res = vkCreatePipelineCache(vulkan_->GetDevice(), &pc, nullptr, &pipelineCache_); + if (res != VK_SUCCESS) { + return false; + } } } @@ -731,6 +754,7 @@ bool PipelineManagerVulkan::LoadCache(FILE *file, bool loadRawPipelineCache, Sha bool failed = fread(&size, sizeof(size), 1, file) != 1; NOTICE_LOG(G3D, "Creating %d pipelines...", size); + int pipelineCreateFailCount = 0; for (uint32_t i = 0; i < size; i++) { if (failed || cancelCache_) { break; @@ -758,11 +782,14 @@ bool PipelineManagerVulkan::LoadCache(FILE *file, bool loadRawPipelineCache, Sha DecVtxFormat fmt; fmt.InitializeFromID(key.vtxFmtId); - GetOrCreatePipeline(layout, rp, key.raster, + VulkanPipeline *pipeline = GetOrCreatePipeline(layout, rp, key.raster, key.useHWTransform ? &fmt : 0, vs, fs, key.useHWTransform); + if (!pipeline) { + pipelineCreateFailCount += 1; + } } - NOTICE_LOG(G3D, "Recreated Vulkan pipeline cache (%d pipelines).", (int)size); + NOTICE_LOG(G3D, "Recreated Vulkan pipeline cache (%d pipelines, %d failed).", (int)size, pipelineCreateFailCount); return true; } diff --git a/GPU/Vulkan/ShaderManagerVulkan.cpp b/GPU/Vulkan/ShaderManagerVulkan.cpp index 9151e9299b..f225560ad1 100644 --- a/GPU/Vulkan/ShaderManagerVulkan.cpp +++ b/GPU/Vulkan/ShaderManagerVulkan.cpp @@ -43,7 +43,7 @@ #include "GPU/Vulkan/FramebufferManagerVulkan.h" VulkanFragmentShader::VulkanFragmentShader(VulkanContext *vulkan, FShaderID id, const char *code) - : vulkan_(vulkan), id_(id), failed_(false), module_(0) { + : vulkan_(vulkan), id_(id) { PROFILE_THIS_SCOPE("shadercomp"); source_ = code; @@ -101,7 +101,7 @@ std::string VulkanFragmentShader::GetShaderString(DebugShaderStringType type) co } VulkanVertexShader::VulkanVertexShader(VulkanContext *vulkan, VShaderID id, const char *code, bool useHWTransform) - : vulkan_(vulkan), id_(id), failed_(false), useHWTransform_(useHWTransform), module_(VK_NULL_HANDLE) { + : vulkan_(vulkan), useHWTransform_(useHWTransform), id_(id) { PROFILE_THIS_SCOPE("shadercomp"); source_ = code; std::string errorMessage; diff --git a/GPU/Vulkan/ShaderManagerVulkan.h b/GPU/Vulkan/ShaderManagerVulkan.h index c7d036ce21..c9aa297d02 100644 --- a/GPU/Vulkan/ShaderManagerVulkan.h +++ b/GPU/Vulkan/ShaderManagerVulkan.h @@ -47,11 +47,11 @@ public: const FShaderID &GetID() { return id_; } protected: - VkShaderModule module_; + VkShaderModule module_ = VK_NULL_HANDLE; VulkanContext *vulkan_; std::string source_; - bool failed_; + bool failed_ = false; FShaderID id_; }; @@ -70,11 +70,11 @@ public: const VShaderID &GetID() { return id_; } protected: - VkShaderModule module_; + VkShaderModule module_ = VK_NULL_HANDLE; VulkanContext *vulkan_; std::string source_; - bool failed_; + bool failed_ = false; bool useHWTransform_; VShaderID id_; }; diff --git a/GPU/Vulkan/StateMappingVulkan.cpp b/GPU/Vulkan/StateMappingVulkan.cpp index a636531e53..0ae5f0305b 100644 --- a/GPU/Vulkan/StateMappingVulkan.cpp +++ b/GPU/Vulkan/StateMappingVulkan.cpp @@ -253,6 +253,9 @@ void DrawEngineVulkan::ConvertStateToVulkanKey(FramebufferManagerVulkan &fbManag } if (gstate_c.IsDirty(DIRTY_DEPTHSTENCIL_STATE)) { + GenericStencilFuncState stencilState; + ConvertStencilFuncState(stencilState); + if (gstate.isModeClear()) { key.depthTestEnable = true; key.depthCompareOp = VK_COMPARE_OP_ALWAYS; @@ -275,7 +278,7 @@ void DrawEngineVulkan::ConvertStateToVulkanKey(FramebufferManagerVulkan &fbManag // We override this value in the pipeline from software transform for clear rectangles. dynState.stencilRef = 0xFF; // But we still apply the stencil write mask. - dynState.stencilWriteMask = (~gstate.getStencilWriteMask()) & 0xFF; + dynState.stencilWriteMask = stencilState.writeMask; } else { key.stencilTestEnable = false; key.stencilCompareOp = VK_COMPARE_OP_ALWAYS; @@ -299,9 +302,6 @@ void DrawEngineVulkan::ConvertStateToVulkanKey(FramebufferManagerVulkan &fbManag key.depthCompareOp = VK_COMPARE_OP_ALWAYS; } - GenericStencilFuncState stencilState; - ConvertStencilFuncState(stencilState); - // Stencil Test if (stencilState.enabled) { key.stencilTestEnable = true; diff --git a/GPU/Vulkan/TextureCacheVulkan.cpp b/GPU/Vulkan/TextureCacheVulkan.cpp index aa3f0f94c0..eae65ce59f 100644 --- a/GPU/Vulkan/TextureCacheVulkan.cpp +++ b/GPU/Vulkan/TextureCacheVulkan.cpp @@ -297,9 +297,8 @@ std::vector SamplerCache::DebugGetSamplerIDs() const { TextureCacheVulkan::TextureCacheVulkan(Draw::DrawContext *draw, VulkanContext *vulkan) : TextureCacheCommon(draw), vulkan_(vulkan), - samplerCache_(vulkan), - computeShaderManager_(vulkan) { - timesInvalidatedAllThisFrame_ = 0; + computeShaderManager_(vulkan), + samplerCache_(vulkan) { DeviceRestore(vulkan, draw); SetupTextureDecoder(); } @@ -394,6 +393,7 @@ void TextureCacheVulkan::CompileScalingShader() { if (copyCS_ != VK_NULL_HANDLE) vulkan_->Delete().QueueDeleteShaderModule(copyCS_); textureShader_.clear(); + maxScaleFactor_ = 255; } else if (uploadCS_ || copyCS_) { // No need to recreate. return; @@ -417,6 +417,7 @@ void TextureCacheVulkan::CompileScalingShader() { _dbg_assert_msg_(copyCS_ != VK_NULL_HANDLE, "failed to compile copy shader"); textureShader_ = g_Config.sTextureShaderName; + maxScaleFactor_ = shaderInfo->maxScale; } void TextureCacheVulkan::ReleaseTexture(TexCacheEntry *entry, bool delete_them) { @@ -518,7 +519,7 @@ void TextureCacheVulkan::BindTexture(TexCacheEntry *entry) { entry->vkTex->Touch(); imageView_ = entry->vkTex->GetImageView(); int maxLevel = (entry->status & TexCacheEntry::STATUS_BAD_MIPS) ? 0 : entry->maxLevel; - SamplerCacheKey samplerKey = GetSamplingParams(maxLevel, entry->addr); + SamplerCacheKey samplerKey = GetSamplingParams(maxLevel, entry); curSampler_ = samplerCache_.GetOrCreateSampler(samplerKey); drawEngine_->SetDepalTexture(VK_NULL_HANDLE); gstate_c.SetUseShaderDepal(false); @@ -765,6 +766,8 @@ void TextureCacheVulkan::BuildTexture(TexCacheEntry *const entry) { VkFormat dstFmt = GetDestFormat(GETextureFormat(entry->format), gstate.getClutPaletteFormat()); int scaleFactor = standardScaleFactor_; + if (scaleFactor > maxScaleFactor_) + scaleFactor = maxScaleFactor_; // Rachet down scale factor in low-memory mode. if (lowMemoryMode_) { @@ -864,7 +867,7 @@ void TextureCacheVulkan::BuildTexture(TexCacheEntry *const entry) { } char texName[128]{}; - snprintf(texName, sizeof(texName), "texture_%08x_%s", entry->addr, GeTextureFormatToString((GETextureFormat)entry->format)); + snprintf(texName, sizeof(texName), "texture_%08x_%s", entry->addr, GeTextureFormatToString((GETextureFormat)entry->format, gstate.getClutPaletteFormat())); image->SetTag(texName); bool allocSuccess = image->CreateDirect(cmdInit, allocator_, w * scaleFactor, h * scaleFactor, maxLevelToGenerate + 1, actualFmt, imageLayout, usage, mapping); @@ -901,7 +904,7 @@ void TextureCacheVulkan::BuildTexture(TexCacheEntry *const entry) { replacedInfo.cachekey = cachekey; replacedInfo.hash = entry->fullhash; replacedInfo.addr = entry->addr; - replacedInfo.isVideo = videos_.find(entry->addr & 0x3FFFFFFF) != videos_.end(); + replacedInfo.isVideo = IsVideo(entry->addr); replacedInfo.isFinal = (entry->status & TexCacheEntry::STATUS_TO_SCALE) == 0; replacedInfo.scaleFactor = scaleFactor; replacedInfo.fmt = FromVulkanFormat(actualFmt); @@ -931,6 +934,7 @@ void TextureCacheVulkan::BuildTexture(TexCacheEntry *const entry) { void *data; bool dataScaled = true; if (replaced.Valid()) { + // Directly load the replaced image. data = drawEngine_->GetPushBufferForTextureData()->PushAligned(size, &bufferOffset, &texBuf, pushAlignment); replaced.Load(i, data, stride); entry->vkTex->UploadMip(cmdInit, i, mipWidth, mipHeight, texBuf, bufferOffset, stride / bpp); @@ -1011,7 +1015,7 @@ void TextureCacheVulkan::BuildTexture(TexCacheEntry *const entry) { // Generate any additional mipmap levels. for (int level = maxLevel + 1; level <= maxLevelToGenerate; level++) { - entry->vkTex->GenerateMip(cmdInit, level); + entry->vkTex->GenerateMip(cmdInit, level, computeUpload ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); } if (maxLevel == 0) { @@ -1073,7 +1077,6 @@ TexCacheEntry::TexStatus TextureCacheVulkan::CheckAlpha(const u32 *pixelData, Vk } void TextureCacheVulkan::LoadTextureLevel(TexCacheEntry &entry, uint8_t *writePtr, int rowPitch, int level, int scaleFactor, VkFormat dstFmt) { - VulkanTexture *tex = entry.vkTex; int w = gstate.getTextureWidth(level); int h = gstate.getTextureHeight(level); diff --git a/GPU/Vulkan/TextureCacheVulkan.h b/GPU/Vulkan/TextureCacheVulkan.h index 254efcd40b..364f5e202d 100644 --- a/GPU/Vulkan/TextureCacheVulkan.h +++ b/GPU/Vulkan/TextureCacheVulkan.h @@ -128,10 +128,6 @@ private: TextureScalerVulkan scaler; - int decimationCounter_ = 0; - int texelsScaledThisFrame_ = 0; - int timesInvalidatedAllThisFrame_ = 0; - FramebufferManagerVulkan *framebufferManagerVulkan_; DepalShaderCacheVulkan *depalShaderCache_; ShaderManagerVulkan *shaderManagerVulkan_; @@ -139,6 +135,7 @@ private: Vulkan2D *vulkan2D_; std::string textureShader_; + int maxScaleFactor_ = 255; VkShaderModule uploadCS_ = VK_NULL_HANDLE; VkShaderModule copyCS_ = VK_NULL_HANDLE; diff --git a/GPU/Vulkan/VulkanUtil.cpp b/GPU/Vulkan/VulkanUtil.cpp index fa2deab322..3f4e889e9b 100644 --- a/GPU/Vulkan/VulkanUtil.cpp +++ b/GPU/Vulkan/VulkanUtil.cpp @@ -47,7 +47,6 @@ void Vulkan2D::DestroyDeviceObjects() { } keptPipelines_.clear(); - VkDevice device = vulkan_->GetDevice(); if (descriptorSetLayout_ != VK_NULL_HANDLE) { vulkan_->Delete().QueueDeleteDescriptorSetLayout(descriptorSetLayout_); } diff --git a/GPU/ge_constants.h b/GPU/ge_constants.h index 92c06c3f1d..954934dcab 100644 --- a/GPU/ge_constants.h +++ b/GPU/ge_constants.h @@ -419,7 +419,6 @@ enum GETextureFormat GE_TFMT_DXT5 = 10, }; -const char *GeTextureFormatToString(GETextureFormat tfmt); inline bool IsClutFormat(GETextureFormat tfmt) { return tfmt == GE_TFMT_CLUT4 || tfmt == GE_TFMT_CLUT8 || tfmt == GE_TFMT_CLUT16 || tfmt == GE_TFMT_CLUT32; } @@ -609,3 +608,7 @@ enum GEPaletteFormat GE_CMODE_16BIT_ABGR4444, GE_CMODE_32BIT_ABGR8888, }; + +const char *GEPaletteFormatToString(GEPaletteFormat pfmt); +const char *GeTextureFormatToString(GETextureFormat tfmt); +const char *GeTextureFormatToString(GETextureFormat tfmt, GEPaletteFormat pfmt); diff --git a/Qt/QtMain.cpp b/Qt/QtMain.cpp index 5be0cefa01..70aa0100d4 100644 --- a/Qt/QtMain.cpp +++ b/Qt/QtMain.cpp @@ -35,6 +35,7 @@ #include "Common/System/System.h" #include "Common/GPU/OpenGL/GLFeatures.h" #include "Common/Math/math_util.h" +#include "Common/Profiler/Profiler.h" #include "QtMain.h" #include "Common/Data/Text/I18n.h" @@ -224,6 +225,8 @@ bool System_GetPropertyBool(SystemProperty prop) { #else return false; #endif + case SYSPROP_CAN_JIT: + return true; default: return false; } @@ -689,6 +692,7 @@ int main(int argc, char *argv[]) } } + PROFILE_INIT(); glslang::InitializeProcess(); #if defined(Q_OS_LINUX) QApplication::setAttribute(Qt::AA_X11InitThreads, true); diff --git a/Qt/mainwindow.cpp b/Qt/mainwindow.cpp index 38258740ce..964a61880d 100644 --- a/Qt/mainwindow.cpp +++ b/Qt/mainwindow.cpp @@ -1,4 +1,5 @@ // Qt Desktop UI: works on Linux, Windows and Mac OSX +#include "ppsspp_config.h" #include "mainwindow.h" #include diff --git a/Qt/mainwindow.h b/Qt/mainwindow.h index cdc08ca196..2a9660d744 100644 --- a/Qt/mainwindow.h +++ b/Qt/mainwindow.h @@ -184,7 +184,11 @@ private slots: void cheatsAct() { g_Config.bEnableCheats = !g_Config.bEnableCheats; } // Chat - void chatAct() { NativeMessageReceived("chat screen", ""); } + void chatAct() { + g_Config.bEnableNetworkChat = true; + updateMenus(); + NativeMessageReceived("chat screen", ""); + } void fullscrAct(); void raiseTopMost(); diff --git a/README.md b/README.md index bc8d4b1b9c..bf9f2dce93 100644 --- a/README.md +++ b/README.md @@ -30,6 +30,55 @@ If you want to download regularly updated builds for Android, Windows x86 and x6 For game compatibility, see [community compatibility feedback](https://report.ppsspp.org/games). + +What's new in 1.11.2 +==================== +* An additional few crash fixes ([#14129], [#14134], [#14132]) + +What's new in 1.11.1 +==================== +* A few crash fixes ([#14085], [#14089], [#14091], [#14092]), a few adhoc fixes +* Glitchy menu audio on some devices ([#14101]), in-game UI font memory leak ([#14078]) +* Couple of adhoc fixes ([#14106], [#14117]) + +What's new in 1.11.0 +==================== +* Lots of minor bug fixes, crash fixes, and performance fixes and improvements. +* New Browse... button to allow opening SD cards on Android 11 +* Countless AdHoc networking fixes by ANR2ME, for example Dragon Ball Shin Budokai, PowerStone, + Bleach Heat The Soul 7, Kingdom Hearts, GTA: VCS and many more. +* Graphics issue with car reflections fixed in Outrun, Dirt 2 ([#13636], [#13640], [#13760]) +* Cut-off cards in Yu Gi Oh fixed ([#7124]). +* Numerous fixes to the builtin fonts by nassau-tk +* Added exception handler so PPSSPP stays alive if a game crashes ([#11795]/[#13092]) +* Desktop: Support for multiple instance multiplayer ([#13172], ...) +* Workaround for rendering bugs with flat shading in iOS 14 +* Multiple fixes to the IR interpreter ([#13897], ...) +* UI: New fullscreen button on desktop platforms, optional navigation sounds ([#13239]) +* Audio and multiple hangs fixes in UWP version ([#13792], ...) +* Partial microphone support ([#12336], ...) +* Workaround for wacky action mirroring bug in Hitman Reborn Battle Arena 2 ([#13706], [#13526]) +* Hardware texture upscaling for Vulkan, mipmap generation ([#13235], [#13514]) +* Added MMPX Vulkan texture upscaling shader ([#13986]) +* Depth texturing support in Vulkan and D3D11 ([#13262], [#13556], ...) +* Performance fix for Test Drive Unlimited ([#13355], ...) +* Allow rewind on mobile ([#13866]) +* Added option to disable on-screen messages ([#13695]) +* Added "Lower resolution for effects" on libretro ([#13654]) +* Allow chaining multiple post-processing shaders ([#12924]) +* Support for loading game-specific plugins ([#13335]) +* Fixed Assassin's Creed: Bloodlines Save issue on Android ([#12761]) +* Hanayaka Nari Wa ga Ichizoku: mono voices fixed ([#5213]) +* Additional fixed games: + * Namco Museum - Battle Collection, Vol 2 ([#9523], [#13297], [#13298]) + * Dream Club Portable (graphics bugs, GL and Vulkan) ([#6025]) + * Capcom Classic Collection Reloaded (stuck in return game) ([#4671]) + * Xyanide Resurrection (freezing) ([#8526]) + * Dissidia Final Fantasy Chinese (patched game, invalid address) ([#13204]) + * Crazy Taxi ([#13368]) + * Spiderman: Friend or Foe ([#13969]) + * Downstream Panic (US) (New Game crash) ([#13633]) + What's new in 1.10.3 -------------------- * Fix for control layout editor ([#13125]) @@ -336,3 +385,51 @@ What's new in 1.7.0 [#10909]: https://github.com/hrydgard/ppsspp/issues/10909 "WebSocket based debugger interface" [#11447]: https://github.com/hrydgard/ppsspp/issues/11447 "Avoid calling any GL calls during shutdown on Android. Should help #11063" [#11350]: https://github.com/hrydgard/ppsspp/issues/11350 "TexCache: Optimize DXT3/DXT5 decode to single pass" +[#13636]: https://github.com/hrydgard/ppsspp/issues/13636 "Reinterpret framebuffer formats as needed. Outrun reflections partial fix" +[#13640]: https://github.com/hrydgard/ppsspp/issues/13640 "Fix car reflections in Outrun" +[#13760]: https://github.com/hrydgard/ppsspp/issues/13760 "Fix car lighting issues in DiRT 2." +[#7124]: https://github.com/hrydgard/ppsspp/issues/7124 "Yu-Gi-Oh! GX Tag Force Card summoning (card cut-off / cropped)" +[#11795]: https://github.com/hrydgard/ppsspp/issues/11795 "Exception handler - catch bad memory accesses" +[#13092]: https://github.com/hrydgard/ppsspp/issues/13092 "Bad memory access handling improvements" +[#13172]: https://github.com/hrydgard/ppsspp/issues/13172 "Generalized multi-instance" +[#13897]: https://github.com/hrydgard/ppsspp/issues/13897 "LittleBigPlanet - Game Not Loading, Blue Screen (iOS, Unplayable)" +[#13239]: https://github.com/hrydgard/ppsspp/issues/13239 "Add sound effects for PPSSPP interface navigation" +[#13792]: https://github.com/hrydgard/ppsspp/issues/13792 "Fix UWP audio and a hang bug" +[#12336]: https://github.com/hrydgard/ppsspp/issues/12336 "Microphone support" +[#13706]: https://github.com/hrydgard/ppsspp/issues/13706 "Add back the old implementation of vfpu_sin/cos/sincos." +[#13526]: https://github.com/hrydgard/ppsspp/issues/13526 "VFPU: Compute sines and cosines in double precision." +[#13235]: https://github.com/hrydgard/ppsspp/issues/13235 "Vulkan: Allow custom texture upscaling shaders" +[#13514]: https://github.com/hrydgard/ppsspp/issues/13514 "Vulkan: Automatically generate mipmaps for replaced/scaled textures" +[#13986]: https://github.com/hrydgard/ppsspp/issues/13986 "Vulkan: Add MMPX upscaling texture shader" +[#13355]: https://github.com/hrydgard/ppsspp/issues/13355 "Refactor framebuffer attachment. Fixes Test Drive Unlimited performance" +[#13866]: https://github.com/hrydgard/ppsspp/issues/13866 "SaveState: Allow rewind on mobile" +[#13695]: https://github.com/hrydgard/ppsspp/issues/13695 "Add developer setting \"Show on-screen messages\". Uncheck to hide them." +[#13654]: https://github.com/hrydgard/ppsspp/issues/13654 "Expose the \"Lower resolution for effects\" setting in libretro." +[#12924]: https://github.com/hrydgard/ppsspp/issues/12924 "Postprocessing: User chain support" +[#13335]: https://github.com/hrydgard/ppsspp/issues/13335 "Support for loading game-specific plugins" +[#12761]: https://github.com/hrydgard/ppsspp/issues/12761 "[Android][OpenGL&Vulkan][Save issue] Assassin's Creed : Bloodlines (ULJM05571)" +[#5213]: https://github.com/hrydgard/ppsspp/issues/5213 "Hanayaka Nari Wa ga Ichizoku strange MP3 mono voice" +[#9523]: https://github.com/hrydgard/ppsspp/issues/9523 "Namco Museum - Battle Collection - ULUS100035 loading problem" +[#13297]: https://github.com/hrydgard/ppsspp/issues/13297 "Namco Museum Vol. 2 - ULJS00047 infinite loading in some game" +[#13298]: https://github.com/hrydgard/ppsspp/issues/13298 "Fix sceKernelExitThread" +[#6025]: https://github.com/hrydgard/ppsspp/issues/6025 "Dream Club Portable crash after select girl" +[#4671]: https://github.com/hrydgard/ppsspp/issues/4671 "Capcom Classic Collection Reloaded stuck in return game" +[#8526]: https://github.com/hrydgard/ppsspp/issues/8526 "Xyanide Resurrection freezing" +[#13204]: https://github.com/hrydgard/ppsspp/issues/13204 "Dissidia Final Fantasy Chinese patch invalid address" +[#13368]: https://github.com/hrydgard/ppsspp/issues/13368 "Reschedule after resuming thread from suspend." +[#13969]: https://github.com/hrydgard/ppsspp/issues/13969 "Io: Don't allow async close while async busy" +[#13633]: https://github.com/hrydgard/ppsspp/issues/13633 "Downstream Panic (US) New Game crashes" +[#13667]: https://github.com/hrydgard/ppsspp/issues/13667 "Dynasty Warriors Multi Raid 2: Online questions" +[#13262]: https://github.com/hrydgard/ppsspp/issues/13262 "Implement texturing from depth buffers (Vulkan only so far)" +[#13556]: https://github.com/hrydgard/ppsspp/issues/13556 "D3D11 depth texture support" +[#14085]: https://github.com/hrydgard/ppsspp/issues/14085 "Handle exec addr errors better - don't let IgnoreBadMemoryAccesses skip dispatcher exceptions" +[#14089]: https://github.com/hrydgard/ppsspp/issues/14089 "GL: Call CreateDeviceObjects *after* updating render_." +[#14091]: https://github.com/hrydgard/ppsspp/issues/14091 "Only allow sceMpegGetAvcAu warmup for God Eater Series" +[#14092]: https://github.com/hrydgard/ppsspp/issues/14092 "SaveState: Prevent crash on bad cookie marker" +[#14101]: https://github.com/hrydgard/ppsspp/issues/14101 "Menu audio glitchfix" +[#14078]: https://github.com/hrydgard/ppsspp/issues/14078 "PPGe: Decimate text images properly" +[#14106]: https://github.com/hrydgard/ppsspp/issues/14106 "[Adhoc] Fix frozen (0 FPS) issue on Kao Challengers and Asterix & Obelix XX" +[#14117]: https://github.com/hrydgard/ppsspp/issues/14117 "[Adhoc] Fix lob" +[#14129]: https://github.com/hrydgard/ppsspp/issues/14129 "GPU: Force reinterpret off without copy image" +[#14134]: https://github.com/hrydgard/ppsspp/issues/14134 "Android: Ensure shutdown waits for render" +[#14132]: https://github.com/hrydgard/ppsspp/issues/14132 "Io: Truncate reads/writes to valid memory" diff --git a/SDL/SDLCocoaMetalLayer.mm b/SDL/SDLCocoaMetalLayer.mm index c45158ff5d..cb7bd55192 100644 --- a/SDL/SDLCocoaMetalLayer.mm +++ b/SDL/SDLCocoaMetalLayer.mm @@ -1,4 +1,5 @@ -#if defined(PPSSPP_PLATFORM_MAC) +#include "ppsspp_config.h" +#if PPSSPP_PLATFORM(MAC) #import #else #import @@ -9,7 +10,7 @@ void *makeWindowMetalCompatible(void *window) { // https://github.com/KhronosGroup/MoltenVK/issues/78#issuecomment-371118536 -#if defined(PPSSPP_PLATFORM_MAC) +#if PPSSPP_PLATFORM(MAC) NSView *view = ((NSWindow *)window).contentView; assert([view isKindOfClass:[NSView class]]); diff --git a/SDL/SDLMain.cpp b/SDL/SDLMain.cpp index 98b56fe912..bfc6de059d 100644 --- a/SDL/SDLMain.cpp +++ b/SDL/SDLMain.cpp @@ -29,6 +29,7 @@ SDLJoystick *joystick = NULL; #include "NKCodeFromSDL.h" #include "Common/Math/math_util.h" #include "Common/GPU/OpenGL/GLRenderManager.h" +#include "Common/Profiler/Profiler.h" #include "SDL_syswm.h" @@ -386,6 +387,8 @@ bool System_GetPropertyBool(SystemProperty prop) { #else return false; #endif + case SYSPROP_CAN_JIT: + return true; default: return false; } @@ -454,9 +457,9 @@ static void EmuThreadFunc(GraphicsContext *graphicsContext) { UpdateRunLoop(); } emuThreadState = (int)EmuThreadState::STOPPED; + graphicsContext->StopThread(); NativeShutdownGraphics(); - graphicsContext->StopThread(); } static void EmuThreadStart(GraphicsContext *context) { @@ -489,6 +492,7 @@ int main(int argc, char *argv[]) { nxlinkStdio(); #endif // HAVE_LIBNX + PROFILE_INIT(); glslang::InitializeProcess(); #if PPSSPP_PLATFORM(RPI) diff --git a/SDL/SDLMain.mm b/SDL/SDLMain.mm index 748dcac0bf..7819ad0c09 100644 --- a/SDL/SDLMain.mm +++ b/SDL/SDLMain.mm @@ -7,6 +7,7 @@ #include "SDL.h" #include "SDLMain.h" +#include "Common/Profiler/Profiler.h" #include /* for MAXPATHLEN */ #include @@ -371,6 +372,8 @@ int main (int argc, char **argv) gFinderLaunch = NO; } + PROFILE_INIT(); + #if SDL_USE_NIB_FILE NSApplicationMain (argc, argv); #else diff --git a/SDL/SDLVulkanGraphicsContext.cpp b/SDL/SDLVulkanGraphicsContext.cpp index d8cf460bfc..433f35b03f 100644 --- a/SDL/SDLVulkanGraphicsContext.cpp +++ b/SDL/SDLVulkanGraphicsContext.cpp @@ -1,3 +1,4 @@ +#include "ppsspp_config.h" #include "Core/Config.h" #include "Core/ConfigValues.h" #include "Common/System/System.h" @@ -93,7 +94,7 @@ bool SDLVulkanGraphicsContext::Init(SDL_Window *&window, int x, int y, int mode, break; #endif #if defined(VK_USE_PLATFORM_METAL_EXT) -#if defined(PPSSPP_PLATFORM_MAC) +#if PPSSPP_PLATFORM(MAC) case SDL_SYSWM_COCOA: vulkan_->InitSurface(WINDOWSYSTEM_METAL_EXT, makeWindowMetalCompatible(sys_info.info.cocoa.window), nullptr); break; diff --git a/UI/BackgroundAudio.cpp b/UI/BackgroundAudio.cpp index ba03df8ca2..809993d5b4 100644 --- a/UI/BackgroundAudio.cpp +++ b/UI/BackgroundAudio.cpp @@ -248,6 +248,7 @@ BackgroundAudio g_BackgroundAudio; BackgroundAudio::BackgroundAudio() { buffer = new int[BUFSIZE](); + sndLoadPending_.store(false); } BackgroundAudio::~BackgroundAudio() { @@ -308,21 +309,24 @@ void BackgroundAudio::Clear(bool hard) { at3Reader_ = nullptr; } playbackOffset_ = 0; + sndLoadPending_ = false; } void BackgroundAudio::SetGame(const std::string &path) { - std::lock_guard lock(mutex_); if (path == bgGamePath_) { // Do nothing return; } - if (path.size() == 0) { + std::lock_guard lock(mutex_); + if (path.empty()) { Clear(false); + sndLoadPending_ = false; fadingOut_ = true; } else { Clear(true); gameLastChanged_ = time_now_d(); + sndLoadPending_ = true; fadingOut_ = false; } volume_ = 1.0f; @@ -343,14 +347,12 @@ int BackgroundAudio::Play() { int sz = lastPlaybackTime_ <= 0.0 ? 44100 / 60 : (int)((now - lastPlaybackTime_) * 44100); sz = std::min(BUFSIZE / 2, sz); if (at3Reader_) { - if (sz >= 16) { - if (at3Reader_->Read(buffer, sz)) { - if (fadingOut_) { - for (int i = 0; i < sz*2; i += 2) { - buffer[i] *= volume_; - buffer[i + 1] *= volume_; - volume_ += delta_; - } + if (at3Reader_->Read(buffer, sz)) { + if (fadingOut_) { + for (int i = 0; i < sz*2; i += 2) { + buffer[i] *= volume_; + buffer[i + 1] *= volume_; + volume_ += delta_; } } } @@ -399,27 +401,24 @@ int BackgroundAudio::Play() { void BackgroundAudio::Update() { // If there's a game, and some time has passed since the selected game // last changed... (to prevent crazy amount of reads when skipping through a list) - if (bgGamePath_.size() && (time_now_d() - gameLastChanged_ > 0.5)) { + if (sndLoadPending_ && (time_now_d() - gameLastChanged_ > 0.5)) { std::lock_guard lock(mutex_); - if (!at3Reader_) { - // Grab some audio from the current game and play it. - if (!g_gameInfoCache) - return; + // Already loaded somehow? Or no game info cache? + if (at3Reader_ || !g_gameInfoCache) + return; - std::shared_ptr gameInfo = g_gameInfoCache->GetInfo(NULL, bgGamePath_, GAMEINFO_WANTSND); - if (!gameInfo) - return; - - if (gameInfo->pending) { - // Should try again shortly.. - return; - } - - if (gameInfo->sndFileData.size()) { - const std::string &data = gameInfo->sndFileData; - at3Reader_ = new AT3PlusReader(data); - lastPlaybackTime_ = 0.0; - } + // Grab some audio from the current game and play it. + std::shared_ptr gameInfo = g_gameInfoCache->GetInfo(nullptr, bgGamePath_, GAMEINFO_WANTSND); + if (!gameInfo || gameInfo->pending) { + // Should try again shortly.. + return; } + + const std::string &data = gameInfo->sndFileData; + if (!data.empty()) { + at3Reader_ = new AT3PlusReader(data); + lastPlaybackTime_ = 0.0; + } + sndLoadPending_ = false; } } diff --git a/UI/BackgroundAudio.h b/UI/BackgroundAudio.h index fb572f52a7..d8c1ba18e6 100644 --- a/UI/BackgroundAudio.h +++ b/UI/BackgroundAudio.h @@ -1,7 +1,8 @@ #pragma once -#include +#include #include +#include #include #include "Common/UI/Root.h" @@ -13,7 +14,6 @@ public: BackgroundAudio(); ~BackgroundAudio(); - void Clear(bool hard); void SetGame(const std::string &path); void Update(); int Play(); @@ -22,12 +22,15 @@ public: void PlaySFX(UI::UISound sfx); private: + void Clear(bool hard); + enum { BUFSIZE = 44100, }; std::mutex mutex_; std::string bgGamePath_; + std::atomic sndLoadPending_; int playbackOffset_ = 0; AT3PlusReader *at3Reader_; double gameLastChanged_ = 0.0; diff --git a/UI/ChatScreen.cpp b/UI/ChatScreen.cpp index c09d92708a..b21d64d27e 100644 --- a/UI/ChatScreen.cpp +++ b/UI/ChatScreen.cpp @@ -22,7 +22,7 @@ void ChatMenu::CreatePopupContents(UI::ViewGroup *parent) { scroll_ = outer->Add(new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(1.0))); LinearLayout *bottom = outer->Add(new LinearLayout(ORIENT_HORIZONTAL, new LayoutParams(FILL_PARENT, WRAP_CONTENT))); #if PPSSPP_PLATFORM(WINDOWS) || defined(USING_QT_UI) || defined(SDL) - chatEdit_ = bottom->Add(new TextEdit("", n->T("Chat Here"), new LinearLayoutParams(1.0))); + chatEdit_ = bottom->Add(new TextEdit("", n->T("Chat message"), n->T("Chat Here"), new LinearLayoutParams(1.0))); #if defined(USING_WIN_UI) // freeze the ui when using ctrl + C hotkey need workaround if (g_Config.bBypassOSKWithKeyboard && !g_Config.bFullScreen) { diff --git a/UI/ComboKeyMappingScreen.cpp b/UI/ComboKeyMappingScreen.cpp index d5742c575a..f1582ef9aa 100644 --- a/UI/ComboKeyMappingScreen.cpp +++ b/UI/ComboKeyMappingScreen.cpp @@ -52,7 +52,7 @@ void ComboKeyScreen::CreateViews() { for (int i = 0; i < 5; i++) { comboselect->AddChoice(comboKeyImages[i]); } - comboselect->SetSelection(*mode); + comboselect->SetSelection(*mode, false); comboselect->OnChoice.Handle(this, &ComboKeyScreen::onCombo); leftColumn->Add(comboselect); root__->Add(leftColumn); diff --git a/UI/ControlMappingScreen.cpp b/UI/ControlMappingScreen.cpp index 0859c52f63..2fc9fba4d6 100644 --- a/UI/ControlMappingScreen.cpp +++ b/UI/ControlMappingScreen.cpp @@ -15,6 +15,7 @@ // Official git repository and contact information can be found at // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. +#include "ppsspp_config.h" #include #include #include @@ -109,8 +110,6 @@ void ControlMapper::Refresh() { LinearLayout *root = Add(new LinearLayout(ORIENT_HORIZONTAL, new LinearLayoutParams(550, WRAP_CONTENT))); root->SetSpacing(3.0f); - const int padding = 4; - auto iter = keyImages.find(keyName_); // First, look among images. if (iter != keyImages.end()) { @@ -138,7 +137,6 @@ void ControlMapper::Refresh() { for (size_t i = 0; i < mappings.size(); i++) { std::string deviceName = GetDeviceName(mappings[i].deviceId); std::string keyName = KeyMap::GetKeyOrAxisName(mappings[i].keyCode); - int image = -1; LinearLayout *row = rightColumn->Add(new LinearLayout(ORIENT_HORIZONTAL, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT))); row->SetSpacing(1.0f); @@ -246,7 +244,7 @@ void ControlMappingScreen::CreateViews() { rightScroll_ = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(1.0f)); rightScroll_->SetTag("ControlMapping"); rightScroll_->SetScrollToTop(false); - LinearLayout *rightColumn = new LinearLayout(ORIENT_VERTICAL, new LinearLayoutParams(1.0f)); + LinearLayout *rightColumn = new LinearLayoutList(ORIENT_VERTICAL, new LinearLayoutParams(1.0f)); rightScroll_->Add(rightColumn); root_->Add(leftColumn); @@ -437,6 +435,7 @@ public: xAxis_(xAxis), xDir_(xDir), yAxis_(yAxis), yDir_(yDir) {} void Draw(UIContext &dc) override; + std::string DescribeText() const override { return ""; } void Update() override; void Axis(const AxisInput &input) override { // TODO: Check input.deviceId? diff --git a/UI/CwCheatScreen.cpp b/UI/CwCheatScreen.cpp index 99a3b412d9..4f3bdb79d4 100644 --- a/UI/CwCheatScreen.cpp +++ b/UI/CwCheatScreen.cpp @@ -15,6 +15,7 @@ // Official git repository and contact information can be found at // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. +#include "ppsspp_config.h" #include "ext/xxhash.h" #include "Common/UI/UI.h" @@ -97,7 +98,7 @@ void CwCheatScreen::CreateViews() { rightScroll_->SetTag("CwCheats"); rightScroll_->SetScrollToTop(false); rightScroll_->ScrollTo(g_Config.fCwCheatScrollPosition); - LinearLayout *rightColumn = new LinearLayout(ORIENT_VERTICAL, new LinearLayoutParams(200, FILL_PARENT, actionMenuMargins)); + LinearLayout *rightColumn = new LinearLayoutList(ORIENT_VERTICAL, new LinearLayoutParams(200, FILL_PARENT, actionMenuMargins)); rightScroll_->Add(rightColumn); rightColumn->Add(new ItemHeader(cw->T("Cheats"))); @@ -182,7 +183,7 @@ UI::EventReturn CwCheatScreen::OnEditCheatFile(UI::EventParams ¶ms) { } void CwCheatScreen::ParseCheatDB(std::string line, std::vector title, std::vector newList, std::string cheatFile) { - bool finished = false, skip = false; + bool finished = false; std::string gameID = StringFromFormat("_S %s-%s", gameID_.substr(0, 4).c_str(), gameID_.substr(4).c_str()); std::fstream fs; diff --git a/UI/DevScreens.cpp b/UI/DevScreens.cpp index 5145909f51..96740b5789 100644 --- a/UI/DevScreens.cpp +++ b/UI/DevScreens.cpp @@ -24,6 +24,7 @@ #include "Common/System/System.h" #include "Common/GPU/OpenGL/GLFeatures.h" #include "Common/Data/Text/I18n.h" +#include "Common/Net/HTTPClient.h" #include "Common/UI/Context.h" #include "Common/UI/View.h" #include "Common/UI/ViewGroup.h" @@ -48,6 +49,7 @@ #include "GPU/GPUState.h" #include "UI/MiscScreens.h" #include "UI/DevScreens.h" +#include "UI/MainScreen.h" #include "UI/ControlMappingScreen.h" #include "UI/GameSettingsScreen.h" @@ -57,6 +59,12 @@ int GetD3DCompilerVersion(); #endif +#if PPSSPP_PLATFORM(ANDROID) + +#include "android/jni/app-android.h" + +#endif + static const char *logLevelList[] = { "Notice", "Error", @@ -205,7 +213,7 @@ void LogScreen::CreateViews() { scroll_ = outer->Add(new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(1.0))); LinearLayout *bottom = outer->Add(new LinearLayout(ORIENT_HORIZONTAL, new LayoutParams(FILL_PARENT, WRAP_CONTENT))); bottom->Add(new Button(di->T("Back")))->OnClick.Handle(this, &UIScreen::OnBack); - cmdLine_ = bottom->Add(new TextEdit("", "Command Line", new LinearLayoutParams(1.0))); + cmdLine_ = bottom->Add(new TextEdit("", "Command", "Command Line", new LinearLayoutParams(1.0))); cmdLine_->OnEnter.Handle(this, &LogScreen::OnSubmit); bottom->Add(new Button(di->T("Submit")))->OnClick.Handle(this, &LogScreen::OnSubmit); @@ -256,7 +264,7 @@ void LogConfigScreen::CreateViews() { UI::GridLayoutSettings gridsettings(cellSize, 64, 5); gridsettings.fillCells = true; - GridLayout *grid = vert->Add(new GridLayout(gridsettings, new LayoutParams(FILL_PARENT, WRAP_CONTENT))); + GridLayout *grid = vert->Add(new GridLayoutList(gridsettings, new LayoutParams(FILL_PARENT, WRAP_CONTENT))); for (int i = 0; i < LogManager::GetNumChannels(); i++) { LogTypes::LOG_TYPE type = (LogTypes::LOG_TYPE)i; @@ -441,7 +449,7 @@ void SystemInfoScreen::CreateViews() { root_->Add(tabHolder); ViewGroup *deviceSpecsScroll = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, FILL_PARENT)); deviceSpecsScroll->SetTag("DevSystemInfoDeviceSpecs"); - LinearLayout *deviceSpecs = new LinearLayout(ORIENT_VERTICAL); + LinearLayout *deviceSpecs = new LinearLayoutList(ORIENT_VERTICAL); deviceSpecs->SetSpacing(0); deviceSpecsScroll->Add(deviceSpecs); tabHolder->AddTab(si->T("Device Info"), deviceSpecsScroll); @@ -470,6 +478,9 @@ void SystemInfoScreen::CreateViews() { 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)); +#endif +#if PPSSPP_PLATFORM(IOS) + deviceSpecs->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"))); @@ -569,9 +580,30 @@ void SystemInfoScreen::CreateViews() { deviceSpecs->Add(new InfoItem("Moga", moga)); #endif + ViewGroup *storageScroll = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, FILL_PARENT)); + storageScroll->SetTag("DevSystemInfoBuildConfig"); + LinearLayout *storage = new LinearLayout(ORIENT_VERTICAL); + storage->SetSpacing(0); + storageScroll->Add(storage); + tabHolder->AddTab(si->T("Storage"), storageScroll); + + storage->Add(new ItemHeader(si->T("Directories"))); + // Intentionally non-translated + storage->Add(new InfoItem("MemStickDirectory", g_Config.memStickDirectory)); + storage->Add(new InfoItem("InternalDataDirectory", g_Config.internalDataDirectory)); + storage->Add(new InfoItem("AppCacheDir", g_Config.appCacheDirectory)); + storage->Add(new InfoItem("ExtStorageDir", g_Config.externalDirectory)); + +#if PPSSPP_PLATFORM(ANDROID) + storage->Add(new InfoItem("ExtFilesDir", g_extFilesDir)); + if (System_GetPropertyBool(SYSPROP_ANDROID_SCOPED_STORAGE)) { + storage->Add(new InfoItem("Scoped Storage", di->T("Yes"))); + } +#endif + ViewGroup *buildConfigScroll = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, FILL_PARENT)); buildConfigScroll->SetTag("DevSystemInfoBuildConfig"); - LinearLayout *buildConfig = new LinearLayout(ORIENT_VERTICAL); + LinearLayout *buildConfig = new LinearLayoutList(ORIENT_VERTICAL); buildConfig->SetSpacing(0); buildConfigScroll->Add(buildConfig); tabHolder->AddTab(si->T("Build Config"), buildConfigScroll); @@ -585,8 +617,8 @@ void SystemInfoScreen::CreateViews() { #else buildConfig->Add(new InfoItem("NDEBUG", "")); #endif -#ifdef USE_ADDRESS_SANITIZER - buildConfig->Add(new InfoItem("USE_ADDRESS_SANITIZER", "")); +#ifdef USE_ASAN + buildConfig->Add(new InfoItem("USE_ASAN", "")); #endif #ifdef USING_GLES2 buildConfig->Add(new InfoItem("USING_GLES2", "")); @@ -609,7 +641,7 @@ void SystemInfoScreen::CreateViews() { ViewGroup *cpuExtensionsScroll = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, FILL_PARENT)); cpuExtensionsScroll->SetTag("DevSystemInfoCPUExt"); - LinearLayout *cpuExtensions = new LinearLayout(ORIENT_VERTICAL); + LinearLayout *cpuExtensions = new LinearLayoutList(ORIENT_VERTICAL); cpuExtensions->SetSpacing(0); cpuExtensionsScroll->Add(cpuExtensions); @@ -624,7 +656,7 @@ void SystemInfoScreen::CreateViews() { ViewGroup *gpuExtensionsScroll = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, FILL_PARENT)); gpuExtensionsScroll->SetTag("DevSystemInfoOGLExt"); - LinearLayout *gpuExtensions = new LinearLayout(ORIENT_VERTICAL); + LinearLayout *gpuExtensions = new LinearLayoutList(ORIENT_VERTICAL); gpuExtensions->SetSpacing(0); gpuExtensionsScroll->Add(gpuExtensions); @@ -653,7 +685,7 @@ void SystemInfoScreen::CreateViews() { if (exts.size() > 0) { ViewGroup *eglExtensionsScroll = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, FILL_PARENT)); eglExtensionsScroll->SetTag("DevSystemInfoEGLExt"); - LinearLayout *eglExtensions = new LinearLayout(ORIENT_VERTICAL); + LinearLayout *eglExtensions = new LinearLayoutList(ORIENT_VERTICAL); eglExtensions->SetSpacing(0); eglExtensionsScroll->Add(eglExtensions); @@ -803,7 +835,7 @@ void JitCompareScreen::CreateViews() { leftColumn->Add(new Choice(dev->T("Stats")))->OnClick.Handle(this, &JitCompareScreen::OnShowStats); leftColumn->Add(new Choice(di->T("Back")))->OnClick.Handle(this, &UIScreen::OnBack); blockName_ = leftColumn->Add(new TextView(dev->T("No block"))); - blockAddr_ = leftColumn->Add(new TextEdit("", "", new LayoutParams(FILL_PARENT, WRAP_CONTENT))); + blockAddr_ = leftColumn->Add(new TextEdit("", dev->T("Block address"), "", new LayoutParams(FILL_PARENT, WRAP_CONTENT))); blockAddr_->OnTextChange.Handle(this, &JitCompareScreen::OnAddressChange); blockStats_ = leftColumn->Add(new TextView("")); @@ -1063,7 +1095,7 @@ void ShaderListScreen::CreateViews() { layout->Add(new Button(di->T("Back")))->OnClick.Handle(this, &UIScreen::OnBack); for (size_t i = 0; i < ARRAY_SIZE(shaderTypes); i++) { ScrollView *scroll = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(1.0)); - LinearLayout *shaderList = new LinearLayout(ORIENT_VERTICAL, new LayoutParams(FILL_PARENT, WRAP_CONTENT)); + LinearLayout *shaderList = new LinearLayoutList(ORIENT_VERTICAL, new LayoutParams(FILL_PARENT, WRAP_CONTENT)); int count = ListShaders(shaderTypes[i].type, shaderList); scroll->Add(shaderList); tabs_->AddTab(StringFromFormat("%s (%d)", shaderTypes[i].name, count), scroll); @@ -1092,7 +1124,7 @@ void ShaderViewScreen::CreateViews() { scroll->SetTag("DevShaderView"); layout->Add(scroll); - LinearLayout *lineLayout = new LinearLayout(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT)); + LinearLayout *lineLayout = new LinearLayoutList(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT)); lineLayout->SetSpacing(0.0); scroll->Add(lineLayout); @@ -1105,3 +1137,90 @@ void ShaderViewScreen::CreateViews() { layout->Add(new Button(di->T("Back")))->OnClick.Handle(this, &UIScreen::OnBack); } + +const std::string framedumpsBaseUrl = "http://framedump.ppsspp.org/repro/"; + +FrameDumpTestScreen::FrameDumpTestScreen() { + +} + +FrameDumpTestScreen::~FrameDumpTestScreen() { + g_DownloadManager.CancelAll(); +} + +void FrameDumpTestScreen::CreateViews() { + using namespace UI; + + root_ = new AnchorLayout(new LayoutParams(FILL_PARENT, FILL_PARENT)); + auto di = GetI18NCategory("Dialog"); + + TabHolder *tabHolder; + tabHolder = new TabHolder(ORIENT_VERTICAL, 200, new AnchorLayoutParams(10, 0, 10, 0, false)); + root_->Add(tabHolder); + AddStandardBack(root_); + tabHolder->SetTag("DumpTypes"); + root_->SetDefaultFocusView(tabHolder); + + ViewGroup *dumpsScroll = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, FILL_PARENT)); + dumpsScroll->SetTag("GameSettingsGraphics"); + LinearLayout *dumps = new LinearLayoutList(ORIENT_VERTICAL); + dumps->SetSpacing(0); + dumpsScroll->Add(dumps); + tabHolder->AddTab("Dumps", dumps); + + dumps->Add(new ItemHeader("GE Frame Dumps")); + + for (auto &file : files_) { + std::string url = framedumpsBaseUrl + file; + Choice *c = dumps->Add(new Choice(file)); + c->SetTag(url); + c->OnClick.Handle(this, &FrameDumpTestScreen::OnLoadDump); + } +} + +UI::EventReturn FrameDumpTestScreen::OnLoadDump(UI::EventParams ¶ms) { + std::string url = params.v->Tag(); + INFO_LOG(COMMON, "Trying to launch '%s'", url.c_str()); + // Our disc streaming functionality detects the URL and takes over and handles loading framedumps well, + // except for some reason the game ID. + // TODO: Fix that since it can be important for compat settings. + LaunchFile(screenManager(), url); + return UI::EVENT_DONE; +} + +void FrameDumpTestScreen::update() { + UIScreen::update(); + + if (!listing_) { + listing_ = g_DownloadManager.StartDownload(framedumpsBaseUrl, ""); + } + + if (listing_ && listing_->Done() && files_.empty()) { + if (listing_->ResultCode() == 200) { + std::string listingHtml; + listing_->buffer().TakeAll(&listingHtml); + + std::vector lines; + // We rely slightly on nginx listing format here. Not great. + SplitString(listingHtml, '\n', lines); + for (auto &line : lines) { + std::string trimmed = StripSpaces(line); + if (startsWith(trimmed, " #include "Common/Data/Text/I18n.h" +#include "Common/Net/HTTPClient.h" #include "Common/UI/UIScreen.h" #include "UI/MiscScreens.h" @@ -181,5 +182,21 @@ private: DebugShaderType type_; }; +class FrameDumpTestScreen : public UIDialogScreenWithBackground { +public: + FrameDumpTestScreen(); + ~FrameDumpTestScreen(); + + void CreateViews() override; + void update() override; + +private: + UI::EventReturn OnLoadDump(UI::EventParams &e); + + std::vector files_; + std::shared_ptr listing_; + std::shared_ptr dumpDownload_; +}; + void DrawProfile(UIContext &ui); const char *GetCompilerABI(); diff --git a/UI/DisplayLayoutScreen.cpp b/UI/DisplayLayoutScreen.cpp index 96b224c573..2211cb6cbb 100644 --- a/UI/DisplayLayoutScreen.cpp +++ b/UI/DisplayLayoutScreen.cpp @@ -65,7 +65,9 @@ public: : MultiTouchDisplay(img, scale, new UI::AnchorLayoutParams(x * screenBounds.w, y * screenBounds.h, UI::NONE, UI::NONE, true)), x_(x), y_(y), screenBounds_(screenBounds) { UpdateScale(scale); - } + } + + std::string DescribeText() const override; void SaveDisplayPosition() { x_ = bounds_.centerX() / screenBounds_.w; @@ -84,6 +86,11 @@ private: const Bounds &screenBounds_; }; +std::string DragDropDisplay::DescribeText() const { + auto u = GetI18NCategory("UI Elements"); + return u->T("Screen representation"); +} + DisplayLayoutScreen::DisplayLayoutScreen() { // Ignore insets - just couldn't get the logic to work. ignoreInsets_ = true; @@ -219,8 +226,12 @@ public: Boundary(UI::LayoutParams *layoutParams) : UI::View(layoutParams) { } + std::string DescribeText() const override { + return ""; + } + void Draw(UIContext &dc) override { - dc.Draw()->DrawImageStretch(dc.theme->whiteImage, bounds_.x, bounds_.y, bounds_.x2(), bounds_.y2(), dc.theme->itemDownStyle.background.color); + dc.Draw()->DrawImageCenterTexel(dc.theme->whiteImage, bounds_.x, bounds_.y, bounds_.x2(), bounds_.y2(), dc.theme->itemDownStyle.background.color); } }; @@ -308,7 +319,7 @@ void DisplayLayoutScreen::CreateViews() { mode_ = new ChoiceStrip(ORIENT_VERTICAL, new AnchorLayoutParams(leftColumnWidth, WRAP_CONTENT, 10 + leftInset, NONE, NONE, 158 + 64 + 10)); mode_->AddChoice(di->T("Move")); mode_->AddChoice(di->T("Resize")); - mode_->SetSelection(0); + mode_->SetSelection(0, false); } displayRepresentation_ = new DragDropDisplay(g_Config.fSmallDisplayOffsetX, g_Config.fSmallDisplayOffsetY, ImageID("I_PSP_DISPLAY"), ScaleSettingToUI(), bounds); displayRepresentation_->SetVisibility(V_VISIBLE); diff --git a/UI/EmuScreen.cpp b/UI/EmuScreen.cpp index 9f55a050e9..703e4626d9 100644 --- a/UI/EmuScreen.cpp +++ b/UI/EmuScreen.cpp @@ -91,8 +91,6 @@ static AVIDump avi; #endif -UI::ChoiceWithValueDisplay *chatButtons; - static bool frameStep_; static int lastNumFlips; static bool startDumping; @@ -252,8 +250,6 @@ void EmuScreen::bootGame(const std::string &filename) { coreParam.printfEmuLog = false; coreParam.headLess = false; - const Bounds &bounds = screenManager()->getUIContext()->GetBounds(); - if (g_Config.iInternalResolution == 0) { coreParam.renderWidth = pixel_xres; coreParam.renderHeight = pixel_yres; @@ -1009,7 +1005,7 @@ public: GameInfoBGView(const std::string &gamePath, UI::LayoutParams *layoutParams) : InertView(layoutParams), gamePath_(gamePath) { } - void Draw(UIContext &dc) { + void Draw(UIContext &dc) override { // Should only be called when visible. std::shared_ptr ginfo = g_gameInfoCache->GetInfo(dc.GetDrawContext(), gamePath_, GAMEINFO_WANTBG); dc.Flush(); @@ -1029,6 +1025,10 @@ public: } } + std::string DescribeText() const override { + return ""; + } + void SetColor(uint32_t c) { color_ = c; } @@ -1061,37 +1061,42 @@ void EmuScreen::CreateViews() { cardboardDisableButton_->SetScale(0.65f); // make it smaller - this button can be in the way otherwise. if (g_Config.bEnableNetworkChat) { + AnchorLayoutParams *layoutParams = nullptr; switch (g_Config.iChatButtonPosition) { case 0: - chatButtons = new ChoiceWithValueDisplay(&newChat, n->T("Chat"), new AnchorLayoutParams(130, WRAP_CONTENT, 80, NONE, NONE, 50, true)); + layoutParams = new AnchorLayoutParams(130, WRAP_CONTENT, 80, NONE, NONE, 50, true); break; case 1: - chatButtons = new ChoiceWithValueDisplay(&newChat, n->T("Chat"), new AnchorLayoutParams(130, WRAP_CONTENT, bounds.centerX(), NONE, NONE, 50, true)); + layoutParams = new AnchorLayoutParams(130, WRAP_CONTENT, bounds.centerX(), NONE, NONE, 50, true); break; case 2: - chatButtons = new ChoiceWithValueDisplay(&newChat, n->T("Chat"), new AnchorLayoutParams(130, WRAP_CONTENT, NONE, NONE, 80, 50, true)); + layoutParams = new AnchorLayoutParams(130, WRAP_CONTENT, NONE, NONE, 80, 50, true); break; case 3: - chatButtons = new ChoiceWithValueDisplay(&newChat, n->T("Chat"), new AnchorLayoutParams(130, WRAP_CONTENT, 80, 50, NONE, NONE, true)); + layoutParams = new AnchorLayoutParams(130, WRAP_CONTENT, 80, 50, NONE, NONE, true); break; case 4: - chatButtons = new ChoiceWithValueDisplay(&newChat, n->T("Chat"), new AnchorLayoutParams(130, WRAP_CONTENT, bounds.centerX(), 50, NONE, NONE, true)); + layoutParams = new AnchorLayoutParams(130, WRAP_CONTENT, bounds.centerX(), 50, NONE, NONE, true); break; case 5: - chatButtons = new ChoiceWithValueDisplay(&newChat, n->T("Chat"), new AnchorLayoutParams(130, WRAP_CONTENT, NONE, 50, 80, NONE, true)); + layoutParams = new AnchorLayoutParams(130, WRAP_CONTENT, NONE, 50, 80, NONE, true); break; case 6: - chatButtons = new ChoiceWithValueDisplay(&newChat, n->T("Chat"), new AnchorLayoutParams(130, WRAP_CONTENT, 80, bounds.centerY(), NONE, NONE, true)); + layoutParams = new AnchorLayoutParams(130, WRAP_CONTENT, 80, bounds.centerY(), NONE, NONE, true); break; case 7: - chatButtons = new ChoiceWithValueDisplay(&newChat, n->T("Chat"), new AnchorLayoutParams(130, WRAP_CONTENT, NONE, bounds.centerY(), 80, NONE, true)); + layoutParams = new AnchorLayoutParams(130, WRAP_CONTENT, NONE, bounds.centerY(), 80, NONE, true); break; default: - chatButtons = new ChoiceWithValueDisplay(&newChat, n->T("Chat"), new AnchorLayoutParams(130, WRAP_CONTENT, 80, NONE, NONE, 50, true)); + layoutParams = new AnchorLayoutParams(130, WRAP_CONTENT, 80, NONE, NONE, 50, true); break; } - root_->Add(chatButtons)->OnClick.Handle(this, &EmuScreen::OnChat); + ChoiceWithValueDisplay *btn = new ChoiceWithValueDisplay(&newChat, n->T("Chat"), layoutParams); + root_->Add(btn)->OnClick.Handle(this, &EmuScreen::OnChat); + chatButton_ = btn; + } else { + chatButton_ = nullptr; } saveStatePreview_ = new AsyncImageFileView("", IS_FIXED, nullptr, new AnchorLayoutParams(bounds.centerX(), 100, NONE, NONE, true)); @@ -1165,8 +1170,8 @@ UI::EventReturn EmuScreen::OnDisableCardboard(UI::EventParams ¶ms) { } UI::EventReturn EmuScreen::OnChat(UI::EventParams ¶ms) { - if (chatButtons->GetVisibility() == UI::V_VISIBLE) { - chatButtons->SetVisibility(UI::V_GONE); + if (chatButton_ != nullptr && chatButton_->GetVisibility() == UI::V_VISIBLE) { + chatButton_->SetVisibility(UI::V_GONE); } screenManager()->push(new ChatMenu()); return UI::EVENT_DONE; @@ -1597,7 +1602,7 @@ bool EmuScreen::hasVisibleUI() { return true; if (!osm.IsEmpty() || g_Config.bShowTouchControls || g_Config.iShowFPSCounter != 0) return true; - if (g_Config.bEnableCardboardVR) + if (g_Config.bEnableCardboardVR || g_Config.bEnableNetworkChat) return true; // Debug UI. if (g_Config.bShowDebugStats || g_Config.bShowDeveloperMenu || g_Config.bShowAudioDebug || g_Config.bShowFrameProfiler || g_Config.bSimpleFrameStats) diff --git a/UI/EmuScreen.h b/UI/EmuScreen.h index 3c8492f044..afe95bde63 100644 --- a/UI/EmuScreen.h +++ b/UI/EmuScreen.h @@ -106,6 +106,7 @@ private: UI::Spinner *loadingSpinner_ = nullptr; UI::TextView *loadingTextView_ = nullptr; UI::Button *resumeButton_ = nullptr; + UI::View *chatButton_ = nullptr; UI::Button *cardboardDisableButton_ = nullptr; OnScreenMessagesView *onScreenMessagesView_ = nullptr; diff --git a/UI/GPUDriverTestScreen.cpp b/UI/GPUDriverTestScreen.cpp index c6f6011954..7f0773971a 100644 --- a/UI/GPUDriverTestScreen.cpp +++ b/UI/GPUDriverTestScreen.cpp @@ -82,16 +82,12 @@ static const std::vector fsAdrenoLogicTest = { R"(#version 450 #extension GL_ARB_separate_shader_objects : enable #extension GL_ARB_shading_language_420pack : enable - precision lowp float; - layout(location = 0) flat in lowp vec4 oColor0; + layout(location = 0) in vec4 oColor0; layout(location = 1) in highp vec2 oTexCoord0; layout(location = 0) out vec4 fragColor0; layout(set = 0, binding = 1) uniform sampler2D Sampler0; void main() { - vec4 t = texture(Sampler0, oTexCoord0).aaaa; - vec4 p = oColor0; - vec4 v = p * t; - v.rgb = clamp(v.rgb * 2.0, 0.0, 1.0); + vec4 v = texture(Sampler0, oTexCoord0).aaaa * oColor0; if (v.r < 0.2 && v.g < 0.2 && v.b < 0.2) discard; fragColor0 = vec4(0.0, 1.0, 0.0, 1.0); })" @@ -126,7 +122,7 @@ static const std::vector vsAdrenoLogicTest = { "layout (location = 0) in vec4 pos;\n" "layout (location = 1) in vec4 inColor;\n" "layout (location = 2) in vec2 inTexCoord;\n" - "layout (location = 0) out lowp vec4 outColor;\n" + "layout (location = 0) out vec4 outColor;\n" "layout (location = 1) out highp vec2 outTexCoord;\n" "out gl_PerVertex { vec4 gl_Position; };\n" "void main() {\n" @@ -183,7 +179,6 @@ static const std::vector fsFlat = { static const std::vector vsFlat = { { GLSL_3xx, - "#version 300 es\n" "in vec3 Position;\n" "in vec2 TexCoord0;\n" "in lowp vec4 Color0;\n" @@ -627,6 +622,12 @@ void GPUDriverTestScreen::ShaderTest() { // Wherever possible we should reconfigure the GL provoking vertex to match Vulkan, probably. dc.DrawImageVGradient(ImageID("I_ICON"), 0xFFFFFFFF, 0xFF808080, bounds); dc.Flush(); + + y += 120; + + dc.Begin(); + dc.DrawText("Test done", x, y, 0xFFFFFFFF, FLAG_DYNAMIC_ASCII); + dc.Flush(); } diff --git a/UI/GameInfoCache.cpp b/UI/GameInfoCache.cpp index c0cf42d56f..3cea4ebcdd 100644 --- a/UI/GameInfoCache.cpp +++ b/UI/GameInfoCache.cpp @@ -233,7 +233,6 @@ bool GameInfo::DeleteAllSaveData() { std::vector fileInfo; getFilesInDir(saveDataDir[j].c_str(), &fileInfo); - u64 totalSize = 0; for (size_t i = 0; i < fileInfo.size(); i++) { File::Delete(fileInfo[i].fullName.c_str()); } diff --git a/UI/GameScreen.cpp b/UI/GameScreen.cpp index 697a2145c9..b42b80abf4 100644 --- a/UI/GameScreen.cpp +++ b/UI/GameScreen.cpp @@ -27,10 +27,12 @@ #include "Common/Data/Text/I18n.h" #include "Common/Data/Encoding/Utf8.h" #include "Common/File/FileUtil.h" +#include "Common/StringUtils.h" #include "Common/System/System.h" #include "Common/System/NativeApp.h" #include "Core/Host.h" #include "Core/Config.h" +#include "Core/Reporting.h" #include "Core/System.h" #include "UI/CwCheatScreen.h" #include "UI/EmuScreen.h" @@ -92,12 +94,16 @@ void GameScreen::CreateViews() { tvInstallDataSize_->SetVisibility(V_GONE); tvRegion_ = infoLayout->Add(new TextView("", ALIGN_LEFT, true, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT))); tvRegion_->SetShadow(true); + tvCRC_ = infoLayout->Add(new TextView("", ALIGN_LEFT, true, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT))); + tvCRC_->SetShadow(true); + tvCRC_->SetVisibility(Reporting::HasCRC(gamePath_) ? V_VISIBLE : V_GONE); } else { tvTitle_ = nullptr; tvGameSize_ = nullptr; tvSaveDataSize_ = nullptr; tvInstallDataSize_ = nullptr; tvRegion_ = nullptr; + tvCRC_ = nullptr; } ViewGroup *rightColumn = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(300, FILL_PARENT, actionMenuMargins)); @@ -234,6 +240,13 @@ void GameScreen::render() { } } + if (tvCRC_ && Reporting::HasCRC(gamePath_)) { + auto rp = GetI18NCategory("Reporting"); + std::string crc = StringFromFormat("%08X", Reporting::RetrieveCRC(gamePath_)); + tvCRC_->SetText(ReplaceAll(rp->T("FeedbackCRCValue", "Disc CRC: [VALUE]"), "[VALUE]", crc)); + tvCRC_->SetVisibility(UI::V_VISIBLE); + } + if (!info->id.empty()) { btnGameSettings_->SetVisibility(info->hasConfig ? UI::V_VISIBLE : UI::V_GONE); btnDeleteGameConfig_->SetVisibility(info->hasConfig ? UI::V_VISIBLE : UI::V_GONE); diff --git a/UI/GameScreen.h b/UI/GameScreen.h index 2e7fa3f596..a746250436 100644 --- a/UI/GameScreen.h +++ b/UI/GameScreen.h @@ -67,6 +67,7 @@ private: UI::TextView *tvSaveDataSize_; UI::TextView *tvInstallDataSize_; UI::TextView *tvRegion_; + UI::TextView *tvCRC_; UI::Choice *btnGameSettings_; UI::Choice *btnCreateGameConfig_; diff --git a/UI/GameSettingsScreen.cpp b/UI/GameSettingsScreen.cpp index d80926bcb3..7665e96e35 100644 --- a/UI/GameSettingsScreen.cpp +++ b/UI/GameSettingsScreen.cpp @@ -216,7 +216,7 @@ void GameSettingsScreen::CreateViews() { // Graphics ViewGroup *graphicsSettingsScroll = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, FILL_PARENT)); graphicsSettingsScroll->SetTag("GameSettingsGraphics"); - LinearLayout *graphicsSettings = new LinearLayout(ORIENT_VERTICAL); + LinearLayout *graphicsSettings = new LinearLayoutList(ORIENT_VERTICAL); graphicsSettings->SetSpacing(0); graphicsSettingsScroll->Add(graphicsSettings); tabHolder->AddTab(ms->T("Graphics"), graphicsSettingsScroll); @@ -459,15 +459,6 @@ void GameSettingsScreen::CreateViews() { return !g_Config.bSoftwareRendering && g_Config.bHardwareTransform; }); - CheckBox *clearHack = graphicsSettings->Add(new CheckBox(&g_Config.bClearFramebuffersOnFirstUseHack, gr->T("Clear Speedhack", "Clear framebuffers on first use (speedhack)"))); - clearHack->OnClick.Add([=](EventParams &e) { - settingInfo_->Show(gr->T("ClearSpeedhack Tip", "Sometimes faster (mostly on mobile devices), may cause glitches"), e.v); - return UI::EVENT_CONTINUE; - }); - clearHack->SetEnabledFunc([] { - return !g_Config.bSoftwareRendering; - }); - CheckBox *texBackoff = graphicsSettings->Add(new CheckBox(&g_Config.bTextureBackoffCache, gr->T("Lazy texture caching", "Lazy texture caching (speedup)"))); texBackoff->SetDisabledPtr(&g_Config.bSoftwareRendering); @@ -611,7 +602,7 @@ void GameSettingsScreen::CreateViews() { // Audio ViewGroup *audioSettingsScroll = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, FILL_PARENT)); audioSettingsScroll->SetTag("GameSettingsAudio"); - LinearLayout *audioSettings = new LinearLayout(ORIENT_VERTICAL); + LinearLayout *audioSettings = new LinearLayoutList(ORIENT_VERTICAL); audioSettings->SetSpacing(0); audioSettingsScroll->Add(audioSettings); tabHolder->AddTab(ms->T("Audio"), audioSettingsScroll); @@ -670,7 +661,7 @@ void GameSettingsScreen::CreateViews() { // Control ViewGroup *controlsSettingsScroll = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, FILL_PARENT)); controlsSettingsScroll->SetTag("GameSettingsControls"); - LinearLayout *controlsSettings = new LinearLayout(ORIENT_VERTICAL); + LinearLayout *controlsSettings = new LinearLayoutList(ORIENT_VERTICAL); controlsSettings->SetSpacing(0); controlsSettingsScroll->Add(controlsSettings); tabHolder->AddTab(ms->T("Controls"), controlsSettingsScroll); @@ -679,6 +670,7 @@ void GameSettingsScreen::CreateViews() { if (!g_Config.bSimpleUI) { #if defined(USING_WIN_UI) + controlsSettings->Add(new CheckBox(&g_Config.bSystemControls, co->T("Enable standard shortcut keys"))); controlsSettings->Add(new CheckBox(&g_Config.bGamepadOnlyFocused, co->T("Ignore gamepads when not focused"))); controlsSettings->Add(new CheckBox(&g_Config.bEnableDInputWithXInput, co->T("Enable Dinput along Xinput","Enable Dinput along Xinput gamepads(might cause problems with gamepads that can use both without manual switch)"))); @@ -791,7 +783,7 @@ void GameSettingsScreen::CreateViews() { ViewGroup *networkingSettingsScroll = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, FILL_PARENT)); networkingSettingsScroll->SetTag("GameSettingsNetworking"); - LinearLayout *networkingSettings = new LinearLayout(ORIENT_VERTICAL); + LinearLayout *networkingSettings = new LinearLayoutList(ORIENT_VERTICAL); networkingSettings->SetSpacing(0); networkingSettingsScroll->Add(networkingSettings); tabHolder->AddTab(ms->T("Networking"), networkingSettingsScroll); @@ -873,12 +865,11 @@ void GameSettingsScreen::CreateViews() { networkingSettings->Add(new ItemHeader(n->T("Misc", "Misc (default = compatibility)"))); networkingSettings->Add(new PopupSliderChoice(&g_Config.iPortOffset, 0, 60000, n->T("Port offset", "Port offset (0 = PSP compatibility)"), 100, screenManager())); networkingSettings->Add(new PopupSliderChoice(&g_Config.iMinTimeout, 0, 15000, n->T("Minimum Timeout", "Minimum Timeout (override in ms, 0 = default)"), 50, screenManager())); - networkingSettings->Add(new CheckBox(&g_Config.bTCPNoDelay, n->T("TCP No Delay", "TCP No Delay (faster TCP)"))); networkingSettings->Add(new CheckBox(&g_Config.bForcedFirstConnect, n->T("Forced First Connect", "Forced First Connect (faster Connect)"))); ViewGroup *toolsScroll = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, FILL_PARENT)); toolsScroll->SetTag("GameSettingsTools"); - LinearLayout *tools = new LinearLayout(ORIENT_VERTICAL); + LinearLayout *tools = new LinearLayoutList(ORIENT_VERTICAL); tools->SetSpacing(0); toolsScroll->Add(tools); tabHolder->AddTab(ms->T("Tools"), toolsScroll); @@ -896,7 +887,7 @@ void GameSettingsScreen::CreateViews() { // System ViewGroup *systemSettingsScroll = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, FILL_PARENT)); systemSettingsScroll->SetTag("GameSettingsSystem"); - LinearLayout *systemSettings = new LinearLayout(ORIENT_VERTICAL); + LinearLayout *systemSettings = new LinearLayoutList(ORIENT_VERTICAL); systemSettings->SetSpacing(0); systemSettingsScroll->Add(systemSettings); tabHolder->AddTab(ms->T("System"), systemSettingsScroll); @@ -932,7 +923,7 @@ void GameSettingsScreen::CreateViews() { rewindFreq->SetZeroLabel(sy->T("Off")); systemSettings->Add(new CheckBox(&g_Config.bMemStickInserted, sy->T("Memory Stick inserted"))); - PopupSliderChoice *memStickSize = systemSettings->Add(new PopupSliderChoice(&g_Config.iMemStickSizeGB, 1, 32, sy->T("Change Memory Stick Size", "Change Memory Stick Size(GB)"), screenManager(), "GB")); + systemSettings->Add(new PopupSliderChoice(&g_Config.iMemStickSizeGB, 1, 32, sy->T("Change Memory Stick Size", "Change Memory Stick Size(GB)"), screenManager(), "GB")); } @@ -1024,7 +1015,7 @@ void GameSettingsScreen::CreateViews() { } #endif -#if defined(_M_X64) +#if PPSSPP_ARCH(AMD64) systemSettings->Add(new CheckBox(&g_Config.bCacheFullIsoInRam, sy->T("Cache ISO in RAM", "Cache full ISO in RAM")))->SetEnabled(!PSP_IsInited()); #endif @@ -1664,7 +1655,7 @@ void DeveloperToolsScreen::CreateViews() { AddStandardBack(root_); - LinearLayout *list = settingsScroll->Add(new LinearLayout(ORIENT_VERTICAL, new LinearLayoutParams(1.0f))); + LinearLayout *list = settingsScroll->Add(new LinearLayoutList(ORIENT_VERTICAL, new LinearLayoutParams(1.0f))); list->SetSpacing(0); list->Add(new ItemHeader(sy->T("General"))); @@ -1694,6 +1685,8 @@ void DeveloperToolsScreen::CreateViews() { if (g_Config.iGPUBackend == (int)GPUBackend::VULKAN || g_Config.iGPUBackend == (int)GPUBackend::OPENGL) { list->Add(new Choice(dev->T("GPU Driver Test")))->OnClick.Handle(this, &DeveloperToolsScreen::OnGPUDriverTest); } + list->Add(new CheckBox(&g_Config.bVendorBugChecksEnabled, dev->T("Enable driver bug workarounds"))); + list->Add(new Choice(dev->T("Framedump tests")))->OnClick.Handle(this, &DeveloperToolsScreen::OnFramedumpTest); list->Add(new Choice(dev->T("Touchscreen Test")))->OnClick.Handle(this, &DeveloperToolsScreen::OnTouchscreenTest); allowDebugger_ = !WebServerStopped(WebServerFlags::DEBUGGER); @@ -1872,6 +1865,11 @@ UI::EventReturn DeveloperToolsScreen::OnGPUDriverTest(UI::EventParams &e) { return UI::EVENT_DONE; } +UI::EventReturn DeveloperToolsScreen::OnFramedumpTest(UI::EventParams &e) { + screenManager()->push(new FrameDumpTestScreen()); + return UI::EVENT_DONE; +} + UI::EventReturn DeveloperToolsScreen::OnTouchscreenTest(UI::EventParams &e) { screenManager()->push(new TouchTestScreen()); return UI::EVENT_DONE; @@ -1925,7 +1923,7 @@ void HostnameSelectScreen::CreatePopupContents(UI::ViewGroup *parent) { LinearLayout *valueRow = new LinearLayout(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, FILL_PARENT, Margins(0, 0, 0, 10))); - addrView_ = new TextEdit(*value_, ""); + addrView_ = new TextEdit(*value_, n->T("Hostname"), ""); addrView_->SetTextAlign(FLAG_DYNAMIC_ASCII); valueRow->Add(addrView_); parent->Add(valueRow); diff --git a/UI/GameSettingsScreen.h b/UI/GameSettingsScreen.h index 489863758b..0238215f54 100644 --- a/UI/GameSettingsScreen.h +++ b/UI/GameSettingsScreen.h @@ -179,6 +179,7 @@ private: UI::EventReturn OnJitDebugTools(UI::EventParams &e); UI::EventReturn OnRemoteDebugger(UI::EventParams &e); UI::EventReturn OnGPUDriverTest(UI::EventParams &e); + UI::EventReturn OnFramedumpTest(UI::EventParams &e); UI::EventReturn OnTouchscreenTest(UI::EventParams &e); UI::EventReturn OnCopyStatesToRoot(UI::EventParams &e); diff --git a/UI/GamepadEmu.cpp b/UI/GamepadEmu.cpp index 68922d9b7a..d38bac4b92 100644 --- a/UI/GamepadEmu.cpp +++ b/UI/GamepadEmu.cpp @@ -18,6 +18,7 @@ #include #include "Common/Data/Color/RGBAUtil.h" +#include "Common/Data/Text/I18n.h" #include "Common/System/Display.h" #include "Common/System/System.h" #include "Common/Render/TextureAtlas.h" @@ -36,7 +37,7 @@ static u32 GetButtonColor() { return g_Config.iTouchButtonStyle != 0 ? 0xFFFFFF : 0xc0b080; } -GamepadView::GamepadView(UI::LayoutParams *layoutParams) : UI::View(layoutParams) { +GamepadView::GamepadView(const char *key, UI::LayoutParams *layoutParams) : UI::View(layoutParams), key_(key) { lastFrameTime_ = time_now_d(); } @@ -53,6 +54,11 @@ void GamepadView::Update() { lastFrameTime_ = now; } +std::string GamepadView::DescribeText() const { + auto co = GetI18NCategory("Controls"); + return co->T(key_); +} + float GamepadView::GetButtonOpacity() { if (coreState != CORE_RUNNING) { return 0.0f; @@ -262,8 +268,8 @@ bool PSPButton::IsDown() { return (__CtrlPeekButtons() & pspButtonBit_) != 0; } -PSPDpad::PSPDpad(ImageID arrowIndex, ImageID arrowDownIndex, ImageID overlayIndex, float scale, float spacing, UI::LayoutParams *layoutParams) - : GamepadView(layoutParams), arrowIndex_(arrowIndex), arrowDownIndex_(arrowDownIndex), overlayIndex_(overlayIndex), +PSPDpad::PSPDpad(ImageID arrowIndex, const char *key, ImageID arrowDownIndex, ImageID overlayIndex, float scale, float spacing, UI::LayoutParams *layoutParams) + : GamepadView(key, layoutParams), arrowIndex_(arrowIndex), arrowDownIndex_(arrowDownIndex), overlayIndex_(overlayIndex), scale_(scale), spacing_(spacing), dragPointerId_(-1), down_(0) { } @@ -273,7 +279,6 @@ void PSPDpad::GetContentDimensions(const UIContext &dc, float &w, float &h) cons } void PSPDpad::Touch(const TouchInput &input) { - int lastDown = down_; GamepadView::Touch(input); if (input.flags & TOUCH_DOWN) { @@ -392,8 +397,8 @@ void PSPDpad::Draw(UIContext &dc) { } } -PSPStick::PSPStick(ImageID bgImg, ImageID stickImg, ImageID stickDownImg, int stick, float scale, UI::LayoutParams *layoutParams) - : GamepadView(layoutParams), dragPointerId_(-1), bgImg_(bgImg), stickImageIndex_(stickImg), stickDownImg_(stickDownImg), stick_(stick), scale_(scale), centerX_(-1), centerY_(-1) { +PSPStick::PSPStick(ImageID bgImg, const char *key, ImageID stickImg, ImageID stickDownImg, int stick, float scale, UI::LayoutParams *layoutParams) + : GamepadView(key, layoutParams), dragPointerId_(-1), bgImg_(bgImg), stickImageIndex_(stickImg), stickDownImg_(stickDownImg), stick_(stick), scale_(scale), centerX_(-1), centerY_(-1) { stick_size_ = 50; } @@ -412,7 +417,6 @@ void PSPStick::Draw(UIContext &dc) { uint32_t colorBg = colorAlpha(GetButtonColor(), opacity); uint32_t downBg = colorAlpha(0x00FFFFFF, opacity * 0.5f); - uint32_t color = colorAlpha(0x808080, opacity); if (centerX_ < 0.0f) { centerX_ = bounds_.centerX(); @@ -496,8 +500,8 @@ void PSPStick::ProcessTouch(float x, float y, bool down) { } } -PSPCustomStick::PSPCustomStick(ImageID bgImg, ImageID stickImg, ImageID stickDownImg, float scale, UI::LayoutParams *layoutParams) - : PSPStick(bgImg, stickImg, stickDownImg, -1, scale, layoutParams) { +PSPCustomStick::PSPCustomStick(ImageID bgImg, const char *key, ImageID stickImg, ImageID stickDownImg, float scale, UI::LayoutParams *layoutParams) + : PSPStick(bgImg, key, stickImg, stickDownImg, -1, scale, layoutParams) { } void PSPCustomStick::Draw(UIContext &dc) { @@ -511,7 +515,6 @@ void PSPCustomStick::Draw(UIContext &dc) { uint32_t colorBg = colorAlpha(GetButtonColor(), opacity); uint32_t downBg = colorAlpha(0x00FFFFFF, opacity * 0.5f); - uint32_t color = colorAlpha(0x808080, opacity); if (centerX_ < 0.0f) { centerX_ = bounds_.centerX(); @@ -772,49 +775,49 @@ UI::ViewGroup *CreatePadLayout(float xres, float yres, bool *pause) { const ImageID stickBg = g_Config.iTouchButtonStyle ? ImageID("I_STICK_BG_LINE") : ImageID("I_STICK_BG"); static const ImageID comboKeyImages[5] = { ImageID("I_1"), ImageID("I_2"), ImageID("I_3"), ImageID("I_4"), ImageID("I_5") }; - auto addPSPButton = [=](int buttonBit, ImageID bgImg, ImageID bgDownImg, ImageID img, const ConfigTouchPos &touch, ButtonOffset off = { 0, 0 }) -> PSPButton * { + auto addPSPButton = [=](int buttonBit, const char *key, ImageID bgImg, ImageID bgDownImg, ImageID img, const ConfigTouchPos &touch, ButtonOffset off = { 0, 0 }) -> PSPButton * { if (touch.show) { - return root->Add(new PSPButton(buttonBit, bgImg, bgDownImg, img, touch.scale, buttonLayoutParams(touch, off))); + return root->Add(new PSPButton(buttonBit, key, bgImg, bgDownImg, img, touch.scale, buttonLayoutParams(touch, off))); } return nullptr; }; - auto addComboKey = [=](int buttonBit, bool toggle, ImageID bgImg, ImageID bgDownImg, ImageID img, const ConfigTouchPos &touch) -> ComboKey * { + auto addComboKey = [=](int buttonBit, const char *key, bool toggle, ImageID bgImg, ImageID bgDownImg, ImageID img, const ConfigTouchPos &touch) -> ComboKey * { if (touch.show) { - return root->Add(new ComboKey(buttonBit, toggle, bgImg, bgDownImg, img, touch.scale, buttonLayoutParams(touch))); + return root->Add(new ComboKey(buttonBit, key, toggle, bgImg, bgDownImg, img, touch.scale, buttonLayoutParams(touch))); } return nullptr; }; - auto addBoolButton = [=](bool *value, ImageID bgImg, ImageID bgDownImg, ImageID img, const ConfigTouchPos &touch) -> BoolButton * { + auto addBoolButton = [=](bool *value, const char *key, ImageID bgImg, ImageID bgDownImg, ImageID img, const ConfigTouchPos &touch) -> BoolButton * { if (touch.show) { - return root->Add(new BoolButton(value, bgImg, bgDownImg, img, touch.scale, buttonLayoutParams(touch))); + return root->Add(new BoolButton(value, key, bgImg, bgDownImg, img, touch.scale, buttonLayoutParams(touch))); } return nullptr; }; - auto addFPSLimitButton = [=](FPSLimit value, ImageID bgImg, ImageID bgDownImg, ImageID img, const ConfigTouchPos &touch) -> FPSLimitButton * { + auto addFPSLimitButton = [=](FPSLimit value, const char *key, ImageID bgImg, ImageID bgDownImg, ImageID img, const ConfigTouchPos &touch) -> FPSLimitButton * { if (touch.show) { - return root->Add(new FPSLimitButton(value, bgImg, bgDownImg, img, touch.scale, buttonLayoutParams(touch))); + return root->Add(new FPSLimitButton(value, key, bgImg, bgDownImg, img, touch.scale, buttonLayoutParams(touch))); } return nullptr; }; if (!System_GetPropertyBool(SYSPROP_HAS_BACK_BUTTON) || g_Config.bShowTouchPause) { - root->Add(new BoolButton(pause, roundImage, ImageID("I_ROUND"), ImageID("I_ARROW"), 1.0f, new AnchorLayoutParams(halfW, 20, NONE, NONE, true)))->SetAngle(90); + root->Add(new BoolButton(pause, "Pause button", roundImage, ImageID("I_ROUND"), ImageID("I_ARROW"), 1.0f, new AnchorLayoutParams(halfW, 20, NONE, NONE, true)))->SetAngle(90); } // touchActionButtonCenter.show will always be true, since that's the default. if (g_Config.bShowTouchCircle) - addPSPButton(CTRL_CIRCLE, roundImage, ImageID("I_ROUND"), ImageID("I_CIRCLE"), g_Config.touchActionButtonCenter, circleOffset); + addPSPButton(CTRL_CIRCLE, "Circle button", roundImage, ImageID("I_ROUND"), ImageID("I_CIRCLE"), g_Config.touchActionButtonCenter, circleOffset); if (g_Config.bShowTouchCross) - addPSPButton(CTRL_CROSS, roundImage, ImageID("I_ROUND"), ImageID("I_CROSS"), g_Config.touchActionButtonCenter, crossOffset); + addPSPButton(CTRL_CROSS, "Cross button", roundImage, ImageID("I_ROUND"), ImageID("I_CROSS"), g_Config.touchActionButtonCenter, crossOffset); if (g_Config.bShowTouchTriangle) - addPSPButton(CTRL_TRIANGLE, roundImage, ImageID("I_ROUND"), ImageID("I_TRIANGLE"), g_Config.touchActionButtonCenter, triangleOffset); + addPSPButton(CTRL_TRIANGLE, "Triangle button", roundImage, ImageID("I_ROUND"), ImageID("I_TRIANGLE"), g_Config.touchActionButtonCenter, triangleOffset); if (g_Config.bShowTouchSquare) - addPSPButton(CTRL_SQUARE, roundImage, ImageID("I_ROUND"), ImageID("I_SQUARE"), g_Config.touchActionButtonCenter, squareOffset); + addPSPButton(CTRL_SQUARE, "Square button", roundImage, ImageID("I_ROUND"), ImageID("I_SQUARE"), g_Config.touchActionButtonCenter, squareOffset); - addPSPButton(CTRL_START, rectImage, ImageID("I_RECT"), ImageID("I_START"), g_Config.touchStartKey); - addPSPButton(CTRL_SELECT, rectImage, ImageID("I_RECT"), ImageID("I_SELECT"), g_Config.touchSelectKey); + addPSPButton(CTRL_START, "Start button", rectImage, ImageID("I_RECT"), ImageID("I_START"), g_Config.touchStartKey); + addPSPButton(CTRL_SELECT, "Select button", rectImage, ImageID("I_RECT"), ImageID("I_SELECT"), g_Config.touchSelectKey); - BoolButton *unthrottle = addBoolButton(&PSP_CoreParameter().unthrottle, rectImage, ImageID("I_RECT"), ImageID("I_ARROW"), g_Config.touchUnthrottleKey); + BoolButton *unthrottle = addBoolButton(&PSP_CoreParameter().unthrottle, "Unthrottle button", rectImage, ImageID("I_RECT"), ImageID("I_ARROW"), g_Config.touchUnthrottleKey); if (unthrottle) { unthrottle->SetAngle(180.0f); unthrottle->OnChange.Add([](UI::EventParams &e) { @@ -826,50 +829,50 @@ UI::ViewGroup *CreatePadLayout(float xres, float yres, bool *pause) { } if (g_Config.touchRapidFireKey.show) { - auto rapidFire = root->Add(new RapidFireButton(rectImage, ImageID("I_RECT"), ImageID("I_ARROW"), g_Config.touchRapidFireKey.scale, buttonLayoutParams(g_Config.touchRapidFireKey))); + auto rapidFire = root->Add(new RapidFireButton("Rapid fire button", rectImage, ImageID("I_RECT"), ImageID("I_ARROW"), g_Config.touchRapidFireKey.scale, buttonLayoutParams(g_Config.touchRapidFireKey))); rapidFire->SetAngle(90.0f, 180.0f); } if (g_Config.touchAnalogRotationCWKey.show) { - auto analogRotationCC = root->Add(new AnalogRotationButton(true, rectImage, ImageID("I_RECT"), ImageID("I_ARROW"), g_Config.touchAnalogRotationCWKey.scale, buttonLayoutParams(g_Config.touchAnalogRotationCWKey))); + auto analogRotationCC = root->Add(new AnalogRotationButton(true, "Analog clockwise rotation button", rectImage, ImageID("I_RECT"), ImageID("I_ARROW"), g_Config.touchAnalogRotationCWKey.scale, buttonLayoutParams(g_Config.touchAnalogRotationCWKey))); analogRotationCC->SetAngle(190.0f, 180.0f); } if (g_Config.touchAnalogRotationCCWKey.show) { - auto analogRotationCCW = root->Add(new AnalogRotationButton(false, rectImage, ImageID("I_RECT"), ImageID("I_ARROW"), g_Config.touchAnalogRotationCCWKey.scale, buttonLayoutParams(g_Config.touchAnalogRotationCCWKey))); + auto analogRotationCCW = root->Add(new AnalogRotationButton(false, "Analog counter clockwise rotation button", rectImage, ImageID("I_RECT"), ImageID("I_ARROW"), g_Config.touchAnalogRotationCCWKey.scale, buttonLayoutParams(g_Config.touchAnalogRotationCCWKey))); analogRotationCCW->SetAngle(350.0f, 180.0f); } - FPSLimitButton *speed1 = addFPSLimitButton(FPSLimit::CUSTOM1, rectImage, ImageID("I_RECT"), ImageID("I_ARROW"), g_Config.touchSpeed1Key); + FPSLimitButton *speed1 = addFPSLimitButton(FPSLimit::CUSTOM1, "Alternate speed 1 button", rectImage, ImageID("I_RECT"), ImageID("I_ARROW"), g_Config.touchSpeed1Key); if (speed1) speed1->SetAngle(170.0f, 180.0f); - FPSLimitButton *speed2 = addFPSLimitButton(FPSLimit::CUSTOM2, rectImage, ImageID("I_RECT"), ImageID("I_ARROW"), g_Config.touchSpeed2Key); + FPSLimitButton *speed2 = addFPSLimitButton(FPSLimit::CUSTOM2, "Alternate speed 2 button", rectImage, ImageID("I_RECT"), ImageID("I_ARROW"), g_Config.touchSpeed2Key); if (speed2) speed2->SetAngle(190.0f, 180.0f); - addPSPButton(CTRL_LTRIGGER, shoulderImage, ImageID("I_SHOULDER"), ImageID("I_L"), g_Config.touchLKey); - PSPButton *rTrigger = addPSPButton(CTRL_RTRIGGER, shoulderImage, ImageID("I_SHOULDER"), ImageID("I_R"), g_Config.touchRKey); + addPSPButton(CTRL_LTRIGGER, "Left shoulder button", shoulderImage, ImageID("I_SHOULDER"), ImageID("I_L"), g_Config.touchLKey); + PSPButton *rTrigger = addPSPButton(CTRL_RTRIGGER, "Right shoulder button", shoulderImage, ImageID("I_SHOULDER"), ImageID("I_R"), g_Config.touchRKey); if (rTrigger) rTrigger->FlipImageH(true); if (g_Config.touchDpad.show) - root->Add(new PSPDpad(dirImage, ImageID("I_DIR"), ImageID("I_ARROW"), g_Config.touchDpad.scale, g_Config.fDpadSpacing, buttonLayoutParams(g_Config.touchDpad))); + root->Add(new PSPDpad(dirImage, "D-pad", ImageID("I_DIR"), ImageID("I_ARROW"), g_Config.touchDpad.scale, g_Config.fDpadSpacing, buttonLayoutParams(g_Config.touchDpad))); if (g_Config.touchAnalogStick.show) - root->Add(new PSPStick(stickBg, stickImage, ImageID("I_STICK"), 0, g_Config.touchAnalogStick.scale, buttonLayoutParams(g_Config.touchAnalogStick))); + root->Add(new PSPStick(stickBg, "Left analog stick", stickImage, ImageID("I_STICK"), 0, g_Config.touchAnalogStick.scale, buttonLayoutParams(g_Config.touchAnalogStick))); if (g_Config.touchRightAnalogStick.show) { if (g_Config.bRightAnalogCustom) - root->Add(new PSPCustomStick(stickBg, stickImage, ImageID("I_STICK"), g_Config.touchRightAnalogStick.scale, buttonLayoutParams(g_Config.touchRightAnalogStick))); + root->Add(new PSPCustomStick(stickBg, "Right analog stick", stickImage, ImageID("I_STICK"), g_Config.touchRightAnalogStick.scale, buttonLayoutParams(g_Config.touchRightAnalogStick))); else - root->Add(new PSPStick(stickBg, stickImage, ImageID("I_STICK"), 1, g_Config.touchRightAnalogStick.scale, buttonLayoutParams(g_Config.touchRightAnalogStick))); + root->Add(new PSPStick(stickBg, "Right analog stick", stickImage, ImageID("I_STICK"), 1, g_Config.touchRightAnalogStick.scale, buttonLayoutParams(g_Config.touchRightAnalogStick))); } - addComboKey(g_Config.iCombokey0, g_Config.bComboToggle0, roundImage, ImageID("I_ROUND"), comboKeyImages[0], g_Config.touchCombo0); - addComboKey(g_Config.iCombokey1, g_Config.bComboToggle1, roundImage, ImageID("I_ROUND"), comboKeyImages[1], g_Config.touchCombo1); - addComboKey(g_Config.iCombokey2, g_Config.bComboToggle2, roundImage, ImageID("I_ROUND"), comboKeyImages[2], g_Config.touchCombo2); - addComboKey(g_Config.iCombokey3, g_Config.bComboToggle3, roundImage, ImageID("I_ROUND"), comboKeyImages[3], g_Config.touchCombo3); - addComboKey(g_Config.iCombokey4, g_Config.bComboToggle4, roundImage, ImageID("I_ROUND"), comboKeyImages[4], g_Config.touchCombo4); + addComboKey(g_Config.iCombokey0, "Combo 1 button", g_Config.bComboToggle0, roundImage, ImageID("I_ROUND"), comboKeyImages[0], g_Config.touchCombo0); + addComboKey(g_Config.iCombokey1, "Combo 2 button", g_Config.bComboToggle1, roundImage, ImageID("I_ROUND"), comboKeyImages[1], g_Config.touchCombo1); + addComboKey(g_Config.iCombokey2, "Combo 3 button", g_Config.bComboToggle2, roundImage, ImageID("I_ROUND"), comboKeyImages[2], g_Config.touchCombo2); + addComboKey(g_Config.iCombokey3, "Combo 4 button", g_Config.bComboToggle3, roundImage, ImageID("I_ROUND"), comboKeyImages[3], g_Config.touchCombo3); + addComboKey(g_Config.iCombokey4, "Combo 5 button", g_Config.bComboToggle4, roundImage, ImageID("I_ROUND"), comboKeyImages[4], g_Config.touchCombo4); return root; } diff --git a/UI/GamepadEmu.h b/UI/GamepadEmu.h index 3cacb147c4..d6fc612a8e 100644 --- a/UI/GamepadEmu.h +++ b/UI/GamepadEmu.h @@ -26,25 +26,27 @@ class GamepadView : public UI::View { public: - GamepadView(UI::LayoutParams *layoutParams); + GamepadView(const char *key, UI::LayoutParams *layoutParams); void Touch(const TouchInput &input) override; bool Key(const KeyInput &input) override { return false; } void Update() override; + std::string DescribeText() const override; protected: virtual float GetButtonOpacity(); + const char *key_; double lastFrameTime_; float secondsWithoutTouch_ = 0.0; }; class MultiTouchButton : public GamepadView { public: - MultiTouchButton(ImageID bgImg, ImageID bgDownImg, ImageID img, float scale, UI::LayoutParams *layoutParams) - : GamepadView(layoutParams), scale_(scale), bgImg_(bgImg), bgDownImg_(bgDownImg), img_(img) { + MultiTouchButton(const char *key, ImageID bgImg, ImageID bgDownImg, ImageID img, float scale, UI::LayoutParams *layoutParams) + : GamepadView(key, layoutParams), scale_(scale), bgImg_(bgImg), bgDownImg_(bgDownImg), img_(img) { } void Touch(const TouchInput &input) override; @@ -71,8 +73,8 @@ private: class BoolButton : public MultiTouchButton { public: - BoolButton(bool *value, ImageID bgImg, ImageID bgDownImg, ImageID img, float scale, UI::LayoutParams *layoutParams) - : MultiTouchButton(bgImg, bgDownImg, img, scale, layoutParams), value_(value) { + BoolButton(bool *value, const char *key, ImageID bgImg, ImageID bgDownImg, ImageID img, float scale, UI::LayoutParams *layoutParams) + : MultiTouchButton(key, bgImg, bgDownImg, img, scale, layoutParams), value_(value) { } void Touch(const TouchInput &input) override; @@ -86,8 +88,8 @@ private: class FPSLimitButton : public MultiTouchButton { public: - FPSLimitButton(FPSLimit limit, ImageID bgImg, ImageID bgDownImg, ImageID img, float scale, UI::LayoutParams *layoutParams) - : MultiTouchButton(bgImg, bgDownImg, img, scale, layoutParams), limit_(limit) { + FPSLimitButton(FPSLimit limit, const char *key, ImageID bgImg, ImageID bgDownImg, ImageID img, float scale, UI::LayoutParams *layoutParams) + : MultiTouchButton(key, bgImg, bgDownImg, img, scale, layoutParams), limit_(limit) { } void Touch(const TouchInput &input) override; @@ -99,8 +101,8 @@ private: class RapidFireButton : public MultiTouchButton { public: - RapidFireButton(ImageID bgImg, ImageID bgDownImg, ImageID img, float scale, UI::LayoutParams *layoutParams) - : MultiTouchButton(bgImg, bgDownImg, img, scale, layoutParams) { + RapidFireButton(const char *key, ImageID bgImg, ImageID bgDownImg, ImageID img, float scale, UI::LayoutParams *layoutParams) + : MultiTouchButton(key, bgImg, bgDownImg, img, scale, layoutParams) { } void Touch(const TouchInput &input) override; bool IsDown() override; @@ -108,8 +110,8 @@ public: class AnalogRotationButton : public MultiTouchButton { public: - AnalogRotationButton(bool clockWise, ImageID bgImg, ImageID bgDownImg, ImageID img, float scale, UI::LayoutParams *layoutParams) - : MultiTouchButton(bgImg, bgDownImg, img, scale, layoutParams), clockWise_(clockWise) { + AnalogRotationButton(bool clockWise, const char *key, ImageID bgImg, ImageID bgDownImg, ImageID img, float scale, UI::LayoutParams *layoutParams) + : MultiTouchButton(key, bgImg, bgDownImg, img, scale, layoutParams), clockWise_(clockWise) { } void Touch(const TouchInput &input) override; void Update() override; @@ -121,8 +123,8 @@ private: class PSPButton : public MultiTouchButton { public: - PSPButton(int pspButtonBit, ImageID bgImg, ImageID bgDownImg, ImageID img, float scale, UI::LayoutParams *layoutParams) - : MultiTouchButton(bgImg, bgDownImg, img, scale, layoutParams), pspButtonBit_(pspButtonBit) { + PSPButton(int pspButtonBit, const char *key, ImageID bgImg, ImageID bgDownImg, ImageID img, float scale, UI::LayoutParams *layoutParams) + : MultiTouchButton(key, bgImg, bgDownImg, img, scale, layoutParams), pspButtonBit_(pspButtonBit) { } void Touch(const TouchInput &input) override; bool IsDown() override; @@ -133,7 +135,7 @@ private: class PSPDpad : public GamepadView { public: - PSPDpad(ImageID arrowIndex, ImageID arrowDownIndex, ImageID overlayIndex, float scale, float spacing, UI::LayoutParams *layoutParams); + PSPDpad(ImageID arrowIndex, const char *key, ImageID arrowDownIndex, ImageID overlayIndex, float scale, float spacing, UI::LayoutParams *layoutParams); void Touch(const TouchInput &input) override; void Draw(UIContext &dc) override; @@ -154,7 +156,7 @@ private: class PSPStick : public GamepadView { public: - PSPStick(ImageID bgImg, ImageID stickImg, ImageID stickDownImg, int stick, float scale, UI::LayoutParams *layoutParams); + PSPStick(ImageID bgImg, const char *key, ImageID stickImg, ImageID stickDownImg, int stick, float scale, UI::LayoutParams *layoutParams); void Touch(const TouchInput &input) override; void Draw(UIContext &dc) override; @@ -179,7 +181,7 @@ private: class PSPCustomStick : public PSPStick { public: - PSPCustomStick(ImageID bgImg, ImageID stickImg, ImageID stickDownImg, float scale, UI::LayoutParams *layoutParams); + PSPCustomStick(ImageID bgImg, const char *key, ImageID stickImg, ImageID stickDownImg, float scale, UI::LayoutParams *layoutParams); void Touch(const TouchInput &input) override; void Draw(UIContext &dc) override; @@ -201,8 +203,8 @@ const int baseActionButtonSpacing = 60; class ComboKey : public MultiTouchButton { public: - ComboKey(int pspButtonBit, bool toggle, ImageID bgImg, ImageID bgDownImg, ImageID img, float scale, UI::LayoutParams *layoutParams) - : MultiTouchButton(bgImg, bgDownImg, img, scale, layoutParams), pspButtonBit_(pspButtonBit), toggle_(toggle) { + ComboKey(int pspButtonBit, const char *key, bool toggle, ImageID bgImg, ImageID bgDownImg, ImageID img, float scale, UI::LayoutParams *layoutParams) + : MultiTouchButton(key, bgImg, bgDownImg, img, scale, layoutParams), pspButtonBit_(pspButtonBit), toggle_(toggle) { } void Touch(const TouchInput &input) override; private: diff --git a/UI/MainScreen.cpp b/UI/MainScreen.cpp index 14c6addadc..e94eba6323 100644 --- a/UI/MainScreen.cpp +++ b/UI/MainScreen.cpp @@ -119,6 +119,7 @@ public: : UI::Clickable(layoutParams), gridStyle_(gridStyle), gamePath_(gamePath) {} void Draw(UIContext &dc) override; + std::string DescribeText() const override; void GetContentDimensions(const UIContext &dc, float &w, float &h) const override { if (gridStyle_) { w = 144*g_Config.fGameGridScale; @@ -408,6 +409,12 @@ void GameButton::Draw(UIContext &dc) { dc.RebindTexture(); } +std::string GameButton::DescribeText() const { + std::shared_ptr ginfo = g_gameInfoCache->GetInfo(nullptr, gamePath_, 0); + auto u = GetI18NCategory("UI Elements"); + return ReplaceAll(u->T("%1 button"), "%1", ginfo->GetTitle()); +} + class DirButton : public UI::Button { public: DirButton(const std::string &path, bool gridStyle, UI::LayoutParams *layoutParams) @@ -427,8 +434,8 @@ public: private: std::string path_; - bool absolute_; bool gridStyle_; + bool absolute_; }; void DirButton::Draw(UIContext &dc) { @@ -484,7 +491,7 @@ void DirButton::Draw(UIContext &dc) { } GameBrowser::GameBrowser(std::string path, BrowseFlags browseFlags, bool *gridStyle, ScreenManager *screenManager, std::string lastText, std::string lastLink, UI::LayoutParams *layoutParams) - : LinearLayout(UI::ORIENT_VERTICAL, layoutParams), path_(path), gridStyle_(gridStyle), screenManager_(screenManager), browseFlags_(browseFlags), lastText_(lastText), lastLink_(lastLink) { + : LinearLayout(UI::ORIENT_VERTICAL, layoutParams), path_(path), gridStyle_(gridStyle), browseFlags_(browseFlags), lastText_(lastText), lastLink_(lastLink), screenManager_(screenManager) { using namespace UI; Refresh(); } @@ -657,23 +664,23 @@ void GameBrowser::Refresh() { ChoiceStrip *layoutChoice = topBar->Add(new ChoiceStrip(ORIENT_HORIZONTAL)); layoutChoice->AddChoice(ImageID("I_GRID")); layoutChoice->AddChoice(ImageID("I_LINES")); - layoutChoice->SetSelection(*gridStyle_ ? 0 : 1); + layoutChoice->SetSelection(*gridStyle_ ? 0 : 1, false); layoutChoice->OnChoice.Handle(this, &GameBrowser::LayoutChange); topBar->Add(new Choice(ImageID("I_GEAR"), new LayoutParams(64.0f, 64.0f)))->OnClick.Handle(this, &GameBrowser::GridSettingsClick); Add(topBar); if (*gridStyle_) { - gameList_ = new UI::GridLayout(UI::GridLayoutSettings(150*g_Config.fGameGridScale, 85*g_Config.fGameGridScale), new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT)); + gameList_ = new UI::GridLayoutList(UI::GridLayoutSettings(150*g_Config.fGameGridScale, 85*g_Config.fGameGridScale), new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT)); Add(gameList_); } else { - UI::LinearLayout *gl = new UI::LinearLayout(UI::ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT)); + UI::LinearLayout *gl = new UI::LinearLayoutList(UI::ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT)); gl->SetSpacing(4.0f); gameList_ = gl; Add(gameList_); } } else { if (*gridStyle_) { - gameList_ = new UI::GridLayout(UI::GridLayoutSettings(150*g_Config.fGameGridScale, 85*g_Config.fGameGridScale), new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT)); + gameList_ = new UI::GridLayoutList(UI::GridLayoutSettings(150*g_Config.fGameGridScale, 85*g_Config.fGameGridScale), new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT)); } else { UI::LinearLayout *gl = new UI::LinearLayout(UI::ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT)); gl->SetSpacing(4.0f); @@ -733,7 +740,7 @@ void GameBrowser::Refresh() { fileInfo.clear(); path_.GetListing(fileInfo, "zip:rar:r01:7z:"); if (!fileInfo.empty()) { - UI::LinearLayout *zl = new UI::LinearLayout(UI::ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT)); + UI::LinearLayout *zl = new UI::LinearLayoutList(UI::ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT)); zl->SetSpacing(4.0f); Add(zl); for (size_t i = 0; i < fileInfo.size(); i++) { @@ -1066,12 +1073,14 @@ void MainScreen::CreateViews() { rightColumnItems->SetSpacing(0.0f); AnchorLayout *logos = new AnchorLayout(new AnchorLayoutParams(FILL_PARENT, 60.0f, false)); - logos->Add(new ImageView(ImageID("I_ICON"), IS_DEFAULT, new AnchorLayoutParams(64, 64, 0, 0, NONE, NONE, false))); - logos->Add(new ImageView(ImageID("I_LOGO"), IS_DEFAULT, new AnchorLayoutParams(180, 64, 64, -5.0f, NONE, NONE, false))); + logos->Add(new ImageView(ImageID("I_ICON"), "", IS_DEFAULT, new AnchorLayoutParams(64, 64, 0, 0, NONE, NONE, false))); + logos->Add(new ImageView(ImageID("I_LOGO"), "PPSSPP", IS_DEFAULT, new AnchorLayoutParams(180, 64, 64, -5.0f, NONE, NONE, false))); #if !defined(MOBILE_DEVICE) if (!g_Config.bFullScreen) { - fullscreenButton_ = logos->Add(new Button(ImageID(g_Config.bFullScreen ? "I_RESTORE" : "I_FULLSCREEN"), new AnchorLayoutParams(48, 48, NONE, 0, 0, NONE, false))); + auto gr = GetI18NCategory("Graphics"); + fullscreenButton_ = logos->Add(new Button(gr->T("FullScreen", "Full Screen"), ImageID(g_Config.bFullScreen ? "I_RESTORE" : "I_FULLSCREEN"), new AnchorLayoutParams(48, 48, NONE, 0, 0, NONE, false))); + fullscreenButton_->SetIgnoreText(true); fullscreenButton_->OnClick.Handle(this, &MainScreen::OnFullScreenToggle); } #endif @@ -1180,6 +1189,10 @@ void MainScreen::sendMessage(const char *message, const char *value) { if (!strcmp(message, "boot")) { LaunchFile(screenManager(), std::string(value)); } + if (!strcmp(message, "browse_fileSelect")) { + INFO_LOG(SYSTEM, "Attempting to launch: '%s'", value); + LaunchFile(screenManager(), std::string(value)); + } if (!strcmp(message, "browse_folderSelect")) { std::string filename; #if PPSSPP_PLATFORM(ANDROID) @@ -1540,7 +1553,7 @@ void GridSettingsScreen::CreatePopupContents(UI::ViewGroup *parent) { auto sy = GetI18NCategory("System"); ScrollView *scroll = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, 50, 1.0f)); - LinearLayout *items = new LinearLayout(ORIENT_VERTICAL); + LinearLayout *items = new LinearLayoutList(ORIENT_VERTICAL); items->Add(new CheckBox(&g_Config.bGridView1, sy->T("Display Recent on a grid"))); items->Add(new CheckBox(&g_Config.bGridView2, sy->T("Display Games on a grid"))); @@ -1559,6 +1572,8 @@ void GridSettingsScreen::CreatePopupContents(UI::ViewGroup *parent) { items->Add(new Choice(sy->T("Clear Recent Games List")))->OnClick.Handle(this, &GridSettingsScreen::OnRecentClearClick); } + items->Add(new Choice(di->T("Back")))->OnClick.Handle(this, &UIScreen::OnBack); + scroll->Add(items); parent->Add(scroll); } diff --git a/UI/MainScreen.h b/UI/MainScreen.h index b8a396d157..9c3ca9b4a4 100644 --- a/UI/MainScreen.h +++ b/UI/MainScreen.h @@ -38,6 +38,8 @@ enum class BrowseFlags { }; ENUM_CLASS_BITOPS(BrowseFlags); +bool LaunchFile(ScreenManager *screenManager, std::string path); + class GameBrowser : public UI::LinearLayout { public: GameBrowser(std::string path, BrowseFlags browseFlags, bool *gridStyle, ScreenManager *screenManager, std::string lastText, std::string lastLink, UI::LayoutParams *layoutParams = nullptr); diff --git a/UI/MiscScreens.cpp b/UI/MiscScreens.cpp index 2f1e0a0334..e6c65079e8 100644 --- a/UI/MiscScreens.cpp +++ b/UI/MiscScreens.cpp @@ -131,7 +131,7 @@ void DrawBackground(UIContext &dc, float alpha) { // jitter we accumulate time instead. static int frameCount = 0.0; frameCount++; - double t = (double)frameCount / 60.0; + double t = (double)frameCount / System_GetPropertyFloat(SYSPROP_DISPLAY_REFRESH_RATE); #else double t = time_now_d(); #endif @@ -472,12 +472,19 @@ void LogoScreen::Next() { const float logoScreenSeconds = 2.5f; +LogoScreen::LogoScreen(bool gotoGameSettings) + : gotoGameSettings_(gotoGameSettings) { +} + void LogoScreen::update() { UIScreen::update(); - frames_++; - if (frames_ > 60 * logoScreenSeconds) { + double rate = std::max(30.0, (double)System_GetPropertyFloat(SYSPROP_DISPLAY_REFRESH_RATE)); + + if ((double)frames_ / rate > logoScreenSeconds) { Next(); } + frames_++; + sinceStart_ = (double)frames_ / rate; } void LogoScreen::sendMessage(const char *message, const char *value) { @@ -510,11 +517,9 @@ void LogoScreen::render() { const Bounds &bounds = dc.GetBounds(); - float xres = dc.GetBounds().w; - float yres = dc.GetBounds().h; - dc.Begin(); - float t = (float)frames_ / (60.0f * logoScreenSeconds / 3.0f); + + float t = (float)sinceStart_ / (logoScreenSeconds / 3.0f); float alpha = t; if (t > 1.0f) @@ -533,13 +538,13 @@ void LogoScreen::render() { snprintf(temp, sizeof(temp), "%s Henrik Rydg%c%crd", cr->T("created", "Created by"), 0xC3, 0xA5); dc.Draw()->DrawImage(ImageID("I_ICON"), bounds.centerX() - 120, bounds.centerY() - 30, 1.2f, textColor, ALIGN_CENTER); dc.Draw()->DrawImage(ImageID("I_LOGO"), bounds.centerX() + 40, bounds.centerY() - 30, 1.5f, textColor, ALIGN_CENTER); - //dc.Draw()->DrawTextShadow(UBUNTU48, "PPSSPP", xres / 2, yres / 2 - 30, textColor, ALIGN_CENTER); + //dc.Draw()->DrawTextShadow(UBUNTU48, "PPSSPP", bounds.w / 2, bounds.h / 2 - 30, textColor, ALIGN_CENTER); dc.SetFontScale(1.0f, 1.0f); dc.SetFontStyle(dc.theme->uiFont); dc.DrawText(temp, bounds.centerX(), bounds.centerY() + 40, textColor, ALIGN_CENTER); dc.DrawText(cr->T("license", "Free Software under GPL 2.0+"), bounds.centerX(), bounds.centerY() + 70, textColor, ALIGN_CENTER); - int ppsspp_org_y = yres / 2 + 130; + int ppsspp_org_y = bounds.h / 2 + 130; dc.DrawText("www.ppsspp.org", bounds.centerX(), ppsspp_org_y, textColor, ALIGN_CENTER); #if (defined(_WIN32) && !PPSSPP_PLATFORM(UWP)) || PPSSPP_PLATFORM(ANDROID) || PPSSPP_PLATFORM(LINUX) @@ -571,7 +576,7 @@ void CreditsScreen::CreateViews() { root_->Add(new Button(cr->T("Share PPSSPP"), new AnchorLayoutParams(260, 64, NONE, NONE, 10, 84, false)))->OnClick.Handle(this, &CreditsScreen::OnShare); root_->Add(new Button(cr->T("Twitter @PPSSPP_emu"), new AnchorLayoutParams(260, 64, NONE, NONE, 10, 154, false)))->OnClick.Handle(this, &CreditsScreen::OnTwitter); #endif - root_->Add(new ImageView(ImageID("I_ICON"), IS_DEFAULT, new AnchorLayoutParams(100, 64, 10, 10, NONE, NONE, false))); + root_->Add(new ImageView(ImageID("I_ICON"), "", IS_DEFAULT, new AnchorLayoutParams(100, 64, 10, 10, NONE, NONE, false))); } UI::EventReturn CreditsScreen::OnSupport(UI::EventParams &e) { @@ -618,10 +623,13 @@ UI::EventReturn CreditsScreen::OnOK(UI::EventParams &e) { return UI::EVENT_DONE; } +CreditsScreen::CreditsScreen() { + startTime_ = time_now_d(); +} + void CreditsScreen::update() { UIScreen::update(); UpdateUIState(UISTATE_MENU); - frames_++; } void CreditsScreen::render() { @@ -701,7 +709,6 @@ void CreditsScreen::render() { "Kyhel", "xebra", "LunaMoo", - "AdamN/ANR2ME", "zminhquanz", "ANR2ME", "adenovan", @@ -778,7 +785,10 @@ void CreditsScreen::render() { const int numItems = ARRAY_SIZE(credits); int itemHeight = 36; int totalHeight = numItems * itemHeight + bounds.h + 200; - int y = bounds.y2() - (frames_ % totalHeight); + + float t = (float)(time_now_d() - startTime_) * 60.0; + + float y = bounds.y2() - fmodf(t, (float)totalHeight); for (int i = 0; i < numItems; i++) { float alpha = linearInOut(y+32, 64, bounds.y2() - 192, 64); uint32_t textColor = colorAlpha(dc.theme->infoStyle.fgColor, alpha); diff --git a/UI/MiscScreens.h b/UI/MiscScreens.h index 8c23ef3d43..730f8f7e24 100644 --- a/UI/MiscScreens.h +++ b/UI/MiscScreens.h @@ -126,8 +126,7 @@ private: class LogoScreen : public UIScreen { public: - LogoScreen(bool gotoGameSettings = false) - : gotoGameSettings_(gotoGameSettings) {} + LogoScreen(bool gotoGameSettings = false); bool key(const KeyInput &key) override; bool touch(const TouchInput &touch) override; void update() override; @@ -138,13 +137,14 @@ public: private: void Next(); int frames_ = 0; + double sinceStart_ = 0.0; bool switched_ = false; bool gotoGameSettings_ = false; }; class CreditsScreen : public UIDialogScreenWithBackground { public: - CreditsScreen() : frames_(0) {} + CreditsScreen(); void update() override; void render() override; @@ -160,5 +160,5 @@ private: UI::EventReturn OnShare(UI::EventParams &e); UI::EventReturn OnTwitter(UI::EventParams &e); - int frames_; + double startTime_ = 0.0; }; diff --git a/UI/NativeApp.cpp b/UI/NativeApp.cpp index 489c732f76..a8a863dda5 100644 --- a/UI/NativeApp.cpp +++ b/UI/NativeApp.cpp @@ -30,6 +30,7 @@ #include #include +#include #include #include #include @@ -132,7 +133,7 @@ Atlas g_ui_atlas; #include "../../android/jni/Arm64EmitterTest.h" #endif -#ifdef IOS +#if PPSSPP_PLATFORM(IOS) #include "ios/iOSCoreAudio.h" #elif defined(__APPLE__) #include @@ -148,7 +149,6 @@ static bool isOuya; static bool resized = false; static bool restarting = false; -static bool askedForStoragePermission = false; static int renderCounter = 0; struct PendingMessage { @@ -211,13 +211,13 @@ static LogListener *logger = nullptr; std::string boot_filename = ""; void NativeHost::InitSound() { -#ifdef IOS +#if PPSSPP_PLATFORM(IOS) iOSCoreAudioInit(); #endif } void NativeHost::ShutdownSound() { -#ifdef IOS +#if PPSSPP_PLATFORM(IOS) iOSCoreAudioShutdown(); #endif } @@ -323,7 +323,7 @@ static void PostLoadConfig() { if (g_Config.currentDirectory.empty()) { #if defined(__ANDROID__) g_Config.currentDirectory = g_Config.externalDirectory; -#elif defined(IOS) +#elif PPSSPP_PLATFORM(IOS) g_Config.currentDirectory = g_Config.internalDataDirectory; #elif PPSSPP_PLATFORM(SWITCH) g_Config.currentDirectory = "/"; @@ -454,12 +454,12 @@ void NativeInit(int argc, const char *argv[], const char *savegame_dir, const ch std::string user_data_path = savegame_dir; pendingMessages.clear(); pendingInputBoxes.clear(); -#ifdef IOS +#if PPSSPP_PLATFORM(IOS) user_data_path += "/"; #endif // We want this to be FIRST. -#if defined(IOS) +#if PPSSPP_PLATFORM(IOS) // Packed assets are included in app VFSRegister("", new DirectoryAssetReader(external_dir)); #endif @@ -492,6 +492,8 @@ void NativeInit(int argc, const char *argv[], const char *savegame_dir, const ch g_Config.externalDirectory = external_dir; #if defined(__ANDROID__) + // TODO: This needs to change in Android 12. + // // Maybe there should be an option to use internal memory instead, but I think // that for most people, using external memory (SDCard/USB Storage) makes the // most sense. @@ -506,7 +508,7 @@ void NativeInit(int argc, const char *argv[], const char *savegame_dir, const ch g_Config.memStickDirectory = memstickDir + "/"; } } -#elif defined(IOS) +#elif PPSSPP_PLATFORM(IOS) g_Config.memStickDirectory = user_data_path; g_Config.flash0Directory = std::string(external_dir) + "/flash0/"; #elif PPSSPP_PLATFORM(SWITCH) @@ -559,18 +561,24 @@ void NativeInit(int argc, const char *argv[], const char *savegame_dir, const ch // Parse command line LogTypes::LOG_LEVELS logLevel = LogTypes::LINFO; + bool forceLogLevel = false; + const auto setLogLevel = [&logLevel, &forceLogLevel](LogTypes::LOG_LEVELS level) { + logLevel = level; + forceLogLevel = true; + }; + for (int i = 1; i < argc; i++) { if (argv[i][0] == '-') { switch (argv[i][1]) { case 'd': // Enable debug logging // Note that you must also change the max log level in Log.h. - logLevel = LogTypes::LDEBUG; + setLogLevel(LogTypes::LDEBUG); break; case 'v': // Enable verbose logging // Note that you must also change the max log level in Log.h. - logLevel = LogTypes::LVERBOSE; + setLogLevel(LogTypes::LVERBOSE); break; case 'j': g_Config.iCpuCore = (int)CPUCore::JIT; @@ -585,6 +593,8 @@ void NativeInit(int argc, const char *argv[], const char *savegame_dir, const ch g_Config.bSaveSettings = false; break; case '-': + if (!strncmp(argv[i], "--loglevel=", strlen("--loglevel=")) && strlen(argv[i]) > strlen("--loglevel=")) + setLogLevel(static_cast(std::atoi(argv[i] + strlen("--loglevel=")))); if (!strncmp(argv[i], "--log=", strlen("--log=")) && strlen(argv[i]) > strlen("--log=")) fileToLog = argv[i] + strlen("--log="); if (!strncmp(argv[i], "--state=", strlen("--state=")) && strlen(argv[i]) > strlen("--state=")) @@ -665,6 +675,9 @@ void NativeInit(int argc, const char *argv[], const char *savegame_dir, const ch if (fileToLog) LogManager::GetInstance()->ChangeFileLog(fileToLog); + if (forceLogLevel) + LogManager::GetInstance()->SetAllLogLevels(logLevel); + PostLoadConfig(); #if PPSSPP_PLATFORM(ANDROID) @@ -683,6 +696,12 @@ void NativeInit(int argc, const char *argv[], const char *savegame_dir, const ch } } + if (System_GetPropertyBool(SYSPROP_CAN_JIT) == false && g_Config.iCpuCore == (int)CPUCore::JIT) { + // Just gonna force it to the IR interpreter on startup. + // We don't hide the option, but we make sure it's off on bootup. In case someone wants + // to experiment in future iOS versions or something... + g_Config.iCpuCore = (int)CPUCore::IR_JIT; + } auto des = GetI18NCategory("DesktopUI"); // Note to translators: do not translate this/add this to PPSSPP-lang's files. @@ -1048,6 +1067,9 @@ void RenderOverlays(UIContext *dc, void *userdata) { } void NativeRender(GraphicsContext *graphicsContext) { + _assert_(graphicsContext != nullptr); + _assert_(screenManager != nullptr); + g_GameManager.Update(); if (GetUIState() != UISTATE_INGAME) { diff --git a/UI/OnScreenDisplay.cpp b/UI/OnScreenDisplay.cpp index e63eb37d08..1b4700e9f6 100644 --- a/UI/OnScreenDisplay.cpp +++ b/UI/OnScreenDisplay.cpp @@ -1,3 +1,4 @@ +#include #include "UI/OnScreenDisplay.h" #include "Common/Data/Color/RGBAUtil.h" @@ -44,6 +45,18 @@ void OnScreenMessagesView::Draw(UIContext &dc) { osm.Unlock(); } +std::string OnScreenMessagesView::DescribeText() const { + std::stringstream ss; + const auto &messages = osm.Messages(); + for (auto iter = messages.begin(); iter != messages.end(); ++iter) { + if (iter != messages.begin()) { + ss << "\n"; + } + ss << iter->text; + } + return ss.str(); +} + void OnScreenMessages::Clean() { restart: double now = time_now_d(); diff --git a/UI/OnScreenDisplay.h b/UI/OnScreenDisplay.h index 6cb83aa877..7a56c0b08e 100644 --- a/UI/OnScreenDisplay.h +++ b/UI/OnScreenDisplay.h @@ -42,7 +42,8 @@ private: class OnScreenMessagesView : public UI::InertView { public: OnScreenMessagesView(UI::LayoutParams *layoutParams = nullptr) : UI::InertView(layoutParams) {} - void Draw(UIContext &dc); + void Draw(UIContext &dc) override; + std::string DescribeText() const override; }; extern OnScreenMessages osm; diff --git a/UI/PauseScreen.cpp b/UI/PauseScreen.cpp index 4573cd4e3e..51a43dae9b 100644 --- a/UI/PauseScreen.cpp +++ b/UI/PauseScreen.cpp @@ -333,7 +333,7 @@ void GamePauseScreen::CreateViews() { ViewGroup *leftColumn = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(1.0, scrollMargins)); root_->Add(leftColumn); - LinearLayout *leftColumnItems = new LinearLayout(ORIENT_VERTICAL, new LayoutParams(FILL_PARENT, WRAP_CONTENT)); + LinearLayout *leftColumnItems = new LinearLayoutList(ORIENT_VERTICAL, new LayoutParams(FILL_PARENT, WRAP_CONTENT)); leftColumn->Add(leftColumnItems); leftColumnItems->Add(new Spacer(0.0)); diff --git a/UI/PauseScreen.h b/UI/PauseScreen.h index 69c84d53af..926302c141 100644 --- a/UI/PauseScreen.h +++ b/UI/PauseScreen.h @@ -69,6 +69,7 @@ public: void GetContentDimensionsBySpec(const UIContext &dc, UI::MeasureSpec horiz, UI::MeasureSpec vert, float &w, float &h) const override; void Draw(UIContext &dc) override; + std::string DescribeText() const override { return text_; } void DeviceLost() override; void DeviceRestored(Draw::DrawContext *draw) override; diff --git a/UI/ProfilerDraw.cpp b/UI/ProfilerDraw.cpp index c59cc6e3b9..5c7b1bce3d 100644 --- a/UI/ProfilerDraw.cpp +++ b/UI/ProfilerDraw.cpp @@ -26,6 +26,7 @@ #include "Common/Profiler/Profiler.h" #include "StringUtils.h" +#ifdef USE_PROFILER static const uint32_t nice_colors[] = { 0xFF8040, 0x80FF40, @@ -47,6 +48,7 @@ static const uint32_t nice_colors[] = { 0xF8F8F8, 0x33FFFF, }; +#endif enum ProfileCatStatus { PROFILE_CAT_VISIBLE = 0, diff --git a/UI/RemoteISOScreen.cpp b/UI/RemoteISOScreen.cpp index 8dfabde16a..6ce5950fda 100644 --- a/UI/RemoteISOScreen.cpp +++ b/UI/RemoteISOScreen.cpp @@ -593,7 +593,7 @@ void RemoteISOSettingsScreen::CreateViews() { ViewGroup *remoteisoSettingsScroll = new ScrollView(ORIENT_VERTICAL, new LayoutParams(FILL_PARENT, FILL_PARENT)); remoteisoSettingsScroll->SetTag("RemoteISOSettings"); - LinearLayout *remoteisoSettings = new LinearLayout(ORIENT_VERTICAL); + LinearLayout *remoteisoSettings = new LinearLayoutList(ORIENT_VERTICAL); remoteisoSettings->SetSpacing(0); remoteisoSettingsScroll->Add(remoteisoSettings); diff --git a/UI/ReportScreen.cpp b/UI/ReportScreen.cpp index 1c31856a26..a5cf54e098 100644 --- a/UI/ReportScreen.cpp +++ b/UI/ReportScreen.cpp @@ -195,6 +195,7 @@ void ReportScreen::update() { } } UIDialogScreenWithGameBackground::update(); + UpdateCRCInfo(); } void ReportScreen::resized() { @@ -269,7 +270,7 @@ void ReportScreen::CreateViews() { } #ifdef MOBILE_DEVICE - if (!Core_GetPowerSaving()) { + if (!Core_GetPowerSaving() && !Reporting::HasCRC(gamePath_)) { auto crcWarning = new TextView(rp->T("FeedbackIncludeCRC", "Note: Battery will be used to send a disc CRC"), FLAG_WRAP_TEXT, false, new LinearLayoutParams(Margins(12, 5, 0, 5))); crcWarning->SetShadow(true); crcWarning->SetEnabledPtr(&enableReporting_); @@ -277,6 +278,11 @@ void ReportScreen::CreateViews() { } #endif + crcInfo_ = new TextView("", FLAG_WRAP_TEXT, false, new LinearLayoutParams(Margins(12, 5, 0, 5))); + crcInfo_->SetShadow(true); + crcInfo_->SetVisibility(V_GONE); + leftColumnItems->Add(crcInfo_); + if (tookScreenshot_ && !screenshotFilename_.empty()) { leftColumnItems->Add(new CheckBox(&includeScreenshot_, rp->T("FeedbackIncludeScreen", "Include a screenshot")))->SetEnabledPtr(&enableReporting_); screenshot_ = leftColumnItems->Add(new AsyncImageFileView(screenshotFilename_, IS_KEEP_ASPECT, nullptr, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT, Margins(12, 0)))); @@ -292,7 +298,7 @@ void ReportScreen::CreateViews() { overallDescription_->SetShadow(true); UI::Orientation ratingsOrient = leftColumnWidth >= 750.0f ? ORIENT_HORIZONTAL : ORIENT_VERTICAL; - UI::LinearLayout *ratingsHolder = new LinearLayout(ratingsOrient, new LinearLayoutParams(WRAP_CONTENT, WRAP_CONTENT)); + UI::LinearLayout *ratingsHolder = new LinearLayoutList(ratingsOrient, new LinearLayoutParams(WRAP_CONTENT, WRAP_CONTENT)); leftColumnItems->Add(ratingsHolder); ratingsHolder->Add(new RatingChoice("Graphics", &graphics_))->SetEnabledPtr(&ratingEnabled_)->OnChoice.Handle(this, &ReportScreen::HandleChoice); ratingsHolder->Add(new RatingChoice("Speed", &speed_))->SetEnabledPtr(&ratingEnabled_)->OnChoice.Handle(this, &ReportScreen::HandleChoice); @@ -300,6 +306,8 @@ void ReportScreen::CreateViews() { rightColumnItems->SetSpacing(0.0f); rightColumnItems->Add(new Choice(rp->T("Open Browser")))->OnClick.Handle(this, &ReportScreen::HandleBrowser); + showCrcButton_ = new Choice(rp->T("Show disc CRC")); + rightColumnItems->Add(showCrcButton_)->OnClick.Handle(this, &ReportScreen::HandleShowCRC); submit_ = new Choice(rp->T("Submit Feedback")); rightColumnItems->Add(submit_)->OnClick.Handle(this, &ReportScreen::HandleSubmit); UpdateSubmit(); @@ -314,12 +322,32 @@ void ReportScreen::CreateViews() { leftColumn->Add(leftColumnItems); rightColumn->Add(rightColumnItems); + + UpdateCRCInfo(); } void ReportScreen::UpdateSubmit() { submit_->SetEnabled(enableReporting_ && overall_ != ReportingOverallScore::INVALID && graphics_ >= 0 && speed_ >= 0 && gameplay_ >= 0); } +void ReportScreen::UpdateCRCInfo() { + auto rp = GetI18NCategory("Reporting"); + std::string updated; + + if (Reporting::HasCRC(gamePath_)) { + std::string crc = StringFromFormat("%08X", Reporting::RetrieveCRC(gamePath_)); + updated = ReplaceAll(rp->T("FeedbackCRCValue", "Disc CRC: [VALUE]"), "[VALUE]", crc); + } else if (showCRC_) { + updated = rp->T("FeedbackCRCCalculating", "Disc CRC: Calculating..."); + } + + if (!updated.empty()) { + crcInfo_->SetText(updated); + crcInfo_->SetVisibility(V_VISIBLE); + showCrcButton_->SetEnabled(false); + } +} + void ReportScreen::UpdateOverallDescription() { auto rp = GetI18NCategory("Reporting"); const char *desc; @@ -366,6 +394,12 @@ EventReturn ReportScreen::HandleBrowser(EventParams &e) { return EVENT_DONE; } +EventReturn ReportScreen::HandleShowCRC(EventParams &e) { + Reporting::QueueCRC(gamePath_); + showCRC_ = true; + return EVENT_DONE; +} + ReportFinishScreen::ReportFinishScreen(const std::string &gamePath, ReportingOverallScore score) : UIDialogScreenWithGameBackground(gamePath), score_(score) { } diff --git a/UI/ReportScreen.h b/UI/ReportScreen.h index b77f8a082f..4a99dd5739 100644 --- a/UI/ReportScreen.h +++ b/UI/ReportScreen.h @@ -42,17 +42,21 @@ protected: void resized() override; void CreateViews() override; void UpdateSubmit(); + void UpdateCRCInfo(); void UpdateOverallDescription(); UI::EventReturn HandleChoice(UI::EventParams &e); UI::EventReturn HandleSubmit(UI::EventParams &e); UI::EventReturn HandleBrowser(UI::EventParams &e); + UI::EventReturn HandleShowCRC(UI::EventParams &e); UI::EventReturn HandleReportingChange(UI::EventParams &e); UI::Choice *submit_ = nullptr; UI::View *screenshot_ = nullptr; UI::TextView *reportingNotice_ = nullptr; UI::TextView *overallDescription_ = nullptr; + UI::TextView *crcInfo_ = nullptr; + UI::Choice *showCrcButton_ = nullptr; std::string screenshotFilename_; ReportingOverallScore overall_ = ReportingOverallScore::INVALID; @@ -63,6 +67,7 @@ protected: bool ratingEnabled_; bool tookScreenshot_ = false; bool includeScreenshot_ = true; + bool showCRC_ = false; }; class ReportFinishScreen : public UIDialogScreenWithGameBackground { diff --git a/UI/SavedataScreen.cpp b/UI/SavedataScreen.cpp index 3d52c42410..245897b8f5 100644 --- a/UI/SavedataScreen.cpp +++ b/UI/SavedataScreen.cpp @@ -20,10 +20,12 @@ #include "Common/Data/Color/RGBAUtil.h" #include "Common/Render/DrawBuffer.h" +#include "Common/Data/Encoding/Utf8.h" #include "Common/Data/Text/I18n.h" #include "Common/Math/curves.h" +#include "Common/System/NativeApp.h" +#include "Common/System/System.h" #include "Common/Thread/PrioritizedWorkQueue.h" -#include "Common/Data/Encoding/Utf8.h" #include "Common/UI/Context.h" #include "Common/UI/View.h" #include "Common/UI/ViewGroup.h" @@ -135,13 +137,13 @@ private: std::string savePath_; }; -class SortedLinearLayout : public UI::LinearLayout { +class SortedLinearLayout : public UI::LinearLayoutList { public: typedef std::function CompareFunc; typedef std::function DoneFunc; SortedLinearLayout(UI::Orientation orientation, UI::LayoutParams *layoutParams = nullptr) - : UI::LinearLayout(orientation, layoutParams) { + : UI::LinearLayoutList(orientation, layoutParams) { } void SetCompare(CompareFunc lessFunc, DoneFunc doneFunc) { @@ -174,6 +176,8 @@ public: } void Draw(UIContext &dc) override; + bool UpdateText(); + std::string DescribeText() const override; void GetContentDimensions(const UIContext &dc, float &w, float &h) const override { w = 500; h = 74; @@ -182,6 +186,8 @@ public: const std::string &GamePath() const { return savePath_; } private: + void UpdateText(const std::shared_ptr &ginfo); + std::string savePath_; std::string title_; std::string subtitle_; @@ -201,6 +207,26 @@ static std::string CleanSaveString(std::string str) { return s; } +bool SavedataButton::UpdateText() { + std::shared_ptr ginfo = g_gameInfoCache->GetInfo(nullptr, savePath_, GAMEINFO_WANTSIZE); + if (!ginfo->pending) { + UpdateText(ginfo); + return true; + } + return false; +} + +void SavedataButton::UpdateText(const std::shared_ptr &ginfo) { + const std::string currentTitle = ginfo->GetTitle(); + if (!currentTitle.empty()) { + title_ = CleanSaveString(currentTitle); + } + if (subtitle_.empty() && ginfo->gameSize > 0) { + std::string savedata_title = ginfo->paramSFO.GetValueString("SAVEDATA_TITLE"); + subtitle_ = CleanSaveString(savedata_title) + StringFromFormat(" (%lld kB)", ginfo->gameSize / 1024); + } +} + void SavedataButton::Draw(UIContext &dc) { std::shared_ptr ginfo = g_gameInfoCache->GetInfo(dc.GetDrawContext(), savePath_, GAMEINFO_WANTSIZE); Draw::Texture *texture = 0; @@ -286,15 +312,7 @@ void SavedataButton::Draw(UIContext &dc) { dc.Draw()->Flush(); dc.PushScissor(bounds_); - const std::string currentTitle = ginfo->GetTitle(); - if (!currentTitle.empty()) { - title_ = CleanSaveString(currentTitle); - } - if (subtitle_.empty() && ginfo->gameSize > 0) { - std::string savedata_title = ginfo->paramSFO.GetValueString("SAVEDATA_TITLE"); - subtitle_ = CleanSaveString(savedata_title) + StringFromFormat(" (%lld kB)", ginfo->gameSize / 1024); - } - + UpdateText(ginfo); dc.MeasureText(dc.GetFontStyle(), 1.0f, 1.0f, title_.c_str(), &tw, &th, 0); int availableWidth = bounds_.w - 150; @@ -323,11 +341,68 @@ void SavedataButton::Draw(UIContext &dc) { dc.RebindTexture(); } +std::string SavedataButton::DescribeText() const { + auto u = GetI18NCategory("UI Elements"); + return ReplaceAll(u->T("%1 button"), "%1", title_) + "\n" + subtitle_; +} + SavedataBrowser::SavedataBrowser(std::string path, UI::LayoutParams *layoutParams) : LinearLayout(UI::ORIENT_VERTICAL, layoutParams), path_(path) { Refresh(); } +void SavedataBrowser::Update() { + if (searchPending_) { + searchPending_ = false; + + int n = gameList_->GetNumSubviews(); + bool matches = searchFilter_.empty(); + for (int i = 0; i < n; ++i) { + SavedataButton *v = static_cast(gameList_->GetViewByIndex(i)); + + // Note: might be resetting to empty string. Can do that right away. + if (searchFilter_.empty()) { + v->SetVisibility(UI::V_VISIBLE); + continue; + } + + if (!v->UpdateText()) { + // We'll need to wait until the text is loaded. + searchPending_ = true; + v->SetVisibility(UI::V_GONE); + continue; + } + + std::string label = v->DescribeText(); + std::transform(label.begin(), label.end(), label.begin(), tolower); + bool match = label.find(searchFilter_) != label.npos; + matches = matches || match; + v->SetVisibility(match ? UI::V_VISIBLE : UI::V_GONE); + } + + if (searchingView_) { + bool show = !searchFilter_.empty() && (matches || searchPending_); + searchingView_->SetVisibility(show ? UI::V_VISIBLE : UI::V_GONE); + } + if (noMatchView_) + noMatchView_->SetVisibility(matches || searchPending_ ? UI::V_GONE : UI::V_VISIBLE); + } +} + +void SavedataBrowser::SetSearchFilter(const std::string &filter) { + auto sa = GetI18NCategory("Savedata"); + + searchFilter_.resize(filter.size()); + std::transform(filter.begin(), filter.end(), searchFilter_.begin(), tolower); + + if (gameList_) + searchPending_ = true; + if (noMatchView_) + noMatchView_->SetText(ReplaceAll(sa->T("Nothing matching '%1' was found."), "%1", filter)); + if (searchingView_) + searchingView_->SetText(ReplaceAll(sa->T("Showing matches for '%1'."), "%1", filter)); +} + void SavedataBrowser::SetSortOption(SavedataSortOption opt) { sortOption_ = opt; if (gameList_) { @@ -409,16 +484,10 @@ void SavedataBrowser::Refresh() { auto mm = GetI18NCategory("MainMenu"); auto sa = GetI18NCategory("Savedata"); - SortedLinearLayout *gl = new SortedLinearLayout(UI::ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT)); - gl->SetSpacing(4.0f); - gameList_ = gl; - Add(gameList_); - // Find games in the current directory and create new ones. std::vector savedataButtons; std::vector fileInfo; - getFilesInDir(path_.c_str(), &fileInfo, "ppst:"); for (size_t i = 0; i < fileInfo.size(); i++) { @@ -433,19 +502,35 @@ void SavedataBrowser::Refresh() { } } - for (size_t i = 0; i < savedataButtons.size(); i++) { - SavedataButton *b = gameList_->Add(savedataButtons[i]); - b->OnClick.Handle(this, &SavedataBrowser::SavedataButtonClick); - } + ViewGroup *group = new LinearLayout(ORIENT_VERTICAL, new UI::LinearLayoutParams(UI::Margins(12, 0))); + Add(group); if (savedataButtons.empty()) { - ViewGroup *group = new LinearLayout(ORIENT_VERTICAL, new UI::LinearLayoutParams(UI::Margins(12, 0))); - group->Add(new TextView(sa->T("None yet. Things will appear here after you save."))); - gameList_->Add(group); + group->Add(new TextView(sa->T("None yet. Things will appear here after you save."))); + gameList_ = nullptr; + noMatchView_ = nullptr; + searchingView_ = nullptr; + } else { + noMatchView_ = group->Add(new TextView(sa->T("Nothing matching '%1' was found"))); + noMatchView_->SetVisibility(UI::V_GONE); + searchingView_ = group->Add(new TextView(sa->T("Showing matches for '%1'"))); + searchingView_->SetVisibility(UI::V_GONE); + + SortedLinearLayout *gl = new SortedLinearLayout(UI::ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT)); + gl->SetSpacing(4.0f); + gameList_ = gl; + Add(gameList_); + + for (size_t i = 0; i < savedataButtons.size(); i++) { + SavedataButton *b = gameList_->Add(savedataButtons[i]); + b->OnClick.Handle(this, &SavedataBrowser::SavedataButtonClick); + } } // Reapply. SetSortOption(sortOption_); + if (!searchFilter_.empty()) + SetSearchFilter(searchFilter_); } UI::EventReturn SavedataBrowser::SavedataButtonClick(UI::EventParams &e) { @@ -478,7 +563,8 @@ void SavedataScreen::CreateViews() { gridStyle_ = false; root_ = new AnchorLayout(); - LinearLayout *main = new LinearLayout(ORIENT_VERTICAL, new AnchorLayoutParams(FILL_PARENT, FILL_PARENT)); + // Make space for buttons. + LinearLayout *main = new LinearLayout(ORIENT_VERTICAL, new AnchorLayoutParams(FILL_PARENT, FILL_PARENT, 0, 0, 0, 84.0f)); TabHolder *tabs = new TabHolder(ORIENT_HORIZONTAL, 64, new LinearLayoutParams(FILL_PARENT, FILL_PARENT, 1.0f)); tabs->SetTag("Savedata"); @@ -486,6 +572,8 @@ void SavedataScreen::CreateViews() { scroll->SetTag("SavedataBrowser"); dataBrowser_ = scroll->Add(new SavedataBrowser(savedata_dir, new LayoutParams(FILL_PARENT, FILL_PARENT))); dataBrowser_->SetSortOption(sortOption_); + if (!searchFilter_.empty()) + dataBrowser_->SetSearchFilter(searchFilter_); dataBrowser_->OnChoice.Handle(this, &SavedataScreen::OnSavedataButtonClick); tabs->AddTab(sa->T("Save Data"), scroll); @@ -494,19 +582,25 @@ void SavedataScreen::CreateViews() { scroll2->SetTag("SavedataStatesBrowser"); stateBrowser_ = scroll2->Add(new SavedataBrowser(savestate_dir)); stateBrowser_->SetSortOption(sortOption_); + if (!searchFilter_.empty()) + stateBrowser_->SetSearchFilter(searchFilter_); stateBrowser_->OnChoice.Handle(this, &SavedataScreen::OnSavedataButtonClick); tabs->AddTab(sa->T("Save States"), scroll2); main->Add(tabs); - main->Add(new Button(di->T("Back"), new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT, 0.0f)))->OnClick.Handle(this, &UIScreen::OnBack); ChoiceStrip *sortStrip = new ChoiceStrip(ORIENT_HORIZONTAL, new AnchorLayoutParams(NONE, 0, 0, NONE)); sortStrip->AddChoice(sa->T("Filename")); sortStrip->AddChoice(sa->T("Size")); sortStrip->AddChoice(sa->T("Date")); - sortStrip->SetSelection((int)sortOption_); + sortStrip->SetSelection((int)sortOption_, false); sortStrip->OnChoice.Handle(this, &SavedataScreen::OnSortClick); + AddStandardBack(root_); +#if PPSSPP_PLATFORM(WINDOWS) || defined(USING_QT_UI) || defined(__ANDROID__) + root_->Add(new Choice(di->T("Search"), "", false, new AnchorLayoutParams(WRAP_CONTENT, 64, NONE, NONE, 10, 10)))->OnClick.Handle(this, &SavedataScreen::OnSearch); +#endif + root_->Add(main); root_->Add(sortStrip); } @@ -520,6 +614,18 @@ UI::EventReturn SavedataScreen::OnSortClick(UI::EventParams &e) { return UI::EVENT_DONE; } +UI::EventReturn SavedataScreen::OnSearch(UI::EventParams &e) { + auto di = GetI18NCategory("Dialog"); +#if PPSSPP_PLATFORM(WINDOWS) || defined(USING_QT_UI) || defined(__ANDROID__) + System_InputBoxGetString(di->T("Filter"), searchFilter_, [](bool result, const std::string &value) { + if (result) { + NativeMessageReceived("savedatascreen_search", value.c_str()); + } + }); +#endif + return UI::EVENT_DONE; +} + UI::EventReturn SavedataScreen::OnSavedataButtonClick(UI::EventParams &e) { std::shared_ptr ginfo = g_gameInfoCache->GetInfo(screenManager()->getDrawContext(), e.s, 0); SavedataPopupScreen *popupScreen = new SavedataPopupScreen(e.s, ginfo->GetTitle()); @@ -536,3 +642,12 @@ void SavedataScreen::dialogFinished(const Screen *dialog, DialogResult result) { RecreateViews(); } } + +void SavedataScreen::sendMessage(const char *message, const char *value) { + UIDialogScreenWithGameBackground::sendMessage(message, value); + if (!strcmp(message, "savedatascreen_search")) { + searchFilter_ = value; + dataBrowser_->SetSearchFilter(searchFilter_); + stateBrowser_->SetSearchFilter(searchFilter_); + } +} diff --git a/UI/SavedataScreen.h b/UI/SavedataScreen.h index 9e02f8c9cb..c0909bf321 100644 --- a/UI/SavedataScreen.h +++ b/UI/SavedataScreen.h @@ -36,7 +36,10 @@ class SavedataBrowser : public UI::LinearLayout { public: SavedataBrowser(std::string path, UI::LayoutParams *layoutParams = 0); + void Update() override; + void SetSortOption(SavedataSortOption opt); + void SetSearchFilter(const std::string &filter); UI::Event OnChoice; @@ -51,7 +54,11 @@ private: SavedataSortOption sortOption_ = SavedataSortOption::FILENAME; UI::ViewGroup *gameList_ = nullptr; + UI::TextView *noMatchView_ = nullptr; + UI::TextView *searchingView_ = nullptr; std::string path_; + std::string searchFilter_; + bool searchPending_ = false; }; class SavedataScreen : public UIDialogScreenWithGameBackground { @@ -61,13 +68,17 @@ public: ~SavedataScreen(); void dialogFinished(const Screen *dialog, DialogResult result) override; + void sendMessage(const char *message, const char *value) override; protected: UI::EventReturn OnSavedataButtonClick(UI::EventParams &e); UI::EventReturn OnSortClick(UI::EventParams &e); + UI::EventReturn OnSearch(UI::EventParams &e); void CreateViews() override; + bool gridStyle_; SavedataSortOption sortOption_ = SavedataSortOption::FILENAME; SavedataBrowser *dataBrowser_; SavedataBrowser *stateBrowser_; + std::string searchFilter_; }; diff --git a/UI/Store.cpp b/UI/Store.cpp index b7601bf927..477cb7526d 100644 --- a/UI/Store.cpp +++ b/UI/Store.cpp @@ -62,6 +62,7 @@ public: void GetContentDimensions(const UIContext &dc, float &w, float &h) const override; void Draw(UIContext &dc) override; + std::string DescribeText() const override { return ""; } void SetFilename(std::string filename); void SetColor(uint32_t color) { color_ = color; } @@ -456,7 +457,7 @@ void StoreScreen::CreateViews() { ScrollView *leftScroll = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(WRAP_CONTENT, FILL_PARENT, 0.4f)); leftScroll->SetTag("StoreMainList"); content->Add(leftScroll); - scrollItemView_ = new LinearLayout(ORIENT_VERTICAL, new LayoutParams(FILL_PARENT, WRAP_CONTENT)); + scrollItemView_ = new LinearLayoutList(ORIENT_VERTICAL, new LayoutParams(FILL_PARENT, WRAP_CONTENT)); leftScroll->Add(scrollItemView_); std::vector entries = FilterEntries(); diff --git a/UI/TextureUtil.cpp b/UI/TextureUtil.cpp index cdfe9440e2..3d986e765a 100644 --- a/UI/TextureUtil.cpp +++ b/UI/TextureUtil.cpp @@ -124,25 +124,26 @@ bool ManagedTexture::LoadFromFileData(const uint8_t *data, size_t dataSize, Imag } int potentialLevels = std::min(log2i(width[0]), log2i(height[0])); - - TextureDesc desc{}; - desc.type = TextureType::LINEAR2D; - desc.format = fmt; - desc.width = width[0]; - desc.height = height[0]; - desc.depth = 1; - desc.mipLevels = generateMips ? potentialLevels : num_levels; - desc.generateMips = generateMips && potentialLevels > num_levels; - desc.tag = name; - for (int i = 0; i < num_levels; i++) { - desc.initData.push_back(image[i]); + if (width[0] > 0 && height[0] > 0) { + TextureDesc desc{}; + desc.type = TextureType::LINEAR2D; + desc.format = fmt; + desc.width = width[0]; + desc.height = height[0]; + desc.depth = 1; + desc.mipLevels = generateMips ? potentialLevels : num_levels; + desc.generateMips = generateMips && potentialLevels > num_levels; + desc.tag = name; + for (int i = 0; i < num_levels; i++) { + desc.initData.push_back(image[i]); + } + texture_ = draw_->CreateTexture(desc); } - texture_ = draw_->CreateTexture(desc); for (int i = 0; i < num_levels; i++) { if (image[i]) free(image[i]); } - return texture_; + return texture_ != nullptr; } bool ManagedTexture::LoadFromFile(const std::string &filename, ImageFileType type, bool generateMips) { diff --git a/UI/TextureUtil.h b/UI/TextureUtil.h index 5a4710a65a..e2d0210ae9 100644 --- a/UI/TextureUtil.h +++ b/UI/TextureUtil.h @@ -49,6 +49,7 @@ public: void GetContentDimensions(const UIContext &dc, float &w, float &h) const override; void Draw(UIContext &dc) override; + std::string DescribeText() const override { return ""; } private: std::string gamePath_; diff --git a/UI/TiltAnalogSettingsScreen.cpp b/UI/TiltAnalogSettingsScreen.cpp index e4379606e7..99e2a7a4f6 100644 --- a/UI/TiltAnalogSettingsScreen.cpp +++ b/UI/TiltAnalogSettingsScreen.cpp @@ -29,7 +29,7 @@ void TiltAnalogSettingsScreen::CreateViews() { root_ = new ScrollView(ORIENT_VERTICAL); root_->SetTag("TiltAnalogSettings"); - LinearLayout *settings = new LinearLayout(ORIENT_VERTICAL); + LinearLayout *settings = new LinearLayoutList(ORIENT_VERTICAL); settings->SetSpacing(0); settings->Add(new ItemHeader(co->T("Invert Axes"))); diff --git a/UI/TouchControlLayoutScreen.cpp b/UI/TouchControlLayoutScreen.cpp index c180541833..25a5a07df0 100644 --- a/UI/TouchControlLayoutScreen.cpp +++ b/UI/TouchControlLayoutScreen.cpp @@ -38,8 +38,8 @@ static u32 GetButtonColor() { class DragDropButton : public MultiTouchButton { public: - DragDropButton(ConfigTouchPos &pos, ImageID bgImg, ImageID img, const Bounds &screenBounds) - : MultiTouchButton(bgImg, bgImg, img, pos.scale, new UI::AnchorLayoutParams(pos.x * screenBounds.w, pos.y * screenBounds.h, UI::NONE, UI::NONE, true)), + DragDropButton(ConfigTouchPos &pos, const char *key, ImageID bgImg, ImageID img, const Bounds &screenBounds) + : MultiTouchButton(key, bgImg, bgImg, img, pos.scale, new UI::AnchorLayoutParams(pos.x * screenBounds.w, pos.y * screenBounds.h, UI::NONE, UI::NONE, true)), x_(pos.x), y_(pos.y), theScale_(pos.scale), screenBounds_(screenBounds) { scale_ = theScale_; } @@ -76,8 +76,8 @@ private: class PSPActionButtons : public DragDropButton { public: - PSPActionButtons(ConfigTouchPos &pos, float &spacing, const Bounds &screenBounds) - : DragDropButton(pos, ImageID::invalid(), ImageID::invalid(), screenBounds), spacing_(spacing) { + PSPActionButtons(ConfigTouchPos &pos, const char *key, float &spacing, const Bounds &screenBounds) + : DragDropButton(pos, key, ImageID::invalid(), ImageID::invalid(), screenBounds), spacing_(spacing) { using namespace UI; roundId_ = g_Config.iTouchButtonStyle ? ImageID("I_ROUND_LINE") : ImageID("I_ROUND"); }; @@ -155,8 +155,8 @@ private: class PSPDPadButtons : public DragDropButton { public: - PSPDPadButtons(ConfigTouchPos &pos, float &spacing, const Bounds &screenBounds) - : DragDropButton(pos, ImageID::invalid(), ImageID::invalid(), screenBounds), spacing_(spacing) { + PSPDPadButtons(ConfigTouchPos &pos, const char *key, float &spacing, const Bounds &screenBounds) + : DragDropButton(pos, key, ImageID::invalid(), ImageID::invalid(), screenBounds), spacing_(spacing) { } void Draw(UIContext &dc) override { @@ -221,6 +221,10 @@ public: } } + std::string DescribeText() const override { + return ""; + } + private: int x1, x2, y1, y2; u32 col; @@ -338,7 +342,7 @@ void ControlLayoutView::CreateViews() { // Create all the views. - PSPActionButtons *actionButtons = new PSPActionButtons(g_Config.touchActionButtonCenter, g_Config.fActionButtonSpacing, bounds); + PSPActionButtons *actionButtons = new PSPActionButtons(g_Config.touchActionButtonCenter, "Action buttons", g_Config.fActionButtonSpacing, bounds); actionButtons->setCircleVisibility(g_Config.bShowTouchCircle); actionButtons->setCrossVisibility(g_Config.bShowTouchCross); actionButtons->setTriangleVisibility(g_Config.bShowTouchTriangle); @@ -348,60 +352,59 @@ void ControlLayoutView::CreateViews() { ImageID rectImage = g_Config.iTouchButtonStyle ? ImageID("I_RECT_LINE") : ImageID("I_RECT"); ImageID shoulderImage = g_Config.iTouchButtonStyle ? ImageID("I_SHOULDER_LINE") : ImageID("I_SHOULDER"); - ImageID dirImage = g_Config.iTouchButtonStyle ? ImageID("I_DIR_LINE") : ImageID("I_DIR"); ImageID stickImage = g_Config.iTouchButtonStyle ? ImageID("I_STICK_LINE") : ImageID("I_STICK"); ImageID stickBg = g_Config.iTouchButtonStyle ? ImageID("I_STICK_BG_LINE") : ImageID("I_STICK_BG"); ImageID roundImage = g_Config.iTouchButtonStyle ? ImageID("I_ROUND_LINE") : ImageID("I_ROUND"); const ImageID comboKeyImages[5] = { ImageID("I_1"), ImageID("I_2"), ImageID("I_3"), ImageID("I_4"), ImageID("I_5") }; - auto addDragDropButton = [&](ConfigTouchPos &pos, ImageID bgImg, ImageID img) { + auto addDragDropButton = [&](ConfigTouchPos &pos, const char *key, ImageID bgImg, ImageID img) { DragDropButton *b = nullptr; if (pos.show) { - b = new DragDropButton(pos, bgImg, img, bounds); + b = new DragDropButton(pos, key, bgImg, img, bounds); controls_.push_back(b); } return b; }; if (g_Config.touchDpad.show) { - controls_.push_back(new PSPDPadButtons(g_Config.touchDpad, g_Config.fDpadSpacing, bounds)); + controls_.push_back(new PSPDPadButtons(g_Config.touchDpad, "D-pad", g_Config.fDpadSpacing, bounds)); } - addDragDropButton(g_Config.touchSelectKey, rectImage, ImageID("I_SELECT")); - addDragDropButton(g_Config.touchStartKey, rectImage, ImageID("I_START")); + addDragDropButton(g_Config.touchSelectKey, "Select button", rectImage, ImageID("I_SELECT")); + addDragDropButton(g_Config.touchStartKey, "Start button", rectImage, ImageID("I_START")); - if (auto *unthrottle = addDragDropButton(g_Config.touchUnthrottleKey, rectImage, ImageID("I_ARROW"))) { + if (auto *unthrottle = addDragDropButton(g_Config.touchUnthrottleKey, "Unthrottle button", rectImage, ImageID("I_ARROW"))) { unthrottle->SetAngle(180.0f); } - if (auto *speed1 = addDragDropButton(g_Config.touchSpeed1Key, rectImage, ImageID("I_ARROW"))) { + if (auto *speed1 = addDragDropButton(g_Config.touchSpeed1Key, "Alternate speed 1 button", rectImage, ImageID("I_ARROW"))) { speed1->SetAngle(170.0f, 180.0f); } - if (auto *speed2 = addDragDropButton(g_Config.touchSpeed2Key, rectImage, ImageID("I_ARROW"))) { + if (auto *speed2 = addDragDropButton(g_Config.touchSpeed2Key, "Alternate speed 2 button", rectImage, ImageID("I_ARROW"))) { speed2->SetAngle(190.0f, 180.0f); } - if (auto *rapidFire = addDragDropButton(g_Config.touchRapidFireKey, rectImage, ImageID("I_ARROW"))) { + if (auto *rapidFire = addDragDropButton(g_Config.touchRapidFireKey, "Rapid fire button", rectImage, ImageID("I_ARROW"))) { rapidFire->SetAngle(90.0f, 180.0f); } - if (auto *analogRotationCW = addDragDropButton(g_Config.touchAnalogRotationCWKey, rectImage, ImageID("I_ARROW"))) { + if (auto *analogRotationCW = addDragDropButton(g_Config.touchAnalogRotationCWKey, "Analog clockwise rotation button", rectImage, ImageID("I_ARROW"))) { analogRotationCW->SetAngle(190.0f, 180.0f); } - if (auto *analogRotationCCW = addDragDropButton(g_Config.touchAnalogRotationCCWKey, rectImage, ImageID("I_ARROW"))) { + if (auto *analogRotationCCW = addDragDropButton(g_Config.touchAnalogRotationCCWKey, "Analog counter clockwise rotation button", rectImage, ImageID("I_ARROW"))) { analogRotationCCW->SetAngle(350.0f, 180.0f); } - addDragDropButton(g_Config.touchLKey, shoulderImage, ImageID("I_L")); - if (auto *rbutton = addDragDropButton(g_Config.touchRKey, shoulderImage, ImageID("I_R"))) { + addDragDropButton(g_Config.touchLKey, "Left shoulder button", shoulderImage, ImageID("I_L")); + if (auto *rbutton = addDragDropButton(g_Config.touchRKey, "Right shoulder button", shoulderImage, ImageID("I_R"))) { rbutton->FlipImageH(true); } - addDragDropButton(g_Config.touchAnalogStick, stickBg, stickImage); - addDragDropButton(g_Config.touchRightAnalogStick, stickBg, stickImage); - addDragDropButton(g_Config.touchCombo0, roundImage, comboKeyImages[0]); - addDragDropButton(g_Config.touchCombo1, roundImage, comboKeyImages[1]); - addDragDropButton(g_Config.touchCombo2, roundImage, comboKeyImages[2]); - addDragDropButton(g_Config.touchCombo3, roundImage, comboKeyImages[3]); - addDragDropButton(g_Config.touchCombo4, roundImage, comboKeyImages[4]); + addDragDropButton(g_Config.touchAnalogStick, "Left analog stick", stickBg, stickImage); + addDragDropButton(g_Config.touchRightAnalogStick, "Right analog stick", stickBg, stickImage); + addDragDropButton(g_Config.touchCombo0, "Combo 1 button", roundImage, comboKeyImages[0]); + addDragDropButton(g_Config.touchCombo1, "Combo 2 button", roundImage, comboKeyImages[1]); + addDragDropButton(g_Config.touchCombo2, "Combo 3 button", roundImage, comboKeyImages[2]); + addDragDropButton(g_Config.touchCombo3, "Combo 4 button", roundImage, comboKeyImages[3]); + addDragDropButton(g_Config.touchCombo4, "Combo 5 button", roundImage, comboKeyImages[4]); for (size_t i = 0; i < controls_.size(); i++) { Add(controls_[i]); @@ -508,7 +511,7 @@ void TouchControlLayoutScreen::CreateViews() { mode_ = new ChoiceStrip(ORIENT_VERTICAL, new AnchorLayoutParams(leftColumnWidth, WRAP_CONTENT, 10, NONE, NONE, 140 + 158 + 64 + 10)); mode_->AddChoice(di->T("Move")); mode_->AddChoice(di->T("Resize")); - mode_->SetSelection(0); + mode_->SetSelection(0, false); mode_->OnChoice.Handle(this, &TouchControlLayoutScreen::OnMode); reset->OnClick.Handle(this, &TouchControlLayoutScreen::OnReset); diff --git a/UI/TouchControlVisibilityScreen.cpp b/UI/TouchControlVisibilityScreen.cpp index dab63b2940..9a6bf19a49 100644 --- a/UI/TouchControlVisibilityScreen.cpp +++ b/UI/TouchControlVisibilityScreen.cpp @@ -68,7 +68,7 @@ void TouchControlVisibilityScreen::CreateViews() { UI::GridLayoutSettings gridsettings(cellSize, 64, 5); gridsettings.fillCells = true; - GridLayout *grid = vert->Add(new GridLayout(gridsettings, new LayoutParams(FILL_PARENT, WRAP_CONTENT))); + GridLayout *grid = vert->Add(new GridLayoutList(gridsettings, new LayoutParams(FILL_PARENT, WRAP_CONTENT))); static const char* rightAnalogKey = "Right Analog Stick (tap to customize)"; toggles_.clear(); diff --git a/UI/UI.vcxproj.filters b/UI/UI.vcxproj.filters index ea4444bb8a..eb282757cf 100644 --- a/UI/UI.vcxproj.filters +++ b/UI/UI.vcxproj.filters @@ -44,7 +44,6 @@ Screens - Screens @@ -76,6 +75,9 @@ Screens + + Screens + @@ -120,7 +122,6 @@ Screens - Screens @@ -153,10 +154,13 @@ Screens + + Screens + {faee5dce-633b-4ba6-b19d-ea70ee3c1c38} - + \ No newline at end of file diff --git a/UWP/Armips_UWP/Armips_UWP.vcxproj b/UWP/Armips_UWP/Armips_UWP.vcxproj index 4baa6e6362..a0fcbffe0c 100644 --- a/UWP/Armips_UWP/Armips_UWP.vcxproj +++ b/UWP/Armips_UWP/Armips_UWP.vcxproj @@ -150,7 +150,7 @@ NotUsing false - true + false Console @@ -162,7 +162,7 @@ NotUsing false - true + false Console @@ -222,7 +222,7 @@ NotUsing false - true + false Console @@ -234,7 +234,7 @@ NotUsing false - true + false Console diff --git a/UWP/CommonUWP/CommonUWP.vcxproj b/UWP/CommonUWP/CommonUWP.vcxproj index bf2b16ea49..eec0403a85 100644 --- a/UWP/CommonUWP/CommonUWP.vcxproj +++ b/UWP/CommonUWP/CommonUWP.vcxproj @@ -201,10 +201,10 @@ false - true + false pch.h ../..;../../ext/native;../../ext/snappy;../../ext/glslang;../../ext/zlib;../../ext/libpng17;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) - NOMINMAX;_CRT_SECURE_NO_WARNINGS;_UNICODE;UNICODE;%(PreprocessorDefinitions) + NOMINMAX;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;_UNICODE;UNICODE;%(PreprocessorDefinitions) NotUsing @@ -216,9 +216,9 @@ false - true + false ../..;../../ext/native;../../ext/snappy;../../ext/glslang;../../ext/zlib;../../ext/libpng17;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) - NOMINMAX;_CRT_SECURE_NO_WARNINGS;_UNICODE;UNICODE;%(PreprocessorDefinitions) + NOMINMAX;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;_UNICODE;UNICODE;%(PreprocessorDefinitions) pch.h NotUsing @@ -231,9 +231,9 @@ false - true + false ../..;../../ext/native;../../ext/snappy;../../ext/glslang;../../ext/zlib;../../ext/libpng17;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) - NOMINMAX;_CRT_SECURE_NO_WARNINGS;_UNICODE;UNICODE;%(PreprocessorDefinitions) + NOMINMAX;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;_UNICODE;UNICODE;%(PreprocessorDefinitions) pch.h NotUsing @@ -246,10 +246,10 @@ false - true + false pch.h ../..;../../ext/native;../../ext/snappy;../../ext/glslang;../../ext/zlib;../../ext/libpng17;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) - NOMINMAX;_CRT_SECURE_NO_WARNINGS;_ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE=1;%(ClCompile.PreprocessorDefinitions) + NOMINMAX;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;_ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE=1;%(ClCompile.PreprocessorDefinitions) NotUsing @@ -261,10 +261,10 @@ false - true + false pch.h ../..;../../ext/native;../../ext/snappy;../../ext/glslang;../../ext/zlib;../../ext/libpng17;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) - NOMINMAX;_CRT_SECURE_NO_WARNINGS;_ARM64_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE=1;%(ClCompile.PreprocessorDefinitions) + NOMINMAX;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;_ARM64_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE=1;%(ClCompile.PreprocessorDefinitions) NotUsing @@ -276,9 +276,9 @@ false - true + false ../..;../../ext/native;../../ext/snappy;../../ext/glslang;../../ext/zlib;../../ext/libpng17;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) - NOMINMAX;_CRT_SECURE_NO_WARNINGS;_ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE=1;%(ClCompile.PreprocessorDefinitions) + NOMINMAX;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;_ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE=1;%(ClCompile.PreprocessorDefinitions) pch.h NotUsing @@ -291,9 +291,9 @@ false - true + false ../..;../../ext/native;../../ext/snappy;../../ext/glslang;../../ext/zlib;../../ext/libpng17;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) - NOMINMAX;_CRT_SECURE_NO_WARNINGS;_ARM64_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE=1;%(ClCompile.PreprocessorDefinitions) + NOMINMAX;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;_ARM64_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE=1;%(ClCompile.PreprocessorDefinitions) pch.h NotUsing @@ -306,9 +306,9 @@ false - true + false ../..;../../ext/native;../../ext/snappy;../../ext/glslang;../../ext/zlib;../../ext/libpng17;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) - NOMINMAX;_CRT_SECURE_NO_WARNINGS;_ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE=1;%(ClCompile.PreprocessorDefinitions) + NOMINMAX;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;_ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE=1;%(ClCompile.PreprocessorDefinitions) pch.h NotUsing @@ -321,9 +321,9 @@ false - true + false ../..;../../ext/native;../../ext/snappy;../../ext/glslang;../../ext/zlib;../../ext/libpng17;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) - NOMINMAX;_CRT_SECURE_NO_WARNINGS;_ARM64_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE=1;%(ClCompile.PreprocessorDefinitions) + NOMINMAX;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;_ARM64_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE=1;%(ClCompile.PreprocessorDefinitions) pch.h NotUsing @@ -336,10 +336,10 @@ false - true + false pch.h ../..;../../ext/native;../../ext/snappy;../../ext/glslang;../../ext/zlib;../../ext/libpng17;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) - NOMINMAX;_CRT_SECURE_NO_WARNINGS;_UNICODE;UNICODE;%(PreprocessorDefinitions) + NOMINMAX;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;_UNICODE;UNICODE;%(PreprocessorDefinitions) NotUsing @@ -351,9 +351,9 @@ false - true + false ../..;../../ext/native;../../ext/snappy;../../ext/glslang;../../ext/zlib;../../ext/libpng17;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) - NOMINMAX;_CRT_SECURE_NO_WARNINGS;_UNICODE;UNICODE;%(PreprocessorDefinitions) + NOMINMAX;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;_UNICODE;UNICODE;%(PreprocessorDefinitions) pch.h NotUsing @@ -366,9 +366,9 @@ false - true + false ../..;../../ext/native;../../ext/snappy;../../ext/glslang;../../ext/zlib;../../ext/libpng17;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) - NOMINMAX;_CRT_SECURE_NO_WARNINGS;_UNICODE;UNICODE;%(PreprocessorDefinitions) + NOMINMAX;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;_UNICODE;UNICODE;%(PreprocessorDefinitions) pch.h NotUsing diff --git a/UWP/CoreUWP/CoreUWP.vcxproj b/UWP/CoreUWP/CoreUWP.vcxproj index 8b40a36f2e..d6eea3fe56 100644 --- a/UWP/CoreUWP/CoreUWP.vcxproj +++ b/UWP/CoreUWP/CoreUWP.vcxproj @@ -202,10 +202,10 @@ Use false - true + false pch.h ../../ffmpeg/Windows10/x86/include;../..;../../ext/native;../../ext/snappy;../../ext/libpng17;../../Common;../../ext/zlib;../../ext;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) - USE_FFMPEG;_CRT_SECURE_NO_WARNINGS;NOMINMAX;_UNICODE;UNICODE;%(PreprocessorDefinitions) + USE_FFMPEG;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;NOMINMAX;_UNICODE;UNICODE;%(PreprocessorDefinitions) Console @@ -217,10 +217,10 @@ Use false - true + false pch.h ../../ffmpeg/Windows10/x86/include;../..;../../ext/native;../../ext/snappy;../../ext/libpng17;../../Common;../../ext/zlib;../../ext;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) - USE_FFMPEG;_CRT_SECURE_NO_WARNINGS;NOMINMAX;_UNICODE;UNICODE;%(PreprocessorDefinitions) + USE_FFMPEG;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;NOMINMAX;_UNICODE;UNICODE;%(PreprocessorDefinitions) Console @@ -232,10 +232,10 @@ Use false - true + false pch.h ../../ffmpeg/Windows10/x86/include;../..;../../ext/native;../../ext/snappy;../../ext/libpng17;../../Common;../../ext/zlib;../../ext;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) - USE_FFMPEG;_CRT_SECURE_NO_WARNINGS;NOMINMAX;_UNICODE;UNICODE;%(PreprocessorDefinitions) + USE_FFMPEG;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;NOMINMAX;_UNICODE;UNICODE;%(PreprocessorDefinitions) Console @@ -247,10 +247,10 @@ Use false - true + false pch.h ../../ffmpeg/Windows10/ARM/include;../../ffmpeg/WindowsInclude;../..;../../ext/native;../../ext/snappy;../../ext/libpng17;../../Common;../../ext/zlib;../../ext;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) - USE_FFMPEG;_CRT_SECURE_NO_WARNINGS;NOMINMAX;_ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE=1;%(ClCompile.PreprocessorDefinitions) + USE_FFMPEG;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;NOMINMAX;_ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE=1;%(ClCompile.PreprocessorDefinitions) Console @@ -262,10 +262,10 @@ Use false - true + false pch.h ../../ffmpeg/Windows10/ARM64/include;../../ffmpeg/WindowsInclude;../..;../../ext/native;../../ext/snappy;../../ext/libpng17;../../Common;../../ext/zlib;../../ext;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) - USE_FFMPEG;_CRT_SECURE_NO_WARNINGS;NOMINMAX;_ARM64_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE=1;%(ClCompile.PreprocessorDefinitions) + USE_FFMPEG;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;NOMINMAX;_ARM64_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE=1;%(ClCompile.PreprocessorDefinitions) Console @@ -277,10 +277,10 @@ Use false - true + false pch.h ../../ffmpeg/Windows10/ARM/include;../../ffmpeg/WindowsInclude;../..;../../ext/native;../../ext/snappy;../../ext/libpng17;../../Common;../../ext/zlib;../../ext;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) - USE_FFMPEG;_CRT_SECURE_NO_WARNINGS;NOMINMAX;_ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE=1;%(ClCompile.PreprocessorDefinitions) + USE_FFMPEG;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;NOMINMAX;_ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE=1;%(ClCompile.PreprocessorDefinitions) Console @@ -292,10 +292,10 @@ Use false - true + false pch.h ../../ffmpeg/Windows10/ARM64/include;../../ffmpeg/WindowsInclude;../..;../../ext/native;../../ext/snappy;../../ext/libpng17;../../Common;../../ext/zlib;../../ext;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) - USE_FFMPEG;_CRT_SECURE_NO_WARNINGS;NOMINMAX;_ARM64_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE=1;%(ClCompile.PreprocessorDefinitions) + USE_FFMPEG;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;NOMINMAX;_ARM64_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE=1;%(ClCompile.PreprocessorDefinitions) Console @@ -307,10 +307,10 @@ Use false - true + false pch.h ../../ffmpeg/Windows10/ARM/include;../../ffmpeg/WindowsInclude;../..;../../ext/native;../../ext/snappy;../../ext/libpng17;../../Common;../../ext/zlib;../../ext;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) - USE_FFMPEG;_CRT_SECURE_NO_WARNINGS;NOMINMAX;_ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE=1;%(ClCompile.PreprocessorDefinitions) + USE_FFMPEG;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;NOMINMAX;_ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE=1;%(ClCompile.PreprocessorDefinitions) Console @@ -322,10 +322,10 @@ Use false - true + false pch.h ../../ffmpeg/Windows10/ARM64/include;../../ffmpeg/WindowsInclude;../..;../../ext/native;../../ext/snappy;../../ext/libpng17;../../Common;../../ext/zlib;../../ext;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) - USE_FFMPEG;_CRT_SECURE_NO_WARNINGS;NOMINMAX;_ARM64_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE=1;%(ClCompile.PreprocessorDefinitions) + USE_FFMPEG;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;NOMINMAX;_ARM64_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE=1;%(ClCompile.PreprocessorDefinitions) Console @@ -337,10 +337,10 @@ Use false - true + false pch.h ../../ffmpeg/Windows10/x64/include;../../ffmpeg/WindowsInclude;../..;../../ext/native;../../ext/snappy;../../ext/libpng17;../../Common;../../ext/zlib;../../ext;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) - USE_FFMPEG;_CRT_SECURE_NO_WARNINGS;NOMINMAX;_UNICODE;UNICODE;%(PreprocessorDefinitions) + USE_FFMPEG;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;NOMINMAX;_UNICODE;UNICODE;%(PreprocessorDefinitions) Console @@ -352,10 +352,10 @@ Use false - true + false pch.h ../../ffmpeg/Windows10/x64/include;../../ffmpeg/WindowsInclude;../..;../../ext/native;../../ext/snappy;../../ext/libpng17;../../Common;../../ext/zlib;../../ext;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) - USE_FFMPEG;_CRT_SECURE_NO_WARNINGS;NOMINMAX;_UNICODE;UNICODE;%(PreprocessorDefinitions) + USE_FFMPEG;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;NOMINMAX;_UNICODE;UNICODE;%(PreprocessorDefinitions) Console @@ -367,10 +367,10 @@ Use false - true + false pch.h ../../ffmpeg/Windows10/x64/include;../../ffmpeg/WindowsInclude;../..;../../ext/native;../../ext/snappy;../../ext/libpng17;../../Common;../../ext/zlib;../../ext;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) - USE_FFMPEG;_CRT_SECURE_NO_WARNINGS;NOMINMAX;_UNICODE;UNICODE;%(PreprocessorDefinitions) + USE_FFMPEG;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;NOMINMAX;_UNICODE;UNICODE;%(PreprocessorDefinitions) Console @@ -389,6 +389,7 @@ + @@ -621,6 +622,7 @@ + diff --git a/UWP/CoreUWP/CoreUWP.vcxproj.filters b/UWP/CoreUWP/CoreUWP.vcxproj.filters index c688cd49f5..f324ec0208 100644 --- a/UWP/CoreUWP/CoreUWP.vcxproj.filters +++ b/UWP/CoreUWP/CoreUWP.vcxproj.filters @@ -563,6 +563,9 @@ Debugger + + Debugger + Debugger @@ -1372,6 +1375,9 @@ Debugger + + Debugger + Debugger diff --git a/UWP/GPU_UWP/GPU_UWP.vcxproj b/UWP/GPU_UWP/GPU_UWP.vcxproj index e241137cf6..61dfb4406d 100644 --- a/UWP/GPU_UWP/GPU_UWP.vcxproj +++ b/UWP/GPU_UWP/GPU_UWP.vcxproj @@ -202,7 +202,7 @@ Use false - true + false pch.h ../..;../../ext/snappy;../../ext/native;../../Common;../../ext;../../ext/glslang;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) _CRT_SECURE_NO_WARNINGS;NOMINMAX;_UNICODE;UNICODE;%(PreprocessorDefinitions) @@ -217,7 +217,7 @@ Use false - true + false pch.h ../..;../../ext/snappy;../../ext/native;../../Common;../../ext;../../ext/glslang;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) _CRT_SECURE_NO_WARNINGS;NOMINMAX;_UNICODE;UNICODE;%(PreprocessorDefinitions) @@ -232,7 +232,7 @@ Use false - true + false pch.h ../..;../../ext/snappy;../../ext/native;../../Common;../../ext;../../ext/glslang;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) _CRT_SECURE_NO_WARNINGS;NOMINMAX;_UNICODE;UNICODE;%(PreprocessorDefinitions) @@ -247,7 +247,7 @@ Use false - true + false pch.h ../..;../../ext/snappy;../../ext/native;../../Common;../../ext;../../ext/glslang;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) _CRT_SECURE_NO_WARNINGS;NOMINMAX;_ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE=1;%(ClCompile.PreprocessorDefinitions) @@ -262,7 +262,7 @@ Use false - true + false pch.h ../..;../../ext/snappy;../../ext/native;../../Common;../../ext;../../ext/glslang;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) _CRT_SECURE_NO_WARNINGS;NOMINMAX;_ARM64_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE=1;%(ClCompile.PreprocessorDefinitions) @@ -277,7 +277,7 @@ Use false - true + false pch.h ../..;../../ext/snappy;../../ext/native;../../Common;../../ext;../../ext/glslang;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) _CRT_SECURE_NO_WARNINGS;NOMINMAX;_ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE=1;%(ClCompile.PreprocessorDefinitions) @@ -292,7 +292,7 @@ Use false - true + false pch.h ../..;../../ext/snappy;../../ext/native;../../Common;../../ext;../../ext/glslang;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) _CRT_SECURE_NO_WARNINGS;NOMINMAX;_ARM64_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE=1;%(ClCompile.PreprocessorDefinitions) @@ -307,7 +307,7 @@ Use false - true + false pch.h ../..;../../ext/snappy;../../ext/native;../../Common;../../ext;../../ext/glslang;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) _CRT_SECURE_NO_WARNINGS;NOMINMAX;_ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE=1;%(ClCompile.PreprocessorDefinitions) @@ -322,7 +322,7 @@ Use false - true + false pch.h ../..;../../ext/snappy;../../ext/native;../../Common;../../ext;../../ext/glslang;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) _CRT_SECURE_NO_WARNINGS;NOMINMAX;_ARM64_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE=1;%(ClCompile.PreprocessorDefinitions) @@ -337,7 +337,7 @@ Use false - true + false pch.h ../..;../../ext/snappy;../../ext/native;../../Common;../../ext;../../ext/glslang;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) _CRT_SECURE_NO_WARNINGS;NOMINMAX;_UNICODE;UNICODE;%(PreprocessorDefinitions) @@ -352,7 +352,7 @@ Use false - true + false pch.h ../..;../../ext/snappy;../../ext/native;../../Common;../../ext;../../ext/glslang;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) _CRT_SECURE_NO_WARNINGS;NOMINMAX;_UNICODE;UNICODE;%(PreprocessorDefinitions) @@ -367,7 +367,7 @@ Use false - true + false pch.h ../..;../../ext/snappy;../../ext/native;../../Common;../../ext;../../ext/glslang;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) _CRT_SECURE_NO_WARNINGS;NOMINMAX;_UNICODE;UNICODE;%(PreprocessorDefinitions) diff --git a/UWP/PPSSPP_UWPMain.cpp b/UWP/PPSSPP_UWPMain.cpp index 0ba7fac4b6..18970f130e 100644 --- a/UWP/PPSSPP_UWPMain.cpp +++ b/UWP/PPSSPP_UWPMain.cpp @@ -439,6 +439,8 @@ bool System_GetPropertyBool(SystemProperty prop) { #else return false; #endif + case SYSPROP_CAN_JIT: + return true; default: return false; } diff --git a/UWP/UI_UWP/UI_UWP.vcxproj b/UWP/UI_UWP/UI_UWP.vcxproj index dd4037f7e0..140288da17 100644 --- a/UWP/UI_UWP/UI_UWP.vcxproj +++ b/UWP/UI_UWP/UI_UWP.vcxproj @@ -202,8 +202,8 @@ Use false - true - NOMINMAX;_UNICODE;UNICODE;%(PreprocessorDefinitions) + false + NOMINMAX;_CRT_NONSTDC_NO_DEPRECATE;_UNICODE;UNICODE;%(PreprocessorDefinitions) ../..;../../ext/native;../../Common;../../ext;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) pch.h @@ -217,8 +217,8 @@ Use false - true - NOMINMAX;_UNICODE;UNICODE;%(PreprocessorDefinitions) + false + NOMINMAX;_CRT_NONSTDC_NO_DEPRECATE;_UNICODE;UNICODE;%(PreprocessorDefinitions) ../..;../../ext/native;../../Common;../../ext;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) pch.h @@ -232,8 +232,8 @@ Use false - true - GOLD;NOMINMAX;_UNICODE;UNICODE;%(PreprocessorDefinitions) + false + GOLD;NOMINMAX;_CRT_NONSTDC_NO_DEPRECATE;_UNICODE;UNICODE;%(PreprocessorDefinitions) ../..;../../ext/native;../../Common;../../ext;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) pch.h @@ -247,8 +247,8 @@ Use false - true - NOMINMAX;_ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE=1;%(ClCompile.PreprocessorDefinitions) + false + NOMINMAX;_CRT_NONSTDC_NO_DEPRECATE;_ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE=1;%(ClCompile.PreprocessorDefinitions) ../..;../../ext/native;../../Common;../../ext;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) pch.h @@ -262,8 +262,8 @@ Use false - true - NOMINMAX;_ARM64_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE=1;%(ClCompile.PreprocessorDefinitions) + false + NOMINMAX;_CRT_NONSTDC_NO_DEPRECATE;_ARM64_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE=1;%(ClCompile.PreprocessorDefinitions) ../..;../../ext/native;../../Common;../../ext;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) pch.h @@ -277,8 +277,8 @@ Use false - true - NOMINMAX;_ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE=1;%(ClCompile.PreprocessorDefinitions) + false + NOMINMAX;_CRT_NONSTDC_NO_DEPRECATE;_ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE=1;%(ClCompile.PreprocessorDefinitions) ../..;../../ext/native;../../Common;../../ext;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) pch.h @@ -292,8 +292,8 @@ Use false - true - NOMINMAX;_ARM64_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE=1;%(ClCompile.PreprocessorDefinitions) + false + NOMINMAX;_CRT_NONSTDC_NO_DEPRECATE;_ARM64_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE=1;%(ClCompile.PreprocessorDefinitions) ../..;../../ext/native;../../Common;../../ext;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) pch.h @@ -307,8 +307,8 @@ Use false - true - GOLD;NOMINMAX;_ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE=1;%(ClCompile.PreprocessorDefinitions) + false + GOLD;NOMINMAX;_CRT_NONSTDC_NO_DEPRECATE;_ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE=1;%(ClCompile.PreprocessorDefinitions) ../..;../../ext/native;../../Common;../../ext;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) pch.h @@ -322,8 +322,8 @@ Use false - true - GOLD;NOMINMAX;_ARM64_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE=1;%(ClCompile.PreprocessorDefinitions) + false + GOLD;NOMINMAX;_CRT_NONSTDC_NO_DEPRECATE;_ARM64_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE=1;%(ClCompile.PreprocessorDefinitions) ../..;../../ext/native;../../Common;../../ext;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) pch.h @@ -337,8 +337,8 @@ Use false - true - NOMINMAX;_UNICODE;UNICODE;%(PreprocessorDefinitions) + false + NOMINMAX;_CRT_NONSTDC_NO_DEPRECATE;_UNICODE;UNICODE;%(PreprocessorDefinitions) ../..;../../ext/native;../../Common;../../ext;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) pch.h @@ -352,8 +352,8 @@ Use false - true - NOMINMAX;_UNICODE;UNICODE;%(PreprocessorDefinitions) + false + NOMINMAX;_CRT_NONSTDC_NO_DEPRECATE;_UNICODE;UNICODE;%(PreprocessorDefinitions) ../..;../../ext/native;../../Common;../../ext;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) pch.h @@ -367,8 +367,8 @@ Use false - true - GOLD;NOMINMAX;_UNICODE;UNICODE;%(PreprocessorDefinitions) + false + GOLD;NOMINMAX;_CRT_NONSTDC_NO_DEPRECATE;_UNICODE;UNICODE;%(PreprocessorDefinitions) ../..;../../ext/native;../../Common;../../ext;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) pch.h diff --git a/UWP/UWP.vcxproj b/UWP/UWP.vcxproj index 9dfe5919aa..c355418c48 100644 --- a/UWP/UWP.vcxproj +++ b/UWP/UWP.vcxproj @@ -204,7 +204,7 @@ ..;../ext/native;../ext/snappy;$(ProjectDir);$(IntermediateOutputPath);%(AdditionalIncludeDirectories) /bigobj %(AdditionalOptions) 4453;28204 - _DEBUG;%(PreprocessorDefinitions) + _DEBUG;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) pch.h @@ -229,7 +229,7 @@ ..;../ext/native;../ext/snappy;$(ProjectDir);$(IntermediateOutputPath);%(AdditionalIncludeDirectories) /bigobj %(AdditionalOptions) 4453;28204 - _DEBUG;%(PreprocessorDefinitions) + _DEBUG;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) pch.h @@ -254,7 +254,7 @@ ..;../ext/native;../ext/snappy;$(ProjectDir);$(IntermediateOutputPath);%(AdditionalIncludeDirectories) /bigobj %(AdditionalOptions) 4453;28204 - NDEBUG;%(PreprocessorDefinitions) + NDEBUG;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) pch.h @@ -279,7 +279,7 @@ ..;../ext/native;../ext/snappy;$(ProjectDir);$(IntermediateOutputPath);%(AdditionalIncludeDirectories) /bigobj %(AdditionalOptions) 4453;28204 - NDEBUG;%(PreprocessorDefinitions) + NDEBUG;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) pch.h @@ -304,7 +304,7 @@ ..;../ext/native;../ext/snappy;$(ProjectDir);$(IntermediateOutputPath);%(AdditionalIncludeDirectories) /bigobj %(AdditionalOptions) 4453;28204 - GOLD;NDEBUG;%(PreprocessorDefinitions) + GOLD;NDEBUG;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) pch.h @@ -329,7 +329,7 @@ ..;../ext/native;../ext/snappy;$(ProjectDir);$(IntermediateOutputPath);%(AdditionalIncludeDirectories) /bigobj %(AdditionalOptions) 4453;28204 - GOLD;NDEBUG;%(PreprocessorDefinitions) + GOLD;NDEBUG;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) pch.h @@ -354,7 +354,7 @@ ..;../ext/native;../ext/snappy;$(ProjectDir);$(IntermediateOutputPath);%(AdditionalIncludeDirectories) /bigobj %(AdditionalOptions) 4453;28204 - _DEBUG;%(PreprocessorDefinitions) + _DEBUG;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) pch.h @@ -379,7 +379,7 @@ ..;../ext/native;../ext/snappy;$(ProjectDir);$(IntermediateOutputPath);%(AdditionalIncludeDirectories) /bigobj %(AdditionalOptions) 4453;28204 - NDEBUG;%(PreprocessorDefinitions) + NDEBUG;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) pch.h @@ -404,7 +404,7 @@ ..;../ext/native;../ext/snappy;$(ProjectDir);$(IntermediateOutputPath);%(AdditionalIncludeDirectories) /bigobj %(AdditionalOptions) 4453;28204 - GOLD;NDEBUG;%(PreprocessorDefinitions) + GOLD;NDEBUG;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) pch.h @@ -429,7 +429,7 @@ ..;../ext/native;../ext/snappy;$(ProjectDir);$(IntermediateOutputPath);%(AdditionalIncludeDirectories) /bigobj %(AdditionalOptions) 4453;28204 - _DEBUG;%(PreprocessorDefinitions) + _DEBUG;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) pch.h @@ -454,7 +454,7 @@ ..;../ext/native;../ext/snappy;$(ProjectDir);$(IntermediateOutputPath);%(AdditionalIncludeDirectories) /bigobj %(AdditionalOptions) 4453;28204 - NDEBUG;%(PreprocessorDefinitions) + NDEBUG;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) pch.h @@ -479,7 +479,7 @@ ..;../ext/native;../ext/snappy;$(ProjectDir);$(IntermediateOutputPath);%(AdditionalIncludeDirectories) /bigobj %(AdditionalOptions) 4453;28204 - GOLD;NDEBUG;%(PreprocessorDefinitions) + GOLD;NDEBUG;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) pch.h diff --git a/UWP/glslang_UWP/glslang_UWP.vcxproj b/UWP/glslang_UWP/glslang_UWP.vcxproj index a8b078e226..a25f2b6a41 100644 --- a/UWP/glslang_UWP/glslang_UWP.vcxproj +++ b/UWP/glslang_UWP/glslang_UWP.vcxproj @@ -379,6 +379,13 @@ + + + + + + + @@ -409,14 +416,14 @@ - - - - - - - - + + + + + + + + @@ -436,6 +443,7 @@ + @@ -466,13 +474,13 @@ - - - - - - - + + + + + + + diff --git a/UWP/glslang_UWP/glslang_UWP.vcxproj.filters b/UWP/glslang_UWP/glslang_UWP.vcxproj.filters index d7dd255c39..e18f160695 100644 --- a/UWP/glslang_UWP/glslang_UWP.vcxproj.filters +++ b/UWP/glslang_UWP/glslang_UWP.vcxproj.filters @@ -136,22 +136,25 @@ SPIRV - + + SPIRV + + hlsl - + hlsl - + hlsl - + hlsl - + hlsl - + hlsl @@ -164,7 +167,7 @@ glslang\MachineIndependent - + hlsl @@ -270,6 +273,9 @@ SPIRV + + SPIRV + SPIRV @@ -288,6 +294,9 @@ SPIRV + + SPIRV + SPIRV @@ -300,25 +309,28 @@ SPIRV - + + SPIRV + + hlsl - + hlsl - + hlsl - + hlsl - + hlsl - + hlsl - + hlsl @@ -327,8 +339,17 @@ glslang\MachineIndependent - + hlsl + + HLSL + + + glslang + + + glslang + \ No newline at end of file diff --git a/Windows/CaptureDevice.cpp b/Windows/CaptureDevice.cpp index 6d511f8b90..f28d99b7b1 100644 --- a/Windows/CaptureDevice.cpp +++ b/Windows/CaptureDevice.cpp @@ -106,8 +106,8 @@ AudioFormatTransform g_AudioFormats[] = { const int g_cVideoFormats = ARRAYSIZE(g_VideoFormats); const int g_cAudioFormats = ARRAYSIZE(g_AudioFormats); -MediaParam defaultVideoParam = { 640, 480, 0, MFVideoFormat_RGB24 }; -MediaParam defaultAudioParam = { 44100, 2, 16, MFAudioFormat_PCM }; +MediaParam defaultVideoParam = { { 640, 480, 0, MFVideoFormat_RGB24 } }; +MediaParam defaultAudioParam = { { 44100, 2, 16, MFAudioFormat_PCM } }; HRESULT GetDefaultStride(IMFMediaType *pType, LONG *plStride); @@ -432,10 +432,9 @@ u32 ReaderCallback::doResample(u8 **dst, u32 &dstSampleRate, u32 &dstChannels, u WindowsCaptureDevice::WindowsCaptureDevice(CAPTUREDEVIDE_TYPE _type) : type(_type), error(CAPTUREDEVIDE_ERROR_NO_ERROR), - errorMessage(""), state(CAPTUREDEVIDE_STATE::UNINITIALIZED) { param = { 0 }; - deviceParam = { 0 }; + deviceParam = { { 0 } }; switch (type) { case CAPTUREDEVIDE_TYPE::VIDEO: @@ -838,7 +837,6 @@ void WindowsCaptureDevice::sendMessage(CAPTUREDEVIDE_MESSAGE message) { // Must be unique lock std::unique_lock lock(mutex); messageQueue.push(message); - lock.unlock(); cond.notify_one(); } @@ -849,15 +847,16 @@ CAPTUREDEVIDE_MESSAGE WindowsCaptureDevice::getMessage() { cond.wait(lock, [this]() { return !messageQueue.empty(); }); message = messageQueue.front(); messageQueue.pop(); - lock.unlock(); return message; } void WindowsCaptureDevice::updateState(const CAPTUREDEVIDE_STATE &newState) { - std::unique_lock guard(stateMutex_); state = newState; - stateCond_.notify_all(); + if (isShutDown()) { + std::unique_lock guard(stateMutex_); + stateCond_.notify_all(); + } } void WindowsCaptureDevice::waitShutDown() { diff --git a/Windows/CaptureDevice.h b/Windows/CaptureDevice.h index 64ef4926a6..d457702afe 100644 --- a/Windows/CaptureDevice.h +++ b/Windows/CaptureDevice.h @@ -113,7 +113,7 @@ template void SafeRelease(T **ppT) { class WindowsCaptureDevice; -class ReaderCallback : public IMFSourceReaderCallback { +class ReaderCallback final : public IMFSourceReaderCallback { public: ReaderCallback(WindowsCaptureDevice *device); ~ReaderCallback(); diff --git a/Windows/DSoundStream.cpp b/Windows/DSoundStream.cpp index bd375d025e..bd8e343ad0 100644 --- a/Windows/DSoundStream.cpp +++ b/Windows/DSoundStream.cpp @@ -20,9 +20,6 @@ #include "DSoundStream.h" -// TODO: Get rid of this -static DSoundAudioBackend *g_dsound; - inline int RoundDown128(int x) { return x & (~127); } @@ -150,7 +147,7 @@ int DSoundAudioBackend::RunThread() { return 0; } -DSoundAudioBackend::DSoundAudioBackend() : threadData_(0), ds_(nullptr) { +DSoundAudioBackend::DSoundAudioBackend() { } DSoundAudioBackend::~DSoundAudioBackend() { diff --git a/Windows/DSoundStream.h b/Windows/DSoundStream.h index 22af665e35..7b4798449a 100644 --- a/Windows/DSoundStream.h +++ b/Windows/DSoundStream.h @@ -4,7 +4,9 @@ #include "WindowsAudio.h" #include -#include + +struct IDirectSound8; +struct IDirectSoundBuffer; class DSoundAudioBackend : public WindowsAudioBackend { public: @@ -38,7 +40,7 @@ private: int totalRenderedBytes_; int sampleRate_; - volatile int threadData_; + volatile int threadData_ = 0; enum { BUFSIZE = 0x4000, diff --git a/Windows/Debugger/CtrlDisAsmView.cpp b/Windows/Debugger/CtrlDisAsmView.cpp index e26f9644d8..ebbbf85354 100644 --- a/Windows/Debugger/CtrlDisAsmView.cpp +++ b/Windows/Debugger/CtrlDisAsmView.cpp @@ -866,7 +866,6 @@ void CtrlDisAsmView::toggleBreakpoint(bool toggleEnabled) void CtrlDisAsmView::onMouseDown(WPARAM wParam, LPARAM lParam, int button) { dontRedraw = false; - int x = LOWORD(lParam); int y = HIWORD(lParam); u32 newAddress = yToAddress(y); @@ -921,7 +920,6 @@ void CtrlDisAsmView::onMouseUp(WPARAM wParam, LPARAM lParam, int button) { if (button == 1) { - int x = LOWORD(lParam); int y = HIWORD(lParam); setCurAddress(yToAddress(y), KeyDownAsync(VK_SHIFT)); redraw(); @@ -1076,7 +1074,6 @@ void CtrlDisAsmView::onMouseMove(WPARAM wParam, LPARAM lParam, int button) { if ((button & 1) != 0) { - int x = LOWORD(lParam); int y = HIWORD(lParam); setCurAddress(yToAddress(y), KeyDownAsync(VK_SHIFT)); // TODO: Perhaps don't do this every time, but on a timer? diff --git a/Windows/Debugger/CtrlMemView.cpp b/Windows/Debugger/CtrlMemView.cpp index 7ae71ef331..5b0e4400b8 100644 --- a/Windows/Debugger/CtrlMemView.cpp +++ b/Windows/Debugger/CtrlMemView.cpp @@ -3,6 +3,7 @@ #include #include #include +#include "ext/xxhash.h" #include "Core/Config.h" #include "Windows/resource.h" #include "Core/MemMap.h" @@ -173,8 +174,7 @@ CtrlMemView *CtrlMemView::getFrom(HWND hwnd) } -void CtrlMemView::onPaint(WPARAM wParam, LPARAM lParam) -{ +void CtrlMemView::onPaint(WPARAM wParam, LPARAM lParam) { auto memLock = Memory::Lock(); // draw to a bitmap for double buffering @@ -187,6 +187,7 @@ void CtrlMemView::onPaint(WPARAM wParam, LPARAM lParam) SetBkMode(hdc,OPAQUE); HPEN standardPen = CreatePen(0,0,0xFFFFFF); HBRUSH standardBrush = CreateSolidBrush(0xFFFFFF); + COLORREF standardBG = GetBkColor(hdc); HPEN oldPen = (HPEN) SelectObject(hdc,standardPen); HBRUSH oldBrush = (HBRUSH) SelectObject(hdc,standardBrush); @@ -199,88 +200,124 @@ void CtrlMemView::onPaint(WPARAM wParam, LPARAM lParam) if (displayOffsetScale) drawOffsetScale(hdc); - + + std::vector memRangeInfo = FindMemInfoByFlag(highlightFlags_, windowStart, (visibleRows + 1) * rowSize); + + COLORREF lastTextCol = 0x000000; + COLORREF lastBGCol = standardBG; + auto setTextColors = [&](COLORREF fg, COLORREF bg) { + if (lastTextCol != fg) { + SetTextColor(hdc, fg); + lastTextCol = fg; + } + if (lastBGCol != bg) { + SetBkColor(hdc, bg); + lastBGCol = bg; + } + }; // draw one extra row that may be partially visible - for (int i = 0; i < visibleRows+1; i++) - { - char temp[32]; - - unsigned int address=windowStart + i*rowSize; - int rowY = rowHeight*i; - + for (int i = 0; i < visibleRows + 1; i++) { + int rowY = rowHeight * i; + // Skip the first X rows to make space for the offsets. if (displayOffsetScale) - rowY += rowHeight * offsetSpace; // skip the first X rows to make space for the offsets - - - sprintf(temp,"%08X",address); - SetTextColor(hdc,0x600000); - TextOutA(hdc,addressStart,rowY,temp,(int)strlen(temp)); + rowY += rowHeight * offsetSpace; - SetTextColor(hdc,0x000000); + char temp[32]; + uint32_t address = windowStart + i * rowSize; + sprintf(temp, "%08X", address); - u32 memory[4]; - bool valid = debugger != NULL && debugger->isAlive() && Memory::IsValidAddress(address); - if (valid) - { - memory[0] = debugger->readMemory(address); - memory[1] = debugger->readMemory(address+4); - memory[2] = debugger->readMemory(address+8); - memory[3] = debugger->readMemory(address+12); + setTextColors(0x600000, standardBG); + TextOutA(hdc, addressStart, rowY, temp, (int)strlen(temp)); + + union { + uint32_t words[4]; + uint8_t bytes[16]; + } memory; + bool valid = debugger != nullptr && debugger->isAlive() && Memory::IsValidAddress(address); + for (int i = 0; valid && i < 4; ++i) { + memory.words[i] = debugger->readMemory(address + i * 4); } - u8* m = (u8*) memory; - for (int j = 0; j < rowSize; j++) - { - if (valid) sprintf(temp,"%02X",m[j]); - else strcpy(temp,"??"); - - unsigned char c = m[j]; - if (c < 32 || c >= 128 || valid == false) c = '.'; - - if (address+j == curAddress && searching == false) - { - COLORREF oldBkColor = GetBkColor(hdc); - COLORREF oldTextColor = GetTextColor(hdc); - - if (hasFocus && !asciiSelected) - { - SetTextColor(hdc,0xFFFFFF); - SetBkColor(hdc,0xFF9933); - if (selectedNibble == 0) SelectObject(hdc,(HGDIOBJ)underlineFont); - } else { - SetTextColor(hdc,0); - SetBkColor(hdc,0xC0C0C0); + for (int j = 0; j < rowSize; j++) { + const uint32_t byteAddress = (address + j) & ~0xC0000000; + std::string tag; + bool tagContinues = false; + for (auto info : memRangeInfo) { + if (info.start <= byteAddress && info.start + info.size > byteAddress) { + tag = info.tag; + tagContinues = byteAddress + 1 < info.start + info.size; } - TextOutA(hdc,hexStart+j*3*charWidth,rowY,&temp[0],1); - - if (hasFocus && !asciiSelected) - { - if (selectedNibble == 1) SelectObject(hdc,(HGDIOBJ)underlineFont); - else SelectObject(hdc,(HGDIOBJ)font); - } - TextOutA(hdc,hexStart+j*3*charWidth+charWidth,rowY,&temp[1],1); - - if (hasFocus && asciiSelected) - { - SetTextColor(hdc,0xFFFFFF); - SetBkColor(hdc,0xFF9933); - } else { - SetTextColor(hdc,0); - SetBkColor(hdc,0xC0C0C0); - SelectObject(hdc,(HGDIOBJ)font); - } - TextOutA(hdc,asciiStart+j*(charWidth+2),rowY,(char*)&c,1); - - SetTextColor(hdc,oldTextColor); - SetBkColor(hdc,oldBkColor); - } else { - TextOutA(hdc,hexStart+j*3*charWidth,rowY,temp,2); - TextOutA(hdc,asciiStart+j*(charWidth+2),rowY,(char*)&c,1); } + + int hexX = hexStart + j * 3 * charWidth; + int hexLen = 2; + int asciiX = asciiStart + j * (charWidth + 2); + + char c; + if (valid) { + sprintf(temp, "%02X ", memory.bytes[j]); + c = (char)memory.bytes[j]; + if (memory.bytes[j] < 32 || memory.bytes[j] >= 128) + c = '.'; + } else { + strcpy(temp, "??"); + c = '.'; + } + + COLORREF hexBGCol = standardBG; + COLORREF hexTextCol = 0x000000; + COLORREF continueBGCol = standardBG; + COLORREF asciiBGCol = standardBG; + COLORREF asciiTextCol = 0x000000; + int underline = -1; + + if (address + j == curAddress && searching == false) { + if (asciiSelected) { + hexBGCol = 0xC0C0C0; + hexTextCol = 0x000000; + asciiBGCol = hasFocus ? 0xFF9933 : 0xC0C0C0; + asciiTextCol = hasFocus ? 0xFFFFFF : 0x000000; + } else { + hexBGCol = hasFocus ? 0xFF9933 : 0xC0C0C0; + hexTextCol = hasFocus ? 0xFFFFFF : 0x000000; + asciiBGCol = 0xC0C0C0; + asciiTextCol = 0x000000; + underline = selectedNibble; + } + if (!tag.empty() && tagContinues) { + continueBGCol = pickTagColor(tag); + } + } else if (!tag.empty()) { + hexBGCol = pickTagColor(tag); + continueBGCol = hexBGCol; + asciiBGCol = pickTagColor(tag); + hexLen = tagContinues ? 3 : 2; + } + + setTextColors(hexTextCol, hexBGCol); + if (underline >= 0) { + SelectObject(hdc, underline == 0 ? (HGDIOBJ)underlineFont : (HGDIOBJ)font); + TextOutA(hdc, hexX, rowY, &temp[0], 1); + SelectObject(hdc, underline == 0 ? (HGDIOBJ)font : (HGDIOBJ)underlineFont); + TextOutA(hdc, hexX + charWidth, rowY, &temp[1], 1); + SelectObject(hdc, (HGDIOBJ)font); + + // If the tag keeps going, draw the BG too. + if (continueBGCol != standardBG) { + setTextColors(0x000000, continueBGCol); + TextOutA(hdc, hexX + charWidth * 2, rowY, &temp[2], 1); + } + } else { + TextOutA(hdc, hexX, rowY, temp, hexLen); + } + + setTextColors(asciiTextCol, asciiBGCol); + TextOutA(hdc, asciiX, rowY, &c, 1); } } + setTextColors(0x000000, standardBG); SelectObject(hdc,oldFont); SelectObject(hdc,oldPen); SelectObject(hdc,oldBrush); @@ -518,11 +555,17 @@ void CtrlMemView::onMouseMove(WPARAM wParam, LPARAM lParam, int button) } -void CtrlMemView::updateStatusBarText() -{ - char text[64]; - sprintf(text,"%08X",curAddress); - SendMessage(GetParent(wnd),WM_DEB_SETSTATUSBARTEXT,0,(LPARAM)text); +void CtrlMemView::updateStatusBarText() { + std::vector memRangeInfo = FindMemInfoByFlag(highlightFlags_, curAddress, 1); + + char text[512]; + snprintf(text, sizeof(text), "%08X", curAddress); + // There should only be one. + for (MemBlockInfo info : memRangeInfo) { + snprintf(text, sizeof(text), "%08X - %s %08X-%08X (at PC %08X / %lld ticks)", curAddress, info.tag.c_str(), info.start, info.start + info.size, info.pc, info.ticks); + } + + SendMessage(GetParent(wnd), WM_DEB_SETSTATUSBARTEXT, 0, (LPARAM)text); } void CtrlMemView::gotoPoint(int x, int y) @@ -591,6 +634,7 @@ void CtrlMemView::scrollWindow(int lines) { windowStart += lines*rowSize; curAddress += lines*rowSize; + updateStatusBarText(); redraw(); } @@ -642,6 +686,10 @@ std::vector CtrlMemView::searchString(std::string searchQuery) return searchResAddrs; size_t queryLength = searchQuery.length(); + if (queryLength == 0) + return searchResAddrs; + + // TODO: Scratchpad, VRAM? u32 segmentStart = PSP_GetKernelMemoryBase(); //RAM start const u32 segmentEnd = PSP_GetUserMemoryEnd() - (u32)queryLength; //RAM end u8* ptr; @@ -793,3 +841,16 @@ void CtrlMemView::toggleOffsetScale(CommonToggles toggle) updateStatusBarText(); redraw(); } + +void CtrlMemView::setHighlightType(MemBlockFlags flags) { + if (highlightFlags_ != flags) { + highlightFlags_ = flags; + redraw(); + } +} + +uint32_t CtrlMemView::pickTagColor(const std::string &tag) { + int colors[6] = { 0xe0FFFF, 0xFFE0E0, 0xE8E8FF, 0xFFE0FF, 0xE0FFE0, 0xFFFFE0 }; + int which = XXH3_64bits(tag.c_str(), tag.length()) % ARRAY_SIZE(colors); + return colors[which]; +} diff --git a/Windows/Debugger/CtrlMemView.h b/Windows/Debugger/CtrlMemView.h index 44ab3bcdf0..563f589be2 100644 --- a/Windows/Debugger/CtrlMemView.h +++ b/Windows/Debugger/CtrlMemView.h @@ -17,7 +17,9 @@ // //To get a class instance to be able to access it, just use getFrom(HWND wnd). -#include "../../Core/Debugger/DebugInterface.h" +#include +#include "Core/Debugger/DebugInterface.h" +#include "Core/Debugger/MemBlockInfo.h" enum OffsetSpacing { offsetSpace = 3, // the number of blank lines that should be left to make space for the offsets @@ -63,8 +65,12 @@ class CtrlMemView bool hasFocus; static wchar_t szClassName[]; DebugInterface *debugger; + + MemBlockFlags highlightFlags_ = MemBlockFlags::ALLOC; + void updateStatusBarText(); void search(bool continueSearch); + uint32_t pickTagColor(const std::string &tag); public: CtrlMemView(HWND _wnd); ~CtrlMemView(); @@ -99,4 +105,5 @@ public: void drawOffsetScale(HDC hdc); void toggleOffsetScale(CommonToggles toggle); void toggleStringSearch(CommonToggles toggle); + void setHighlightType(MemBlockFlags flags); }; diff --git a/Windows/Debugger/CtrlRegisterList.cpp b/Windows/Debugger/CtrlRegisterList.cpp index 02992e5f28..9cd9f36c0a 100644 --- a/Windows/Debugger/CtrlRegisterList.cpp +++ b/Windows/Debugger/CtrlRegisterList.cpp @@ -255,7 +255,7 @@ void CtrlRegisterList::onPaint(WPARAM wParam, LPARAM lParam) { char temp[256]; int len; - u32 value; + u32 value = -1; switch (i) { @@ -312,7 +312,6 @@ void CtrlRegisterList::onKeyDown(WPARAM wParam, LPARAM lParam) { RECT rect; GetClientRect(this->wnd, &rect); - int page=(rect.bottom/rowHeight)/2-1; if (ctrlDown && tolower(wParam) == 'c') { diff --git a/Windows/Debugger/CtrlRegisterList.h b/Windows/Debugger/CtrlRegisterList.h index 123e8e0713..398e6803d0 100644 --- a/Windows/Debugger/CtrlRegisterList.h +++ b/Windows/Debugger/CtrlRegisterList.h @@ -28,15 +28,12 @@ class CtrlRegisterList int rowHeight; int selection = 0; - int marker = 0; int category = 0; int oldSelection = 0; - bool selectionChanged = false; bool selecting = false; bool hasFocus = false; - bool showHex = false; DebugInterface *cpu = nullptr; static TCHAR szClassName[]; diff --git a/Windows/Debugger/Debugger.h b/Windows/Debugger/Debugger.h deleted file mode 100644 index 5d442bb098..0000000000 --- a/Windows/Debugger/Debugger.h +++ /dev/null @@ -1,16 +0,0 @@ -///////////////////////////////////////////////////////////////////////////////////////////////////// -// M O D U L E B E G I N /////////////////////////////////////////////////////////////////////////// -///////////////////////////////////////////////////////////////////////////////////////////////////// - -#ifndef DEBUGGER_H -#define DEBUGGER_H - -///////////////////////////////////////////////////////////////////////////////////////////////////// -// I N C L U D E S ////////////////////////////////////////////////////////////////////////////////// -///////////////////////////////////////////////////////////////////////////////////////////////////// - -#include "Debugger_Disam.h" -#include "Debugger_BreakPoints.h" -#include "Debugger_LogManager.h" - -#endif \ No newline at end of file diff --git a/Windows/Debugger/Debugger_Disasm.cpp b/Windows/Debugger/Debugger_Disasm.cpp index 39d29e7f62..3f0bf77907 100644 --- a/Windows/Debugger/Debugger_Disasm.cpp +++ b/Windows/Debugger/Debugger_Disasm.cpp @@ -2,7 +2,7 @@ #include "Core/Config.h" #include "Core/MemMap.h" -#include "Windows/Resource.h" +#include "Windows/resource.h" #include "Windows/InputBox.h" #include "Core/Debugger/Breakpoints.h" @@ -384,7 +384,6 @@ BOOL CDisasm::DlgProc(UINT message, WPARAM wParam, LPARAM lParam) case WM_COMMAND: { CtrlDisAsmView *ptr = CtrlDisAsmView::getFrom(GetDlgItem(m_hDlg,IDC_DISASMVIEW)); - CtrlRegisterList *reglist = CtrlRegisterList::getFrom(GetDlgItem(m_hDlg,IDC_REGLIST)); switch (LOWORD(wParam)) { case ID_TOGGLE_BREAK: SendMessage(MainWindow::GetHWND(), WM_COMMAND, ID_TOGGLE_BREAK, 0); @@ -851,17 +850,17 @@ void CDisasm::SetDebugMode(bool _bDebug, bool switchPC) void CDisasm::Show(bool bShow) { if (deferredSymbolFill_ && bShow) { - if (g_symbolMap) + if (g_symbolMap) { g_symbolMap->FillSymbolListBox(GetDlgItem(m_hDlg, IDC_FUNCTIONLIST), ST_FUNCTION); - deferredSymbolFill_ = false; + deferredSymbolFill_ = false; + } } Dialog::Show(bShow); } void CDisasm::NotifyMapLoaded() { - if (m_bShowState == SW_SHOW) { - if (g_symbolMap) - g_symbolMap->FillSymbolListBox(GetDlgItem(m_hDlg, IDC_FUNCTIONLIST), ST_FUNCTION); + if (m_bShowState != SW_HIDE && g_symbolMap) { + g_symbolMap->FillSymbolListBox(GetDlgItem(m_hDlg, IDC_FUNCTIONLIST), ST_FUNCTION); } else { deferredSymbolFill_ = true; } @@ -880,8 +879,8 @@ void CDisasm::Goto(u32 addr) void CDisasm::UpdateDialog(bool _bComplete) { - HWND gotoInt = GetDlgItem(m_hDlg, IDC_GOTOINT); /* + HWND gotoInt = GetDlgItem(m_hDlg, IDC_GOTOINT); ComboBox_ResetContent(gotoInt); for (int i=0; i +#include #include "..\resource.h" -#include "Common/System/Display.h" #include "Common/Data/Encoding/Utf8.h" +#include "Common/System/Display.h" +#include "Core/Debugger/MemBlockInfo.h" #include "Core/Debugger/SymbolMap.h" -#include "Core/MIPS/MIPSDebugInterface.h" // BAD +#include "Core/MIPS/MIPSDebugInterface.h" #include "Debugger_MemoryDlg.h" #include "CtrlMemView.h" @@ -68,6 +70,20 @@ CMemoryDlg::CMemoryDlg(HINSTANCE _hInstance, HWND _hParent, DebugInterface *_cpu searchBoxHdl = GetDlgItem(m_hDlg, IDC_SEARCH_BOX); srcListHdl = GetDlgItem(m_hDlg, IDC_SEARCH_RESULTS); + layerDropdown_ = GetDlgItem(m_hDlg, IDC_REGIONS); + ComboBox_ResetContent(layerDropdown_); + ComboBox_AddString(layerDropdown_, L"Show allocations"); + ComboBox_SetItemData(layerDropdown_, 0, MemBlockFlags::ALLOC); + ComboBox_AddString(layerDropdown_, L"Show sub allocations"); + ComboBox_SetItemData(layerDropdown_, 1, MemBlockFlags::SUB_ALLOC); + ComboBox_AddString(layerDropdown_, L"Show writes"); + ComboBox_SetItemData(layerDropdown_, 2, MemBlockFlags::WRITE); + ComboBox_AddString(layerDropdown_, L"Show textures"); + ComboBox_SetItemData(layerDropdown_, 3, MemBlockFlags::TEXTURE); + ComboBox_SetCurSel(layerDropdown_, 0); + + status_ = GetDlgItem(m_hDlg, IDC_MEMVIEW_STATUS); + memView = CtrlMemView::getFrom(memViewHdl); memView->setDebugger(_cpu); @@ -118,66 +134,53 @@ void CMemoryDlg::searchBoxRedraw(std::vector results) { void CMemoryDlg::NotifyMapLoaded() { if (m_hDlg) - { - g_symbolMap->FillSymbolListBox(symListHdl,ST_DATA); - int sel = ComboBox_GetCurSel(memViewHdl); - ComboBox_ResetContent(memViewHdl); - /* - for (int i = 0; i < cpu->getMemMap()->numRegions; i++) - { - // TODO: wchar_t - int n = ComboBox_AddString(lb,cpu->getMemMap()->regions[i].name); - ComboBox_SetItemData(lb,n,cpu->getMemMap()->regions[i].start); - }*/ - ComboBox_SetCurSel(memViewHdl,sel>=0?sel:0); - } + g_symbolMap->FillSymbolListBox(symListHdl, ST_DATA); Update(); } -BOOL CMemoryDlg::DlgProc(UINT message, WPARAM wParam, LPARAM lParam) -{ - switch(message){ - case WM_COMMAND:{ +BOOL CMemoryDlg::DlgProc(UINT message, WPARAM wParam, LPARAM lParam) { + wchar_t temp[256]{}; + int n; + + switch (message) { + case WM_COMMAND: { HWND lb = GetDlgItem(m_hDlg, LOWORD(wParam)); - switch (LOWORD(wParam)){ + switch (LOWORD(wParam)) { case IDC_REGIONS: switch (HIWORD(wParam)) { - case LBN_DBLCLK:{ - int n = ComboBox_GetCurSel(lb); - if (n != -1) { - unsigned int addr = (unsigned int)ComboBox_GetItemData(lb,n); - memView->gotoAddr(addr); - } + case CBN_SELENDOK: + n = ComboBox_GetCurSel(lb); + if (n != CB_ERR) { + MemBlockFlags flags = (MemBlockFlags)ComboBox_GetItemData(lb, n); + memView->setHighlightType(MemBlockFlags(flags)); } break; - }; - break; + } + break; case IDC_SYMBOLS: switch (HIWORD(wParam)) { - case LBN_DBLCLK:{ - int n = ListBox_GetCurSel(lb); - if (n != -1) { - unsigned int addr = (unsigned int)ListBox_GetItemData(lb,n); - memView->gotoAddr(addr); - } + case LBN_DBLCLK: + n = ListBox_GetCurSel(lb); + if (n != -1) { + unsigned int addr = (unsigned int)ListBox_GetItemData(lb,n); + memView->gotoAddr(addr); + } + break; } break; - }; case IDC_SEARCH_RESULTS: switch (HIWORD(wParam)) { - case LBN_DBLCLK: { - int n = ListBox_GetCurSel(lb); - if (n != -1) { - unsigned int addr = (unsigned int)ListBox_GetItemData(lb, n); - memView->gotoAddr(addr); - } + case LBN_DBLCLK: + n = ListBox_GetCurSel(lb); + if (n != -1) { + unsigned int addr = (unsigned int)ListBox_GetItemData(lb, n); + memView->gotoAddr(addr); + } + break; } break; - }; - break; case IDC_SHOWOFFSETS: - switch (HIWORD(wParam)) - { + switch (HIWORD(wParam)) { case BN_CLICKED: if (SendDlgItemMessage(m_hDlg, IDC_SHOWOFFSETS, BM_GETCHECK, 0, 0)) memView->toggleOffsetScale(On); @@ -187,10 +190,8 @@ BOOL CMemoryDlg::DlgProc(UINT message, WPARAM wParam, LPARAM lParam) } break; case IDC_BUTTON_SEARCH: - switch (HIWORD(wParam)) - { + switch (HIWORD(wParam)) { case BN_CLICKED: - wchar_t temp[256]; GetWindowText(searchBoxHdl, temp, 255); std::vector results = memView->searchString(ConvertWStringToUTF8(temp).c_str()); if (results.size() > 0){ @@ -198,14 +199,14 @@ BOOL CMemoryDlg::DlgProc(UINT message, WPARAM wParam, LPARAM lParam) } break; } + break; } } break; case WM_DEB_MAPLOADED: NotifyMapLoaded(); break; - case WM_DEB_GOTOADDRESSEDIT:{ - wchar_t temp[256]; + case WM_DEB_GOTOADDRESSEDIT: { u32 addr; GetWindowText(editWnd,temp,255); @@ -222,12 +223,13 @@ BOOL CMemoryDlg::DlgProc(UINT message, WPARAM wParam, LPARAM lParam) Update(); return TRUE; - case WM_INITDIALOG: - { - return TRUE; - } + case WM_DEB_SETSTATUSBARTEXT: + SendMessage(status_, SB_SETTEXT, 0, (LPARAM)ConvertUTF8ToWString((const char *)lParam).c_str()); break; + case WM_INITDIALOG: + return TRUE; + case WM_SIZE: Size(); break; @@ -255,11 +257,11 @@ void CMemoryDlg::Size() int dlg_w = winRect.right - winRect.left; int dlg_h = winRect.bottom - winRect.top; - int wf = slRect.right-slRect.left; int w = dlg_w - 3 * fontScale - wf*2; - int top = 48 * fontScale; - int height = dlg_h - top; + int top = 40 * fontScale; + int bottom = 24 * fontScale; + int height = dlg_h - top - bottom; //HWND, X, Y, width, height, repaint MoveWindow(symListHdl, 0 ,top, wf, height, TRUE); MoveWindow(memViewHdl, wf+4 ,top, w, height, TRUE); diff --git a/Windows/Debugger/Debugger_MemoryDlg.h b/Windows/Debugger/Debugger_MemoryDlg.h index 91bf07e986..cf771c6415 100644 --- a/Windows/Debugger/Debugger_MemoryDlg.h +++ b/Windows/Debugger/Debugger_MemoryDlg.h @@ -17,6 +17,8 @@ private: RECT winRect, srRect; CtrlMemView *memView; HWND memViewHdl, symListHdl, editWnd, searchBoxHdl, srcListHdl; + HWND layerDropdown_; + HWND status_; BOOL DlgProc(UINT message, WPARAM wParam, LPARAM lParam); public: diff --git a/Windows/Debugger/Debugger_Misc.cpp b/Windows/Debugger/Debugger_Misc.cpp deleted file mode 100644 index 60655866ed..0000000000 --- a/Windows/Debugger/Debugger_Misc.cpp +++ /dev/null @@ -1,10 +0,0 @@ -///////////////////////////////////////////////////////////////////////////////////////////////////// -// M O D U L E B E G I N /////////////////////////////////////////////////////////////////////////// -///////////////////////////////////////////////////////////////////////////////////////////////////// - -#include "stdafx.h" - -///////////////////////////////////////////////////////////////////////////////////////////////////// -// I M P L E M E N T A T I O N ////////////////////////////////////////////////////////////////////// -///////////////////////////////////////////////////////////////////////////////////////////////////// - diff --git a/Windows/Debugger/Debugger_Misc.h b/Windows/Debugger/Debugger_Misc.h deleted file mode 100644 index c7c5216a64..0000000000 --- a/Windows/Debugger/Debugger_Misc.h +++ /dev/null @@ -1,12 +0,0 @@ -///////////////////////////////////////////////////////////////////////////////////////////////////// -// M O D U L E B E G I N /////////////////////////////////////////////////////////////////////////// -///////////////////////////////////////////////////////////////////////////////////////////////////// - -#ifndef _DEBUGMISC_H -#define _DEBUGMISC_H - -///////////////////////////////////////////////////////////////////////////////////////////////////// -// E X T E R N A L ////////////////////////////////////////////////////////////////////////////////// -///////////////////////////////////////////////////////////////////////////////////////////////////// - -#endif \ No newline at end of file diff --git a/Windows/Debugger/Debugger_VFPUDlg.cpp b/Windows/Debugger/Debugger_VFPUDlg.cpp index 8851afd62e..5075e61731 100644 --- a/Windows/Debugger/Debugger_VFPUDlg.cpp +++ b/Windows/Debugger/Debugger_VFPUDlg.cpp @@ -32,23 +32,23 @@ CVFPUDlg::CVFPUDlg(HINSTANCE _hInstance, HWND _hParent, DebugInterface *cpu_) : ZeroMemory (&tcItem,sizeof (tcItem)); tcItem.mask = TCIF_TEXT; tcItem.dwState = 0; - tcItem.pszText = L"Float"; + tcItem.pszText = (LPTSTR)L"Float"; tcItem.cchTextMax = (int)wcslen(tcItem.pszText)+1; tcItem.iImage = 0; TabCtrl_InsertItem(tabs, TabCtrl_GetItemCount(tabs),&tcItem); - tcItem.pszText = L"HalfFloat"; + tcItem.pszText = (LPTSTR)L"HalfFloat"; tcItem.cchTextMax = (int)wcslen(tcItem.pszText)+1; TabCtrl_InsertItem(tabs, TabCtrl_GetItemCount(tabs),&tcItem); - tcItem.pszText = L"Hex"; + tcItem.pszText = (LPTSTR)L"Hex"; tcItem.cchTextMax = (int)wcslen(tcItem.pszText)+1; TabCtrl_InsertItem(tabs, TabCtrl_GetItemCount(tabs),&tcItem); - tcItem.pszText = L"Bytes"; + tcItem.pszText = (LPTSTR)L"Bytes"; tcItem.cchTextMax = (int)wcslen(tcItem.pszText)+1; TabCtrl_InsertItem(tabs, TabCtrl_GetItemCount(tabs),&tcItem); - tcItem.pszText = L"Shorts"; + tcItem.pszText = (LPTSTR)L"Shorts"; tcItem.cchTextMax = (int)wcslen(tcItem.pszText)+1; TabCtrl_InsertItem(tabs, TabCtrl_GetItemCount(tabs),&tcItem); - tcItem.pszText = L"Ints"; + tcItem.pszText = (LPTSTR)L"Ints"; tcItem.cchTextMax = (int)wcslen(tcItem.pszText)+1; TabCtrl_InsertItem(tabs, TabCtrl_GetItemCount(tabs),&tcItem); mode=0; diff --git a/Windows/Debugger/DumpMemoryWindow.cpp b/Windows/Debugger/DumpMemoryWindow.cpp index 3676307420..67b95ce8fe 100644 --- a/Windows/Debugger/DumpMemoryWindow.cpp +++ b/Windows/Debugger/DumpMemoryWindow.cpp @@ -2,7 +2,9 @@ #include #include "Common/Data/Encoding/Utf8.h" #include "Core/Core.h" +#include "Core/HLE/ReplaceTables.h" #include "Core/MemMap.h" +#include "Core/MIPS/JitCommon/JitBlockCache.h" #include "Windows/Debugger/DumpMemoryWindow.h" #include "Windows/resource.h" #include "Windows/W32Util/ShellUtil.h" @@ -75,27 +77,49 @@ INT_PTR CALLBACK DumpMemoryWindow::dlgFunc(HWND hwnd, UINT iMsg, WPARAM wParam, } break; case IDOK: - if (bp->fetchDialogData(hwnd)) - { + if (bp->fetchDialogData(hwnd)) { + bool priorDumpWasStepping = Core_IsStepping(); + if (!priorDumpWasStepping && PSP_IsInited()) { + // If emulator isn't paused force paused state, but wait before locking. + Core_EnableStepping(true); + Core_WaitInactive(); + } + auto memLock = Memory::Lock(); if (!PSP_IsInited()) break; FILE *output = _wfopen(bp->fileName_.c_str(), L"wb"); - if (output == NULL) { + if (output == nullptr) { char errorMessage[2048]; snprintf(errorMessage, sizeof(errorMessage), "Could not open file \"%S\".", bp->fileName_.c_str()); - MessageBoxA(hwnd,errorMessage,"Error",MB_OK); + MessageBoxA(hwnd, errorMessage, "Error", MB_OK); break; } - - bool priorDumpWasStepping = Core_IsStepping(); - if (!priorDumpWasStepping) Core_EnableStepping(true); // If emulator isn't paused force paused state - fwrite(Memory::GetPointer(bp->start), 1, bp->size, output); + + bool includeReplacements = SendMessage(GetDlgItem(hwnd, IDC_DUMP_INCLUDEHACKS), BM_GETCHECK, 0, 0) != 0; + if (includeReplacements) { + fwrite(Memory::GetPointer(bp->start), 1, bp->size, output); + } else { + auto savedReplacements = SaveAndClearReplacements(); + if (MIPSComp::jit) { + auto savedBlocks = MIPSComp::jit->SaveAndClearEmuHackOps(); + fwrite(Memory::GetPointer(bp->start), 1, bp->size, output); + MIPSComp::jit->RestoreSavedEmuHackOps(savedBlocks); + } else { + fwrite(Memory::GetPointer(bp->start), 1, bp->size, output); + } + RestoreSavedReplacements(savedReplacements); + } + fclose(output); - if (!priorDumpWasStepping) Core_EnableStepping(false); // If emulator wasn't paused before memory dump resume emulation automatically. - MessageBoxA(hwnd,"Done.","Information",MB_OK); - EndDialog(hwnd,true); + if (!priorDumpWasStepping) { + // If emulator wasn't paused before memory dump resume emulation automatically. + Core_EnableStepping(false); + } + + MessageBoxA(hwnd, "Done.", "Information", MB_OK); + EndDialog(hwnd, true); } break; case IDCANCEL: diff --git a/Windows/DinputDevice.cpp b/Windows/DinputDevice.cpp index 4d415b7c8a..b87bc95537 100644 --- a/Windows/DinputDevice.cpp +++ b/Windows/DinputDevice.cpp @@ -19,15 +19,15 @@ #include #include #include +#include -#include "Core/HLE/sceCtrl.h" -#include "DinputDevice.h" -#include "Core/Config.h" #include "Common/Input/InputState.h" -#include "Common/System/NativeApp.h" #include "Common/Input/KeyCodes.h" +#include "Common/System/NativeApp.h" +#include "Core/Config.h" +#include "Core/HLE/sceCtrl.h" #include "Core/Reporting.h" -#include "Xinput.h" +#include "Windows/DinputDevice.h" #pragma comment(lib,"dinput8.lib") #ifdef min diff --git a/Windows/EmuThread.cpp b/Windows/EmuThread.cpp index 120130161e..a0a8a359fa 100644 --- a/Windows/EmuThread.cpp +++ b/Windows/EmuThread.cpp @@ -131,7 +131,7 @@ void MainThreadFunc() { args.push_back(string.c_str()); } bool performingRestart = NativeIsRestarting(); - NativeInit(static_cast(args.size()), &args[0], "1234", "1234", nullptr); + NativeInit(static_cast(args.size()), &args[0], "", "", nullptr); if (g_Config.iGPUBackend == (int)GPUBackend::OPENGL) { if (!useEmuThread) { @@ -202,7 +202,7 @@ void MainThreadFunc() { std::string full_error = StringFromFormat("%s\n\n%s", genericError, error_string.c_str()); std::wstring title = ConvertUTF8ToWString(err->T("GenericGraphicsError", "Graphics Error")); bool yes = IDYES == MessageBox(0, ConvertUTF8ToWString(full_error).c_str(), title.c_str(), MB_ICONERROR | MB_YESNO); - ERROR_LOG(BOOT, full_error.c_str()); + ERROR_LOG(BOOT, "%s", full_error.c_str()); if (yes) { // Change the config to the alternative and restart. diff --git a/Windows/GEDebugger/CtrlDisplayListView.cpp b/Windows/GEDebugger/CtrlDisplayListView.cpp index 78b3373a5e..cdcb68e1f7 100644 --- a/Windows/GEDebugger/CtrlDisplayListView.cpp +++ b/Windows/GEDebugger/CtrlDisplayListView.cpp @@ -1,15 +1,15 @@ +#include +#include #include "Common/System/Display.h" #include "Windows/GEDebugger/CtrlDisplayListView.h" #include "Windows/GEDebugger/GEDebugger.h" #include "Windows/InputBox.h" -#include "Windows/Main.h" +#include "Windows/main.h" #include "Core/Config.h" #include "GPU/Debugger/Breakpoints.h" #include "GPU/GPUState.h" -#include - -const PTCHAR CtrlDisplayListView::windowClass = _T("CtrlDisplayListView"); +LPCTSTR CtrlDisplayListView::windowClass = _T("CtrlDisplayListView"); const int POPUP_SUBMENU_ID_DISPLAYLISTVIEW = 8; extern HMENU g_hPopupMenus; @@ -188,7 +188,6 @@ void CtrlDisplayListView::onPaint(WPARAM wParam, LPARAM lParam) bool stall = address == list.stall; int rowY1 = rowHeight*i; - int rowY2 = rowHeight*(i+1); // draw background COLORREF backgroundColor = stall ? 0xCCCCFF : 0xFFFFFF; @@ -270,7 +269,6 @@ void CtrlDisplayListView::toggleBreakpoint() void CtrlDisplayListView::onMouseDown(WPARAM wParam, LPARAM lParam, int button) { - int x = LOWORD(lParam); int y = HIWORD(lParam); int line = y/rowHeight; diff --git a/Windows/GEDebugger/CtrlDisplayListView.h b/Windows/GEDebugger/CtrlDisplayListView.h index 319d54ec20..fe605f799e 100644 --- a/Windows/GEDebugger/CtrlDisplayListView.h +++ b/Windows/GEDebugger/CtrlDisplayListView.h @@ -1,14 +1,14 @@ #pragma once +#include #include "Common/CommonWindows.h" #include "GPU/Common/GPUDebugInterface.h" -#include class CtrlDisplayListView { HWND wnd; RECT rect; - static const PTCHAR windowClass; + static LPCTSTR windowClass; DisplayList list; HFONT font; @@ -85,4 +85,4 @@ public: void scrollAddressIntoView(); bool curAddressIsVisible(); -}; \ No newline at end of file +}; diff --git a/Windows/GEDebugger/GEDebugger.cpp b/Windows/GEDebugger/GEDebugger.cpp index 2b74b8f1b5..e35f708eb6 100644 --- a/Windows/GEDebugger/GEDebugger.cpp +++ b/Windows/GEDebugger/GEDebugger.cpp @@ -15,6 +15,7 @@ // Official git repository and contact information can be found at // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. +#include #include #include #include @@ -348,7 +349,7 @@ void CGEDebugger::UpdatePreviews() { return; } - GPUgstate state = {0}; + GPUgstate state{}; if (gpuDebug != nullptr) { state = gpuDebug->GetGState(); @@ -499,7 +500,7 @@ void CGEDebugger::PrimaryPreviewHover(int x, int y) { desc[0] = 0; } else if (x < 0 || y < 0) { // This means they left the area. - GPUgstate state = {0}; + GPUgstate state{}; if (gpuDebug != nullptr) { state = gpuDebug->GetGState(); } @@ -527,7 +528,7 @@ void CGEDebugger::SecondPreviewHover(int x, int y) { desc[0] = 0; } else if (x < 0 || y < 0) { // This means they left the area. - GPUgstate state = {0}; + GPUgstate state{}; if (gpuDebug != nullptr) { state = gpuDebug->GetGState(); } @@ -689,7 +690,7 @@ void CGEDebugger::DescribePixelRGBA(u32 pix, GPUDebugBufferFormat fmt, int x, in } void CGEDebugger::UpdateTextureLevel(int level) { - GPUgstate state = {0}; + GPUgstate state{}; if (gpuDebug != nullptr) { state = gpuDebug->GetGState(); } diff --git a/Windows/GEDebugger/SimpleGLWindow.cpp b/Windows/GEDebugger/SimpleGLWindow.cpp index 37e6b7bd28..d9b60b7418 100644 --- a/Windows/GEDebugger/SimpleGLWindow.cpp +++ b/Windows/GEDebugger/SimpleGLWindow.cpp @@ -15,11 +15,13 @@ // Official git repository and contact information can be found at // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. +#include "Common/CommonWindows.h" #include #include "Common/Math/lin/matrix4x4.h" #include "Common/GPU/OpenGL/GLSLProgram.h" #include "Common/GPU/OpenGL/GLFeatures.h" #include "Common/Common.h" +#include "Common/Log.h" #include "Windows/GEDebugger/SimpleGLWindow.h" const wchar_t *SimpleGLWindow::windowClass = L"SimpleGLWindow"; @@ -109,7 +111,7 @@ void SimpleGLWindow::SetupGL() { pfd.cDepthBits = 16; pfd.iLayerType = PFD_MAIN_PLANE; -#define ENFORCE(x, msg) { if (!(x)) { ERROR_LOG(COMMON, "SimpleGLWindow: %s (%08x)", msg, GetLastError()); return; } } +#define ENFORCE(x, msg) { if (!(x)) { ERROR_LOG(COMMON, "SimpleGLWindow: %s (%08x)", msg, (uint32_t)GetLastError()); return; } } ENFORCE(hDC_ = GetDC(hWnd_), "Unable to create DC."); ENFORCE(pixelFormat = ChoosePixelFormat(hDC_, &pfd), "Unable to match pixel format."); @@ -237,7 +239,7 @@ void SimpleGLWindow::Draw(const u8 *data, int w, int h, bool flipped, Format fmt GLint components = GL_RGBA; GLint memComponents = 0; - GLenum glfmt; + GLenum glfmt = GL_UNSIGNED_BYTE; const u8 *finalData = data; if (fmt == FORMAT_8888) { glPixelStorei(GL_UNPACK_ALIGNMENT, 4); diff --git a/Windows/GEDebugger/TabState.cpp b/Windows/GEDebugger/TabState.cpp index b72ed126a8..275adfd565 100644 --- a/Windows/GEDebugger/TabState.cpp +++ b/Windows/GEDebugger/TabState.cpp @@ -290,28 +290,28 @@ CtrlStateValues::CtrlStateValues(const TabStateRow *rows, int rowCount, HWND hwn void FormatStateRow(wchar_t *dest, const TabStateRow &info, u32 value, bool enabled, u32 otherValue, u32 otherValue2) { switch (info.fmt) { case CMD_FMT_HEX: - swprintf(dest, L"%06x", value); + swprintf(dest, 255, L"%06x", value); break; case CMD_FMT_NUM: - swprintf(dest, L"%d", value); + swprintf(dest, 255, L"%d", value); break; case CMD_FMT_FLOAT24: - swprintf(dest, L"%f", getFloat24(value)); + swprintf(dest, 255, L"%f", getFloat24(value)); break; case CMD_FMT_PTRWIDTH: value |= (otherValue & 0x00FF0000) << 8; otherValue &= 0xFFFF; - swprintf(dest, L"%08x, w=%d", value, otherValue); + swprintf(dest, 255, L"%08x, w=%d", value, otherValue); break; case CMD_FMT_XY: { int x = value & 0x3FF; int y = value >> 10; - swprintf(dest, L"%d,%d", x, y); + swprintf(dest, 255, L"%d,%d", x, y); } break; @@ -321,7 +321,7 @@ void FormatStateRow(wchar_t *dest, const TabStateRow &info, u32 value, bool enab int y1 = value >> 10; int x2 = otherValue & 0x3FF; int y2 = otherValue >> 10; - swprintf(dest, L"%d,%d - %d,%d", x1, y1, x2, y2); + swprintf(dest, 255, L"%d,%d - %d,%d", x1, y1, x2, y2); } break; @@ -330,7 +330,7 @@ void FormatStateRow(wchar_t *dest, const TabStateRow &info, u32 value, bool enab float x = getFloat24(value); float y = getFloat24(otherValue); float z = getFloat24(otherValue2); - swprintf(dest, L"%f, %f, %f", x, y, z); + swprintf(dest, 255, L"%f, %f, %f", x, y, z); } break; @@ -338,7 +338,7 @@ void FormatStateRow(wchar_t *dest, const TabStateRow &info, u32 value, bool enab { int w = 1 << (value & 0x1f); int h = 1 << ((value >> 8) & 0x1f); - swprintf(dest, L"%dx%d", w, h); + swprintf(dest, 255, L"%dx%d", w, h); } break; @@ -346,7 +346,7 @@ void FormatStateRow(wchar_t *dest, const TabStateRow &info, u32 value, bool enab { float x = (float)value / 16.0f; float y = (float)otherValue / 16.0f; - swprintf(dest, L"%fx%f", x, y); + swprintf(dest, 255, L"%fx%f", x, y); } break; @@ -354,7 +354,7 @@ void FormatStateRow(wchar_t *dest, const TabStateRow &info, u32 value, bool enab { char buffer[256]; GeDescribeVertexType(value, buffer); - swprintf(dest, L"%S", buffer); + swprintf(dest, 255, L"%S", buffer); } break; @@ -374,13 +374,13 @@ void FormatStateRow(wchar_t *dest, const TabStateRow &info, u32 value, bool enab "DXT5", }; if (value < (u32)ARRAY_SIZE(texformats)) { - swprintf(dest, L"%S", texformats[value]); + swprintf(dest, 255, L"%S", texformats[value]); } else if ((value & 0xF) < (u32)ARRAY_SIZE(texformats)) { - swprintf(dest, L"%S (extra bits %06x)", texformats[value & 0xF], value); + swprintf(dest, 255, L"%S (extra bits %06x)", texformats[value & 0xF], value); } else { - swprintf(dest, L"%06x", value); + swprintf(dest, 255, L"%06x", value); } } break; @@ -399,12 +399,12 @@ void FormatStateRow(wchar_t *dest, const TabStateRow &info, u32 value, bool enab const u8 offset = (value >> 16) & 0xFF; if (offset < 0x20 && shift < 0x20) { if (offset == 0 && shift == 0) { - swprintf(dest, L"%S ind & %02x", clutformats[palette], mask); + swprintf(dest, 255, L"%S ind & %02x", clutformats[palette], mask); } else { - swprintf(dest, L"%S (ind >> %d) & %02x, offset +%d", clutformats[palette], shift, mask, offset); + swprintf(dest, 255, L"%S (ind >> %d) & %02x, offset +%d", clutformats[palette], shift, mask, offset); } } else { - swprintf(dest, L"%06x", value); + swprintf(dest, 255, L"%06x", value); } } break; @@ -415,9 +415,9 @@ void FormatStateRow(wchar_t *dest, const TabStateRow &info, u32 value, bool enab const u32 mask = otherValue2; const u32 ref = otherValue; if (value < (u32)ARRAY_SIZE(colorTests)) { - swprintf(dest, L"pass if (c & %06x) %S (%06x & %06x)", mask, colorTests[value], ref, mask); + swprintf(dest, 255, L"pass if (c & %06x) %S (%06x & %06x)", mask, colorTests[value], ref, mask); } else { - swprintf(dest, L"%06x, ref=%06x, maks=%06x", value, ref, mask); + swprintf(dest, 255, L"%06x, ref=%06x, maks=%06x", value, ref, mask); } } break; @@ -431,13 +431,13 @@ void FormatStateRow(wchar_t *dest, const TabStateRow &info, u32 value, bool enab const u8 func = (value >> 0) & 0xff; if (func < (u8)ARRAY_SIZE(alphaTestFuncs)) { if (info.fmt == CMD_FMT_ALPHATEST) { - swprintf(dest, L"pass if (a & %02x) %S (%02x & %02x)", mask, alphaTestFuncs[func], ref, mask); + swprintf(dest, 255, L"pass if (a & %02x) %S (%02x & %02x)", mask, alphaTestFuncs[func], ref, mask); } else if (info.fmt == CMD_FMT_STENCILTEST) { // Stencil test is the other way around. - swprintf(dest, L"pass if (%02x & %02x) %S (a & %02x)", ref, mask, alphaTestFuncs[func], mask); + swprintf(dest, 255, L"pass if (%02x & %02x) %S (a & %02x)", ref, mask, alphaTestFuncs[func], mask); } } else { - swprintf(dest, L"%06x", value); + swprintf(dest, 255, L"%06x", value); } } break; @@ -446,23 +446,23 @@ void FormatStateRow(wchar_t *dest, const TabStateRow &info, u32 value, bool enab { static const char *zTestFuncs[] = { "NEVER", "ALWAYS", "==", "!=", "<", "<=", ">", ">=" }; if (value < (u32)ARRAY_SIZE(zTestFuncs)) { - swprintf(dest, L"pass if src %S dst", zTestFuncs[value]); + swprintf(dest, 255, L"pass if src %S dst", zTestFuncs[value]); } else { - swprintf(dest, L"%06x", value); + swprintf(dest, 255, L"%06x", value); } } break; case CMD_FMT_OFFSETADDR: - swprintf(dest, L"%08x", gpuDebug->GetRelativeAddress(0)); + swprintf(dest, 255, L"%08x", gpuDebug->GetRelativeAddress(0)); break; case CMD_FMT_VADDR: - swprintf(dest, L"%08x", gpuDebug->GetVertexAddress()); + swprintf(dest, 255, L"%08x", gpuDebug->GetVertexAddress()); break; case CMD_FMT_IADDR: - swprintf(dest, L"%08x", gpuDebug->GetIndexAddress()); + swprintf(dest, 255, L"%08x", gpuDebug->GetIndexAddress()); break; case CMD_FMT_MATERIALUPDATE: @@ -478,20 +478,20 @@ void FormatStateRow(wchar_t *dest, const TabStateRow &info, u32 value, bool enab "ambient, diffuse, specular", }; if (value < (u32)ARRAY_SIZE(materialTypes)) { - swprintf(dest, L"%S", materialTypes[value]); + swprintf(dest, 255, L"%S", materialTypes[value]); } else { - swprintf(dest, L"%06x", value); + swprintf(dest, 255, L"%06x", value); } } break; case CMD_FMT_SHADEMODEL: if (value == 0) { - swprintf(dest, L"flat"); + swprintf(dest, 255, L"flat"); } else if (value == 1) { - swprintf(dest, L"gouraud"); + swprintf(dest, 255, L"gouraud"); } else { - swprintf(dest, L"%06x", value); + swprintf(dest, 255, L"%06x", value); } break; @@ -503,9 +503,9 @@ void FormatStateRow(wchar_t *dest, const TabStateRow &info, u32 value, bool enab const u8 pass = (value >> 16) & 0xFF; const u8 totalValid = (u8)ARRAY_SIZE(stencilOps); if (sfail < totalValid && zfail < totalValid && pass < totalValid) { - swprintf(dest, L"fail=%S, pass/depthfail=%S, pass=%S", stencilOps[sfail], stencilOps[zfail], stencilOps[pass]); + swprintf(dest, 255, L"fail=%S, pass/depthfail=%S, pass=%S", stencilOps[sfail], stencilOps[zfail], stencilOps[pass]); } else { - swprintf(dest, L"%06x", value); + swprintf(dest, 255, L"%06x", value); } } break; @@ -551,16 +551,16 @@ void FormatStateRow(wchar_t *dest, const TabStateRow &info, u32 value, bool enab const u32 blendMode = (value >> 8); if (blendFactorA < (u8)ARRAY_SIZE(blendFactorsA) && blendFactorB < (u8)ARRAY_SIZE(blendFactorsB) && blendMode < (u32)ARRAY_SIZE(blendModes)) { - swprintf(dest, L"%S: %S, %S", blendModes[blendMode], blendFactorsA[blendFactorA], blendFactorsB[blendFactorB]); + swprintf(dest, 255, L"%S: %S, %S", blendModes[blendMode], blendFactorsA[blendFactorA], blendFactorsB[blendFactorB]); } else { - swprintf(dest, L"%06x", value); + swprintf(dest, 255, L"%06x", value); } } break; case CMD_FMT_CLEARMODE: if (value == 0) { - swprintf(dest, L"%d", value); + swprintf(dest, 255, L"%d", value); } else if ((value & ~(GE_CLEARMODE_ALL | 1)) == 0) { const char *clearmodes[] = { "1, write disabled", @@ -572,9 +572,9 @@ void FormatStateRow(wchar_t *dest, const TabStateRow &info, u32 value, bool enab "1, write alpha/stencil, depth", "1, write color, alpha/stencil, depth", }; - swprintf(dest, L"%S", clearmodes[value >> 8]); + swprintf(dest, 255, L"%S", clearmodes[value >> 8]); } else { - swprintf(dest, L"%06x", value); + swprintf(dest, 255, L"%06x", value); } break; @@ -592,9 +592,9 @@ void FormatStateRow(wchar_t *dest, const TabStateRow &info, u32 value, bool enab const u8 colorDouble = (value >> 16) & 0xFF; if (rgba <= 1 && colorDouble <= 1 && func < (u8)ARRAY_SIZE(texfuncs)) { - swprintf(dest, L"%S, %S%S", texfuncs[func], rgba ? "RGBA" : "RGB", colorDouble ? ", color doubling" : ""); + swprintf(dest, 255, L"%S, %S%S", texfuncs[func], rgba ? "RGBA" : "RGB", colorDouble ? ", color doubling" : ""); } else { - swprintf(dest, L"%06x", value); + swprintf(dest, 255, L"%06x", value); } } break; @@ -606,9 +606,9 @@ void FormatStateRow(wchar_t *dest, const TabStateRow &info, u32 value, bool enab const u8 maxLevel = (value >> 16) & 0xFF; if (swizzle <= 1 && clutLevels <= 1 && maxLevel <= 7) { - swprintf(dest, L"%S%d levels%S", swizzle ? "swizzled, " : "", maxLevel + 1, clutLevels ? ", CLUT per level" : ""); + swprintf(dest, 255, L"%S%d levels%S", swizzle ? "swizzled, " : "", maxLevel + 1, clutLevels ? ", CLUT per level" : ""); } else { - swprintf(dest, L"%06x", value); + swprintf(dest, 255, L"%06x", value); } } break; @@ -635,9 +635,9 @@ void FormatStateRow(wchar_t *dest, const TabStateRow &info, u32 value, bool enab }; if (value < ARRAY_SIZE(logicOps)) { - swprintf(dest, L"%S", logicOps[value]); + swprintf(dest, 255, L"%S", logicOps[value]); } else { - swprintf(dest, L"%06x", value); + swprintf(dest, 255, L"%06x", value); } } break; @@ -647,9 +647,9 @@ void FormatStateRow(wchar_t *dest, const TabStateRow &info, u32 value, bool enab if ((value & ~0x0101) == 0) { const bool clampS = (value & 0x0001) != 0; const bool clampT = (value & 0x0100) != 0; - swprintf(dest, L"%S s, %S t", clampS ? "clamp" : "wrap", clampT ? "clamp" : "wrap"); + swprintf(dest, 255, L"%S s, %S t", clampS ? "clamp" : "wrap", clampT ? "clamp" : "wrap"); } else { - swprintf(dest, L"%06x", value); + swprintf(dest, 255, L"%06x", value); } } break; @@ -666,9 +666,9 @@ void FormatStateRow(wchar_t *dest, const TabStateRow &info, u32 value, bool enab const float bias = (float)biasFixed / 16.0f; if (mipLevel == 0 || mipLevel == 1 || mipLevel == 2) { - swprintf(dest, L"%S: %f", mipLevelModes[mipLevel], bias); + swprintf(dest, 255, L"%S: %f", mipLevelModes[mipLevel], bias); } else { - swprintf(dest, L"%06x", value); + swprintf(dest, 255, L"%06x", value); } } break; @@ -688,9 +688,9 @@ void FormatStateRow(wchar_t *dest, const TabStateRow &info, u32 value, bool enab if ((value & ~0x0107) == 0 && textureFilters[value & 7] != NULL) { const int min = (value & 0x0007) >> 0; const int mag = (value & 0x0100) >> 8; - swprintf(dest, L"min: %S, mag: %S", textureFilters[min], textureFilters[mag]); + swprintf(dest, 255, L"min: %S, mag: %S", textureFilters[min], textureFilters[mag]); } else { - swprintf(dest, L"%06x", value); + swprintf(dest, 255, L"%06x", value); } } break; @@ -712,9 +712,9 @@ void FormatStateRow(wchar_t *dest, const TabStateRow &info, u32 value, bool enab if ((value & ~0x0303) == 0) { const int uvGen = (value & 0x0003) >> 0; const int uvProj = (value & 0x0300) >> 8; - swprintf(dest, L"gen: %S, proj: %S", uvGenModes[uvGen], uvProjModes[uvProj]); + swprintf(dest, 255, L"gen: %S, proj: %S", uvGenModes[uvGen], uvProjModes[uvProj]); } else { - swprintf(dest, L"%06x", value); + swprintf(dest, 255, L"%06x", value); } } break; @@ -723,19 +723,19 @@ void FormatStateRow(wchar_t *dest, const TabStateRow &info, u32 value, bool enab if ((value & ~0x0303) == 0) { const int sLight = (value & 0x0003) >> 0; const int tLight = (value & 0x0300) >> 8; - swprintf(dest, L"s: %d, t: %d", sLight, tLight); + swprintf(dest, 255, L"s: %d, t: %d", sLight, tLight); } else { - swprintf(dest, L"%06x", value); + swprintf(dest, 255, L"%06x", value); } break; case CMD_FMT_LIGHTMODE: if (value == 0) { - swprintf(dest, L"mixed color"); + swprintf(dest, 255, L"mixed color"); } else if (value == 1) { - swprintf(dest, L"separate specular"); + swprintf(dest, 255, L"separate specular"); } else { - swprintf(dest, L"%06x", value); + swprintf(dest, 255, L"%06x", value); } break; @@ -744,7 +744,7 @@ void FormatStateRow(wchar_t *dest, const TabStateRow &info, u32 value, bool enab const char *lightComputations[] = { "diffuse", "diffuse + spec", - "pow(diffuse) + spec", + "pow(diffuse)", "unknown (diffuse?)", }; const char *lightTypes[] = { @@ -756,20 +756,20 @@ void FormatStateRow(wchar_t *dest, const TabStateRow &info, u32 value, bool enab if ((value & ~0x0303) == 0) { const int comp = (value & 0x0003) >> 0; const int type = (value & 0x0300) >> 8; - swprintf(dest, L"type: %S, comp: %S", lightTypes[type], lightComputations[comp]); + swprintf(dest, 255, L"type: %S, comp: %S", lightTypes[type], lightComputations[comp]); } else { - swprintf(dest, L"%06x", value); + swprintf(dest, 255, L"%06x", value); } } break; case CMD_FMT_CULL: if (value == 0) { - swprintf(dest, L"front (CW)"); + swprintf(dest, 255, L"front (CW)"); } else if (value == 1) { - swprintf(dest, L"back (CCW)"); + swprintf(dest, 255, L"back (CCW)"); } else { - swprintf(dest, L"%06x", value); + swprintf(dest, 255, L"%06x", value); } break; @@ -781,23 +781,23 @@ void FormatStateRow(wchar_t *dest, const TabStateRow &info, u32 value, bool enab "points", }; if (value < (u32)ARRAY_SIZE(patchPrims)) { - swprintf(dest, L"%S", patchPrims[value]); + swprintf(dest, 255, L"%S", patchPrims[value]); } else { - swprintf(dest, L"%06x", value); + swprintf(dest, 255, L"%06x", value); } } break; case CMD_FMT_FLAG: if ((value & ~1) == 0) { - swprintf(dest, L"%d", value); + swprintf(dest, 255, L"%d", value); } else { - swprintf(dest, L"%06x", value); + swprintf(dest, 255, L"%06x", value); } break; default: - swprintf(dest, L"BAD FORMAT %06x", value); + swprintf(dest, 255, L"BAD FORMAT %06x", value); } // TODO: Turn row grey or some such? diff --git a/Windows/GEDebugger/TabVertices.cpp b/Windows/GEDebugger/TabVertices.cpp index 55eb23cc4b..580012d2fc 100644 --- a/Windows/GEDebugger/TabVertices.cpp +++ b/Windows/GEDebugger/TabVertices.cpp @@ -136,7 +136,7 @@ void CtrlVertexList::GetColumnText(wchar_t *dest, int row, int col) { if (!indices.empty()) { if (row >= (int)indices.size()) { - swprintf(dest, L"Invalid indice %d", row); + swprintf(dest, 255, L"Invalid indice %d", row); return; } row = indices[row]; @@ -146,7 +146,7 @@ void CtrlVertexList::GetColumnText(wchar_t *dest, int row, int col) { FormatVertColRaw(dest, row, col); } else { if (row >= (int)vertices.size()) { - swprintf(dest, L"Invalid vertex %d", row); + swprintf(dest, 255, L"Invalid vertex %d", row); return; } @@ -156,17 +156,17 @@ void CtrlVertexList::GetColumnText(wchar_t *dest, int row, int col) { void CtrlVertexList::FormatVertCol(wchar_t *dest, const GPUDebugVertex &vert, int col) { switch (col) { - case VERTEXLIST_COL_X: swprintf(dest, L"%f", vert.x); break; - case VERTEXLIST_COL_Y: swprintf(dest, L"%f", vert.y); break; - case VERTEXLIST_COL_Z: swprintf(dest, L"%f", vert.z); break; - case VERTEXLIST_COL_U: swprintf(dest, L"%f", vert.u); break; - case VERTEXLIST_COL_V: swprintf(dest, L"%f", vert.v); break; + case VERTEXLIST_COL_X: swprintf(dest, 255, L"%f", vert.x); break; + case VERTEXLIST_COL_Y: swprintf(dest, 255, L"%f", vert.y); break; + case VERTEXLIST_COL_Z: swprintf(dest, 255, L"%f", vert.z); break; + case VERTEXLIST_COL_U: swprintf(dest, 255, L"%f", vert.u); break; + case VERTEXLIST_COL_V: swprintf(dest, 255, L"%f", vert.v); break; case VERTEXLIST_COL_COLOR: - swprintf(dest, L"%02x%02x%02x%02x", vert.c[0], vert.c[1], vert.c[2], vert.c[3]); + swprintf(dest, 255, L"%02x%02x%02x%02x", vert.c[0], vert.c[1], vert.c[2], vert.c[3]); break; - case VERTEXLIST_COL_NX: swprintf(dest, L"%f", vert.nx); break; - case VERTEXLIST_COL_NY: swprintf(dest, L"%f", vert.ny); break; - case VERTEXLIST_COL_NZ: swprintf(dest, L"%f", vert.nz); break; + case VERTEXLIST_COL_NX: swprintf(dest, 255, L"%f", vert.nx); break; + case VERTEXLIST_COL_NY: swprintf(dest, 255, L"%f", vert.ny); break; + case VERTEXLIST_COL_NZ: swprintf(dest, 255, L"%f", vert.nz); break; default: wcscpy(dest, L"Invalid"); @@ -226,15 +226,15 @@ void CtrlVertexList::FormatVertColRawType(wchar_t *dest, const void *data, int t break; case 1: // 8-bit - swprintf(dest, L"%02x", ((const u8 *)data)[offset]); + swprintf(dest, 255, L"%02x", ((const u8 *)data)[offset]); break; case 2: // 16-bit - swprintf(dest, L"%04x", ((const u16_le *)data)[offset]); + swprintf(dest, 255, L"%04x", ((const u16_le *)data)[offset]); break; case 3: // float - swprintf(dest, L"%f", ((const float *)data)[offset]); + swprintf(dest, 255, L"%f", ((const float *)data)[offset]); break; default: @@ -252,11 +252,11 @@ void CtrlVertexList::FormatVertColRawColor(wchar_t *dest, const void *data, int case GE_VTYPE_COL_565 >> GE_VTYPE_COL_SHIFT: case GE_VTYPE_COL_5551 >> GE_VTYPE_COL_SHIFT: case GE_VTYPE_COL_4444 >> GE_VTYPE_COL_SHIFT: - swprintf(dest, L"%04x", *(const u16_le *)data); + swprintf(dest, 255, L"%04x", *(const u16_le *)data); break; case GE_VTYPE_COL_8888 >> GE_VTYPE_COL_SHIFT: - swprintf(dest, L"%08x", *(const u32_le *)data); + swprintf(dest, 255, L"%08x", *(const u32_le *)data); break; default: @@ -378,11 +378,11 @@ void CtrlMatrixList::GetColumnText(wchar_t *dest, int row, int col) { switch (col) { case MATRIXLIST_COL_NAME: - swprintf(dest, L"Bone #%d row %d", b, r); + swprintf(dest, 255, L"Bone #%d row %d", b, r); break; default: - swprintf(dest, L"%f", state.boneMatrix[offset]); + swprintf(dest, 255, L"%f", state.boneMatrix[offset]); break; } } else if (row >= MATRIXLIST_ROW_TGEN_0) { @@ -391,11 +391,11 @@ void CtrlMatrixList::GetColumnText(wchar_t *dest, int row, int col) { switch (col) { case MATRIXLIST_COL_NAME: - swprintf(dest, L"Texgen %d", r); + swprintf(dest, 255, L"Texgen %d", r); break; default: - swprintf(dest, L"%f", state.tgenMatrix[offset]); + swprintf(dest, 255, L"%f", state.tgenMatrix[offset]); break; } } else if (row >= MATRIXLIST_ROW_PROJ_0) { @@ -404,11 +404,11 @@ void CtrlMatrixList::GetColumnText(wchar_t *dest, int row, int col) { switch (col) { case MATRIXLIST_COL_NAME: - swprintf(dest, L"Proj %d", r); + swprintf(dest, 255, L"Proj %d", r); break; default: - swprintf(dest, L"%f", state.projMatrix[offset]); + swprintf(dest, 255, L"%f", state.projMatrix[offset]); break; } } else if (row >= MATRIXLIST_ROW_VIEW_0) { @@ -417,11 +417,11 @@ void CtrlMatrixList::GetColumnText(wchar_t *dest, int row, int col) { switch (col) { case MATRIXLIST_COL_NAME: - swprintf(dest, L"View %d", r); + swprintf(dest, 255, L"View %d", r); break; default: - swprintf(dest, L"%f", state.viewMatrix[offset]); + swprintf(dest, 255, L"%f", state.viewMatrix[offset]); break; } } else { @@ -430,11 +430,11 @@ void CtrlMatrixList::GetColumnText(wchar_t *dest, int row, int col) { switch (col) { case MATRIXLIST_COL_NAME: - swprintf(dest, L"World %d", r); + swprintf(dest, 255, L"World %d", r); break; default: - swprintf(dest, L"%f", state.worldMatrix[offset]); + swprintf(dest, 255, L"%f", state.worldMatrix[offset]); break; } } diff --git a/Windows/GEDebugger/VertexPreview.cpp b/Windows/GEDebugger/VertexPreview.cpp index cc3589eebc..fc28690abd 100644 --- a/Windows/GEDebugger/VertexPreview.cpp +++ b/Windows/GEDebugger/VertexPreview.cpp @@ -26,6 +26,7 @@ #include "GPU/Common/GPUDebugInterface.h" #include "GPU/Common/SplineCommon.h" #include "GPU/GPUState.h" +#include "Common/Log.h" #include "Common/MemoryUtil.h" static const char preview_fs[] = diff --git a/Windows/GPU/D3D9Context.cpp b/Windows/GPU/D3D9Context.cpp index 0ccfa8364c..67e472d0b9 100644 --- a/Windows/GPU/D3D9Context.cpp +++ b/Windows/GPU/D3D9Context.cpp @@ -108,7 +108,7 @@ bool D3D9Context::Init(HINSTANCE hInst, HWND wnd, std::string *error_message) { } if (FAILED(d3d_->GetDeviceCaps(adapterId_, D3DDEVTYPE_HAL, &d3dCaps))) { - *error_message = "GetDeviceCaps failed (???)"; + *error_message = "GetDeviceCaps failed (?)"; d3d_->Release(); return false; } diff --git a/Windows/GPU/WindowsVulkanContext.h b/Windows/GPU/WindowsVulkanContext.h index 098d737e98..49e6613e3e 100644 --- a/Windows/GPU/WindowsVulkanContext.h +++ b/Windows/GPU/WindowsVulkanContext.h @@ -34,7 +34,7 @@ public: void Resize() override; void Poll() override; - void *GetAPIContext(); + void *GetAPIContext() override; Draw::DrawContext *GetDrawContext() override { return draw_; } private: diff --git a/Windows/InputDevice.h b/Windows/InputDevice.h index 06d0ccd283..eaf6577214 100644 --- a/Windows/InputDevice.h +++ b/Windows/InputDevice.h @@ -24,6 +24,9 @@ class InputDevice { public: + virtual ~InputDevice() { + } + enum { UPDATESTATE_SKIP_PAD = 0x1234}; virtual int UpdateState() = 0; diff --git a/Windows/MainWindowMenu.cpp b/Windows/MainWindowMenu.cpp index 956c92f543..e5decc3957 100644 --- a/Windows/MainWindowMenu.cpp +++ b/Windows/MainWindowMenu.cpp @@ -1,3 +1,4 @@ +#include "ppsspp_config.h" #include #include #include @@ -28,7 +29,7 @@ #include "UI/OnScreenDisplay.h" #include "GPU/Common/PostShader.h" #include "GPU/Common/FramebufferManagerCommon.h" -#include "GPU/Common/TextureCacheCommon.h" +#include "GPU/Common/TextureDecoder.h" #include "GPU/Common/TextureScalerCommon.h" #include "Core/Config.h" @@ -258,9 +259,9 @@ namespace MainWindow { // Emulation menu TranslateMenuItem(menu, ID_EMULATION_PAUSE); - TranslateMenuItem(menu, ID_EMULATION_STOP, L"\tCtrl+W"); - TranslateMenuItem(menu, ID_EMULATION_RESET, L"\tCtrl+B"); - TranslateMenuItem(menu, ID_EMULATION_SWITCH_UMD, L"\tCtrl+U"); + TranslateMenuItem(menu, ID_EMULATION_STOP, g_Config.bSystemControls ? L"\tCtrl+W" : L""); + TranslateMenuItem(menu, ID_EMULATION_RESET, g_Config.bSystemControls ? L"\tCtrl+B" : L""); + TranslateMenuItem(menu, ID_EMULATION_SWITCH_UMD, g_Config.bSystemControls ? L"\tCtrl+U" : L""); TranslateMenuItem(menu, ID_EMULATION_ROTATION_MENU); TranslateMenuItem(menu, ID_EMULATION_ROTATION_H); TranslateMenuItem(menu, ID_EMULATION_ROTATION_V); @@ -268,7 +269,7 @@ namespace MainWindow { TranslateMenuItem(menu, ID_EMULATION_ROTATION_V_R); // Debug menu - TranslateMenuItem(menu, ID_TOGGLE_BREAK, L"\tF8", "Break"); + TranslateMenuItem(menu, ID_TOGGLE_BREAK, g_Config.bSystemControls ? L"\tF8" : L"", "Break"); TranslateMenuItem(menu, ID_DEBUG_BREAKONLOAD); TranslateMenuItem(menu, ID_DEBUG_IGNOREILLEGALREADS); TranslateMenuItem(menu, ID_DEBUG_LOADMAPFILE); @@ -276,14 +277,14 @@ namespace MainWindow { TranslateMenuItem(menu, ID_DEBUG_LOADSYMFILE); TranslateMenuItem(menu, ID_DEBUG_SAVESYMFILE); TranslateMenuItem(menu, ID_DEBUG_RESETSYMBOLTABLE); - TranslateMenuItem(menu, ID_DEBUG_TAKESCREENSHOT, L"\tF12"); + TranslateMenuItem(menu, ID_DEBUG_TAKESCREENSHOT, g_Config.bSystemControls ? L"\tF12" : L""); TranslateMenuItem(menu, ID_DEBUG_DUMPNEXTFRAME); TranslateMenuItem(menu, ID_DEBUG_SHOWDEBUGSTATISTICS); - TranslateMenuItem(menu, ID_DEBUG_DISASSEMBLY, L"\tCtrl+D"); - TranslateMenuItem(menu, ID_DEBUG_GEDEBUGGER, L"\tCtrl+G"); + TranslateMenuItem(menu, ID_DEBUG_DISASSEMBLY, g_Config.bSystemControls ? L"\tCtrl+D" : L""); + TranslateMenuItem(menu, ID_DEBUG_GEDEBUGGER, g_Config.bSystemControls ? L"\tCtrl+G" : L""); TranslateMenuItem(menu, ID_DEBUG_EXTRACTFILE); - TranslateMenuItem(menu, ID_DEBUG_LOG, L"\tCtrl+L"); - TranslateMenuItem(menu, ID_DEBUG_MEMORYVIEW, L"\tCtrl+M"); + TranslateMenuItem(menu, ID_DEBUG_LOG, g_Config.bSystemControls ? L"\tCtrl+L" : L""); + TranslateMenuItem(menu, ID_DEBUG_MEMORYVIEW, g_Config.bSystemControls ? L"\tCtrl+M" : L""); // Options menu TranslateMenuItem(menu, ID_OPTIONS_LANGUAGE); @@ -301,10 +302,10 @@ namespace MainWindow { TranslateMenuItem(menu, ID_FILE_DUMPAUDIO); // Skip display multipliers x1-x10 - TranslateMenuItem(menu, ID_OPTIONS_FULLSCREEN, L"\tAlt+Return, F11"); + TranslateMenuItem(menu, ID_OPTIONS_FULLSCREEN, g_Config.bSystemControls ? L"\tAlt+Return, F11" : L""); TranslateMenuItem(menu, ID_OPTIONS_VSYNC); TranslateMenuItem(menu, ID_OPTIONS_SHADER_MENU); - TranslateMenuItem(menu, ID_OPTIONS_SCREEN_MENU, L"\tCtrl+1"); + TranslateMenuItem(menu, ID_OPTIONS_SCREEN_MENU, g_Config.bSystemControls ? L"\tCtrl+1" : L""); TranslateMenuItem(menu, ID_OPTIONS_SCREENAUTO); // Skip rendering resolution 2x-5x.. TranslateMenuItem(menu, ID_OPTIONS_WINDOW_MENU); @@ -318,7 +319,7 @@ namespace MainWindow { TranslateMenuItem(menu, ID_OPTIONS_RENDERMODE_MENU); TranslateMenuItem(menu, ID_OPTIONS_NONBUFFEREDRENDERING); TranslateMenuItem(menu, ID_OPTIONS_BUFFEREDRENDERING); - TranslateMenuItem(menu, ID_OPTIONS_FRAMESKIP_MENU, L"\tF7"); + TranslateMenuItem(menu, ID_OPTIONS_FRAMESKIP_MENU, g_Config.bSystemControls ? L"\tF7" : L""); TranslateMenuItem(menu, ID_OPTIONS_FRAMESKIP_AUTO); TranslateMenuItem(menu, ID_OPTIONS_FRAMESKIP_0); TranslateMenuItem(menu, ID_OPTIONS_FRAMESKIPTYPE_MENU); @@ -344,8 +345,8 @@ namespace MainWindow { TranslateMenuItem(menu, ID_OPTIONS_VERTEXCACHE); TranslateMenuItem(menu, ID_OPTIONS_SHOWFPS); TranslateMenuItem(menu, ID_EMULATION_SOUND); - TranslateMenuItem(menu, ID_EMULATION_CHEATS, L"\tCtrl+T"); - TranslateMenuItem(menu, ID_EMULATION_CHAT, L"\tCtrl+C"); + TranslateMenuItem(menu, ID_EMULATION_CHEATS, g_Config.bSystemControls ? L"\tCtrl+T" : L""); + TranslateMenuItem(menu, ID_EMULATION_CHAT, g_Config.bSystemControls ? L"\tCtrl+C" : L""); // Help menu: it's translated in CreateHelpMenu. CreateHelpMenu(menu); @@ -550,15 +551,6 @@ namespace MainWindow { osm.Show(messageStream.str()); } - static void enableCheats(bool cheats) { - g_Config.bEnableCheats = cheats; - } - - static void setDisplayOptions(int options) { - g_Config.iSmallDisplayZoomType = options; - NativeMessageReceived("gpu_resized", ""); - } - static void RestartApp() { if (IsDebuggerPresent()) { PostMessage(MainWindow::GetHWND(), WM_USER_RESTART_EMUTHREAD, 0, 0); @@ -574,7 +566,6 @@ namespace MainWindow { auto gr = GetI18NCategory("Graphics"); int wmId = LOWORD(wParam); - int wmEvent = HIWORD(wParam); // Parse the menu selections: switch (wmId) { case ID_FILE_LOAD: @@ -651,6 +642,10 @@ namespace MainWindow { osm.ShowOnOff(gr->T("Cheats"), g_Config.bEnableCheats); break; case ID_EMULATION_CHAT: + if (!g_Config.bEnableNetworkChat) { + g_Config.bEnableNetworkChat = true; + UpdateCommands(); + } if (GetUIState() == UISTATE_INGAME) { NativeMessageReceived("chat screen", ""); } @@ -1397,6 +1392,7 @@ namespace MainWindow { bool isPaused = Core_IsStepping() && GetUIState() == UISTATE_INGAME; TranslateMenuItem(menu, ID_TOGGLE_BREAK, L"\tF8", isPaused ? "Run" : "Break"); + TranslateMenuItem(menu, ID_EMULATION_CHAT, L"\tCtrl+C", g_Config.bEnableNetworkChat ? "Open Chat" : "Enable Chat"); } void UpdateSwitchUMD() { diff --git a/Windows/MainWindowMenu.h b/Windows/MainWindowMenu.h index 26bdfc608e..5669335c22 100644 --- a/Windows/MainWindowMenu.h +++ b/Windows/MainWindowMenu.h @@ -2,6 +2,7 @@ #include "Common/CommonWindows.h" #include +#include "Core/System.h" namespace MainWindow { void MainWindowMenu_Process(HWND hWnd, WPARAM wParam); diff --git a/Windows/W32Util/PropertySheet.h b/Windows/W32Util/PropertySheet.h index 53ea0c5b8b..29e7814e8c 100644 --- a/Windows/W32Util/PropertySheet.h +++ b/Windows/W32Util/PropertySheet.h @@ -57,9 +57,8 @@ namespace W32Util LPCTSTR header; HFONT hTitleFont; HICON icon; - struct Page - { - Page(Tab *_tab, LPCTSTR _resource, LPCTSTR _title, LPCTSTR _subtitle = 0) + struct Page final { + Page(Tab *_tab, LPCTSTR _resource, LPCTSTR _title, LPCTSTR _subtitle = nullptr) : tab(_tab), resource(_resource), title(_title), hdrSubTitle(_subtitle) {} Tab *tab; LPCTSTR resource; @@ -78,7 +77,4 @@ namespace W32Util HFONT GetTitleFont() {return hTitleFont;} static int CALLBACK Callback(HWND hwndDlg, UINT uMsg, LPARAM lParam); }; - - - -} \ No newline at end of file +} diff --git a/Windows/W32Util/TabControl.cpp b/Windows/W32Util/TabControl.cpp index 3da65ff4b9..2d315213df 100644 --- a/Windows/W32Util/TabControl.cpp +++ b/Windows/W32Util/TabControl.cpp @@ -7,7 +7,7 @@ const DWORD tabControlStyleMask = ~(WS_POPUP | WS_TILEDWINDOW); TabControl::TabControl(HWND handle, bool noDisplayArea) - : hwnd(handle), showTabTitles(true), currentTab(0), ignoreBottomMargin(false), noDisplayArea_(noDisplayArea) + : hwnd(handle), noDisplayArea_(noDisplayArea) { SetWindowLongPtr(hwnd,GWLP_USERDATA,(LONG_PTR)this); oldProc = (WNDPROC) SetWindowLongPtr(hwnd,GWLP_WNDPROC,(LONG_PTR)wndProc); @@ -104,7 +104,7 @@ int TabControl::AppendPageToControl(const wchar_t *title) tcItem.iImage = 0; int index = TabCtrl_GetItemCount(hwnd); - int result = TabCtrl_InsertItem(hwnd,index,&tcItem); + TabCtrl_InsertItem(hwnd, index, &tcItem); return index; } diff --git a/Windows/W32Util/TabControl.h b/Windows/W32Util/TabControl.h index e6229166c4..7eb2360dcf 100644 --- a/Windows/W32Util/TabControl.h +++ b/Windows/W32Util/TabControl.h @@ -47,9 +47,9 @@ private: HWND hwnd; WNDPROC oldProc; std::vector tabs; - bool showTabTitles; - bool ignoreBottomMargin; - int currentTab; + bool showTabTitles = true; + bool ignoreBottomMargin = false; + int currentTab = 0; bool hasButtons; bool noDisplayArea_; }; diff --git a/Windows/WASAPIStream.cpp b/Windows/WASAPIStream.cpp index 9e11d558c5..347f5e0560 100644 --- a/Windows/WASAPIStream.cpp +++ b/Windows/WASAPIStream.cpp @@ -31,7 +31,7 @@ const IID IID_IAudioRenderClient = __uuidof(IAudioRenderClient); if ((punk) != NULL) \ { (punk)->Release(); (punk) = NULL; } -class CMMNotificationClient : public IMMNotificationClient { +class CMMNotificationClient final : public IMMNotificationClient { public: CMMNotificationClient() { } @@ -86,7 +86,7 @@ public: // Callback methods for device-event notifications. - HRESULT STDMETHODCALLTYPE OnDefaultDeviceChanged(EDataFlow flow, ERole role, LPCWSTR pwstrDeviceId) { + HRESULT STDMETHODCALLTYPE OnDefaultDeviceChanged(EDataFlow flow, ERole role, LPCWSTR pwstrDeviceId) override { std::lock_guard guard(lock_); if (flow != eRender || role != eMultimedia) { @@ -109,30 +109,30 @@ public: return S_OK; } - HRESULT STDMETHODCALLTYPE OnDeviceAdded(LPCWSTR pwstrDeviceId) { + HRESULT STDMETHODCALLTYPE OnDeviceAdded(LPCWSTR pwstrDeviceId) override { // Ignore. return S_OK; }; - HRESULT STDMETHODCALLTYPE OnDeviceRemoved(LPCWSTR pwstrDeviceId) { + HRESULT STDMETHODCALLTYPE OnDeviceRemoved(LPCWSTR pwstrDeviceId) override { // Ignore. return S_OK; } - HRESULT STDMETHODCALLTYPE OnDeviceStateChanged(LPCWSTR pwstrDeviceId, DWORD dwNewState) { + HRESULT STDMETHODCALLTYPE OnDeviceStateChanged(LPCWSTR pwstrDeviceId, DWORD dwNewState) override { // Ignore. return S_OK; } - HRESULT STDMETHODCALLTYPE OnPropertyValueChanged(LPCWSTR pwstrDeviceId, const PROPERTYKEY key) { + HRESULT STDMETHODCALLTYPE OnPropertyValueChanged(LPCWSTR pwstrDeviceId, const PROPERTYKEY key) override { INFO_LOG(SCEAUDIO, "Changed audio device property " "{%8.8x-%4.4x-%4.4x-%2.2x%2.2x-%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x}#%d", - key.fmtid.Data1, key.fmtid.Data2, key.fmtid.Data3, + (uint32_t)key.fmtid.Data1, key.fmtid.Data2, key.fmtid.Data3, key.fmtid.Data4[0], key.fmtid.Data4[1], key.fmtid.Data4[2], key.fmtid.Data4[3], key.fmtid.Data4[4], key.fmtid.Data4[5], key.fmtid.Data4[6], key.fmtid.Data4[7], - key.pid); + (int)key.pid); return S_OK; } diff --git a/Windows/WindowsHost.cpp b/Windows/WindowsHost.cpp index d7b3d93396..1bd7da7a3d 100644 --- a/Windows/WindowsHost.cpp +++ b/Windows/WindowsHost.cpp @@ -20,7 +20,7 @@ #include // For shell links -#include "windows.h" +#include "Common/CommonWindows.h" #include "winnls.h" #include "shobjidl.h" #include "objbase.h" @@ -81,8 +81,8 @@ static BOOL PostDialogMessage(Dialog *dialog, UINT message, WPARAM wParam = 0, L WindowsHost::WindowsHost(HINSTANCE hInstance, HWND mainWindow, HWND displayWindow) : hInstance_(hInstance), - mainWindow_(mainWindow), - displayWindow_(displayWindow) + displayWindow_(displayWindow), + mainWindow_(mainWindow) { g_mouseDeltaX = 0; g_mouseDeltaY = 0; @@ -261,7 +261,8 @@ void WindowsHost::PollControllers() { } void WindowsHost::BootDone() { - g_symbolMap->SortSymbols(); + if (g_symbolMap) + g_symbolMap->SortSymbols(); PostMessage(mainWindow_, WM_USER + 1, 0, 0); SetDebugMode(!g_Config.bAutoRun); @@ -295,6 +296,8 @@ static std::string SymbolMapFilename(const char *currentFilename, const char* ex } bool WindowsHost::AttemptLoadSymbolMap() { + if (!g_symbolMap) + return false; bool result1 = g_symbolMap->LoadSymbolMap(SymbolMapFilename(PSP_CoreParameter().fileToStart.c_str(),".ppmap").c_str()); // Load the old-style map file. if (!result1) @@ -304,11 +307,13 @@ bool WindowsHost::AttemptLoadSymbolMap() { } void WindowsHost::SaveSymbolMap() { - g_symbolMap->SaveSymbolMap(SymbolMapFilename(PSP_CoreParameter().fileToStart.c_str(),".ppmap").c_str()); + if (g_symbolMap) + g_symbolMap->SaveSymbolMap(SymbolMapFilename(PSP_CoreParameter().fileToStart.c_str(),".ppmap").c_str()); } void WindowsHost::NotifySymbolMapUpdated() { - g_symbolMap->SortSymbols(); + if (g_symbolMap) + g_symbolMap->SortSymbols(); PostMessage(mainWindow_, WM_USER + 1, 0, 0); } diff --git a/Windows/XinputDevice.h b/Windows/XinputDevice.h index fc640e708e..bcff3e5db8 100644 --- a/Windows/XinputDevice.h +++ b/Windows/XinputDevice.h @@ -1,8 +1,8 @@ #pragma once -#include "InputDevice.h" -#include "Xinput.h" +#include #include "Core/HLE/sceCtrl.h" +#include "Windows/InputDevice.h" class XinputDevice final : public InputDevice { public: diff --git a/Windows/main.cpp b/Windows/main.cpp index 4d948cfe5b..34bac908a1 100644 --- a/Windows/main.cpp +++ b/Windows/main.cpp @@ -104,6 +104,9 @@ static std::string restartArgs; HMENU g_hPopupMenus; int g_activeWindow = 0; +static std::thread inputBoxThread; +static bool inputBoxRunning = false; + void OpenDirectory(const char *path) { PIDLIST_ABSOLUTE pidl = ILCreateFromPath(ConvertUTF8ToWString(ReplaceAll(path, "/", "\\")).c_str()); if (pidl) { @@ -307,6 +310,8 @@ bool System_GetPropertyBool(SystemProperty prop) { #else return false; #endif + case SYSPROP_CAN_JIT: + return true; default: return false; } @@ -386,12 +391,19 @@ void EnableCrashingOnCrashes() { } void System_InputBoxGetString(const std::string &title, const std::string &defaultValue, std::function cb) { - std::string out; - if (InputBox_GetString(MainWindow::GetHInstance(), MainWindow::GetHWND(), ConvertUTF8ToWString(title).c_str(), defaultValue, out)) { - NativeInputBoxReceived(cb, true, out); - } else { - NativeInputBoxReceived(cb, false, ""); + if (inputBoxRunning) { + inputBoxThread.join(); } + + inputBoxRunning = true; + inputBoxThread = std::thread([=] { + std::string out; + if (InputBox_GetString(MainWindow::GetHInstance(), MainWindow::GetHWND(), ConvertUTF8ToWString(title).c_str(), defaultValue, out)) { + NativeInputBoxReceived(cb, true, out); + } else { + NativeInputBoxReceived(cb, false, ""); + } + }); } static std::string GetDefaultLangRegion() { @@ -417,6 +429,7 @@ static std::string GetDefaultLangRegion() { static const int EXIT_CODE_VULKAN_WORKS = 42; +#ifndef _DEBUG static bool DetectVulkanInExternalProcess() { std::wstring workingDirectory; std::wstring moduleFilename; @@ -447,6 +460,7 @@ static bool DetectVulkanInExternalProcess() { return exitCode == EXIT_CODE_VULKAN_WORKS; } +#endif std::vector GetWideCmdLine() { wchar_t **wargv; @@ -483,13 +497,17 @@ static void WinMainInit() { #endif PROFILE_INIT(); -#if defined(_M_X64) && defined(_MSC_VER) && _MSC_VER < 1900 +#if PPSSPP_ARCH(AMD64) && defined(_MSC_VER) && _MSC_VER < 1900 // FMA3 support in the 2013 CRT is broken on Vista and Windows 7 RTM (fixed in SP1). Just disable it. _set_FMA3_enable(0); #endif } static void WinMainCleanup() { + if (inputBoxRunning) { + inputBoxThread.join(); + inputBoxRunning = false; + } net::Shutdown(); CoUninitialize(); @@ -656,7 +674,6 @@ int WINAPI WinMain(HINSTANCE _hInstance, HINSTANCE hPrevInstance, LPSTR szCmdLin MainWindow::Show(_hInstance); HWND hwndMain = MainWindow::GetHWND(); - HWND hwndDisplay = MainWindow::GetDisplayHWND(); //initialize custom controls CtrlDisAsmView::init(); @@ -703,16 +720,16 @@ int WINAPI WinMain(HINSTANCE _hInstance, HINSTANCE hPrevInstance, LPSTR szCmdLin { case WINDOW_MAINWINDOW: wnd = hwndMain; - accel = hAccelTable; + accel = g_Config.bSystemControls ? hAccelTable : NULL; break; case WINDOW_CPUDEBUGGER: - wnd = disasmWindow ? disasmWindow->GetDlgHandle() : 0; - accel = hDebugAccelTable; + wnd = disasmWindow ? disasmWindow->GetDlgHandle() : NULL; + accel = g_Config.bSystemControls ? hDebugAccelTable : NULL; break; case WINDOW_GEDEBUGGER: default: - wnd = 0; - accel = 0; + wnd = NULL; + accel = NULL; break; } diff --git a/Windows/main.h b/Windows/main.h index a47bc081b5..8f04b0ede4 100644 --- a/Windows/main.h +++ b/Windows/main.h @@ -18,6 +18,7 @@ #pragma once +#include "ppsspp_config.h" #include "Debugger/Debugger_Disasm.h" #include "Debugger/Debugger_MemoryDlg.h" #include "Common/CommonWindows.h" diff --git a/Windows/ppsspp.rc b/Windows/ppsspp.rc index 9f72a764b8..0fe212de73 100644 --- a/Windows/ppsspp.rc +++ b/Windows/ppsspp.rc @@ -307,7 +307,7 @@ END #include "aboutbox.rc" #endif -IDD_MEMORY DIALOGEX 0, 0, 700, 287 +IDD_MEMORY DIALOGEX 0, 0, 700, 310 STYLE DS_SETFONT | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME EXSTYLE WS_EX_TOOLWINDOW CAPTION "Memory View" @@ -321,14 +321,16 @@ BEGIN CONTROL "Normal",IDC_MODENORMAL,"Button",BS_AUTORADIOBUTTON | WS_GROUP,198,9,40,9 CONTROL "Symbols",IDC_MODESYMBOLS,"Button",BS_AUTORADIOBUTTON,241,9,43,8 GROUPBOX "Mode",IDC_STATIC,191,0,104,22 - AUTOCHECKBOX "Show Offsets",IDC_SHOWOFFSETS,300,9,55,8 - COMBOBOX IDC_REGIONS,87,5,88,139,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + AUTOCHECKBOX "Show Offsets",IDC_SHOWOFFSETS,300,9,55,8 + COMBOBOX IDC_REGIONS,95,5,88,139,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP LISTBOX IDC_SEARCH_RESULTS,557,14,140,272,LBS_SORT | LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP EDITTEXT IDC_SEARCH_BOX,397,6,100,13,ES_AUTOHSCROLL PUSHBUTTON "Search",IDC_BUTTON_SEARCH,504,5,50,14 - LTEXT "Search:",IDC_STATIC,369,6,27,8 + LTEXT "Search:",IDC_STATIC,369,8,27,8 + + CONTROL "",IDC_MEMVIEW_STATUS,"msctls_statusbar32",WS_CHILD | WS_VISIBLE,0,286,700,24 END IDD_INPUTBOX DIALOGEX 0, 0, 163, 55 @@ -376,7 +378,7 @@ BEGIN PUSHBUTTON "Cancel",IDC_BREAKPOINT_CANCEL,186,98,42,14 END -IDD_DUMPMEMORY DIALOGEX 0, 0, 230, 85 +IDD_DUMPMEMORY DIALOGEX 0, 0, 230, 100 STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "Dump memory" FONT 8, "MS Shell Dlg", 400, 0, 0x1 @@ -393,8 +395,9 @@ BEGIN CONTROL "VRAM",IDC_DUMP_VRAM,"Button",BS_AUTORADIOBUTTON,152,22,35,10 CONTROL "Scratchpad",IDC_DUMP_SCRATCHPAD,"Button",BS_AUTORADIOBUTTON,152,34,52,10 CONTROL "Custom range",IDC_DUMP_CUSTOMRANGE,"Button",BS_AUTORADIOBUTTON,152,46,61,10 - DEFPUSHBUTTON "OK",IDOK,117,64,50,14 - PUSHBUTTON "Cancel",IDCANCEL,173,64,50,14 + CONTROL "Include jit checks and replacements",IDC_DUMP_INCLUDEHACKS,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,41,58,120,14 + DEFPUSHBUTTON "OK",IDOK,117,79,50,14 + PUSHBUTTON "Cancel",IDCANCEL,173,79,50,14 END diff --git a/Windows/resource.h b/Windows/resource.h index 1c82abbf5e..bc4dfb7aa1 100644 --- a/Windows/resource.h +++ b/Windows/resource.h @@ -263,6 +263,7 @@ #define ID_DISASM_ASSEMBLE 40091 #define ID_DISASM_ADDNEWBREAKPOINT 40092 #define ID_DISASM_EDITBREAKPOINT 40093 +#define IDC_DUMP_INCLUDEHACKS 40094 #define ID_EMULATION_CHEATS 40096 #define ID_HELP_CHINESE_FORUM 40097 #define ID_OPTIONS_MORE_SETTINGS 40098 @@ -374,6 +375,7 @@ #define IDC_GEDBG_STEPCOUNT_COMBO 40203 #define ID_FILE_DUMP_VIDEO_OUTPUT 40204 #define ID_EMULATION_CHAT 40205 +#define IDC_MEMVIEW_STATUS 40206 // Dummy option to let the buffered rendering hotkey cycle through all the options. #define ID_OPTIONS_BUFFEREDRENDERINGDUMMY 40500 @@ -389,7 +391,7 @@ #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NEXT_RESOURCE_VALUE 256 -#define _APS_NEXT_COMMAND_VALUE 40206 +#define _APS_NEXT_COMMAND_VALUE 40207 #define _APS_NEXT_CONTROL_VALUE 1202 #define _APS_NEXT_SYMED_VALUE 101 #endif diff --git a/Windows/stdafx.h b/Windows/stdafx.h index a7f4a49287..e8c667e29a 100644 --- a/Windows/stdafx.h +++ b/Windows/stdafx.h @@ -17,8 +17,6 @@ #pragma once -#pragma warning(disable:4996) - #undef _WIN32_WINNT #if defined(_MSC_VER) && _MSC_VER < 1700 @@ -36,6 +34,7 @@ #undef _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES #define _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES 1 +#ifndef __clang__ #if defined _M_IX86 #pragma comment(linker,"/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='x86' publicKeyToken='6595b64144ccf1df' language='*'\"") #elif defined _M_X64 @@ -43,6 +42,7 @@ #else #pragma comment(linker,"/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"") #endif +#endif #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN diff --git a/android/AndroidManifest.xml b/android/AndroidManifest.xml index 964747a804..cc8739cf29 100644 --- a/android/AndroidManifest.xml +++ b/android/AndroidManifest.xml @@ -2,8 +2,8 @@ + android:versionCode="111020000" + android:versionName="1.11.2.0"> diff --git a/android/jni/Android.mk b/android/jni/Android.mk index e3e9ba19ff..7475abcf13 100644 --- a/android/jni/Android.mk +++ b/android/jni/Android.mk @@ -403,6 +403,7 @@ EXEC_AND_LIB_FILES := \ $(SRC)/Core/WebServer.cpp \ $(SRC)/Core/Debugger/Breakpoints.cpp \ $(SRC)/Core/Debugger/DisassemblyManager.cpp \ + $(SRC)/Core/Debugger/MemBlockInfo.cpp \ $(SRC)/Core/Debugger/SymbolMap.cpp \ $(SRC)/Core/Debugger/WebSocket.cpp \ $(SRC)/Core/Debugger/WebSocket/BreakpointSubscriber.cpp \ @@ -626,10 +627,6 @@ LOCAL_SRC_FILES := \ $(SRC)/UI/TextureUtil.cpp \ $(SRC)/UI/ComboKeyMappingScreen.cpp -ifeq ($(findstring armeabi-v7a,$(TARGET_ARCH_ABI)),armeabi-v7a) -LOCAL_CFLAGS := $(LOCAL_CFLAGS) -DARM -endif - ifneq ($(SKIPAPP),1) include $(BUILD_SHARED_LIBRARY) endif diff --git a/android/jni/Locals.mk b/android/jni/Locals.mk index 532693d05b..0841c6cb3e 100644 --- a/android/jni/Locals.mk +++ b/android/jni/Locals.mk @@ -38,7 +38,7 @@ ifeq ($(findstring armeabi-v7a,$(TARGET_ARCH_ABI)),armeabi-v7a) LOCAL_LDLIBS += $(LOCAL_PATH)/../../ffmpeg/android/armv7/lib/libavutil.a LOCAL_C_INCLUDES += $(LOCAL_PATH)/../../ffmpeg/android/armv7/include - LOCAL_CFLAGS := $(LOCAL_CFLAGS) -D_ARCH_32 -DARM -DARMEABI_V7A + LOCAL_CFLAGS := $(LOCAL_CFLAGS) -D_ARCH_32 endif ifeq ($(TARGET_ARCH_ABI),armeabi) LOCAL_LDLIBS += $(LOCAL_PATH)/../../ffmpeg/android/armv6/lib/libavformat.a @@ -48,7 +48,7 @@ ifeq ($(TARGET_ARCH_ABI),armeabi) LOCAL_LDLIBS += $(LOCAL_PATH)/../../ffmpeg/android/armv6/lib/libavutil.a LOCAL_C_INCLUDES += $(LOCAL_PATH)/../../ffmpeg/android/armv6/include - LOCAL_CFLAGS := $(LOCAL_CFLAGS) -D_ARCH_32 -DARM -DARMEABI -march=armv6 + LOCAL_CFLAGS := $(LOCAL_CFLAGS) -D_ARCH_32 -march=armv6 endif ifeq ($(TARGET_ARCH_ABI),x86) LOCAL_LDLIBS += $(LOCAL_PATH)/../../ffmpeg/android/x86/lib/libavformat.a diff --git a/android/jni/TestRunner.cpp b/android/jni/TestRunner.cpp index e2908badb9..3f9feed41a 100644 --- a/android/jni/TestRunner.cpp +++ b/android/jni/TestRunner.cpp @@ -59,7 +59,7 @@ static std::string TrimNewlines(const std::string &s) { } bool TestsAvailable() { -#ifdef IOS +#if PPSSPP_PLATFORM(IOS) std::string testDirectory = g_Config.flash0Directory + "../"; #else std::string testDirectory = g_Config.memStickDirectory; @@ -74,7 +74,7 @@ bool TestsAvailable() { bool RunTests() { std::string output; -#ifdef IOS +#if PPSSPP_PLATFORM(IOS) std::string baseDirectory = g_Config.flash0Directory + "../"; #else std::string baseDirectory = g_Config.memStickDirectory; @@ -84,6 +84,10 @@ bool RunTests() { } #endif + GraphicsContext *tempCtx = PSP_CoreParameter().graphicsContext; + // Clear the context during tests. We set it back later. + PSP_CoreParameter().graphicsContext = nullptr; + CoreParameter coreParam; coreParam.cpuCore = (CPUCore)g_Config.iCpuCore; coreParam.gpuCore = GPUCORE_SOFTWARE; @@ -175,6 +179,8 @@ bool RunTests() { PSP_CoreParameter().pixelWidth = pixel_xres; PSP_CoreParameter().pixelHeight = pixel_yres; PSP_CoreParameter().headLess = false; + PSP_CoreParameter().graphicsContext = tempCtx; + g_Config.sReportHost = savedReportHost; return true; // Managed to execute the tests. Says nothing about the result. } diff --git a/android/jni/app-android.cpp b/android/jni/app-android.cpp index a5a1724325..287c8b2ac8 100644 --- a/android/jni/app-android.cpp +++ b/android/jni/app-android.cpp @@ -81,6 +81,7 @@ struct JNIEnv {}; #include "Core/Config.h" #include "Core/ConfigValues.h" #include "Core/Loaders.h" +#include "Core/FileLoaders/LocalFileLoader.h" #include "Core/System.h" #include "Core/HLE/sceUsbCam.h" #include "Core/HLE/sceUsbGps.h" @@ -93,6 +94,9 @@ struct JNIEnv {}; bool useCPUThread = true; +// We'll turn this on when we target Android 12. +bool useScopedStorageIfRequired = false; + enum class EmuThreadState { DISABLED, START_REQUESTED, @@ -124,6 +128,8 @@ std::string langRegion; std::string mogaVersion; std::string boardName; +std::string g_extFilesDir; + std::vector g_additionalStorageDirs; static float left_joystick_x_async; @@ -163,6 +169,10 @@ static float g_safeInsetTop = 0.0; static float g_safeInsetBottom = 0.0; static jmethodID postCommand; + +static jmethodID openContentUri; +static jmethodID closeContentUri; + static jobject nativeActivity; static volatile bool exitRenderLoop; static bool renderLoopRunning; @@ -174,6 +184,7 @@ static float dp_yscale = 1.0f; static bool renderer_inited = false; static bool sustainedPerfSupported = false; +static std::mutex renderLock; // See NativeQueryConfig("androidJavaGL") to change this value. static bool javaGL = true; @@ -221,6 +232,46 @@ void AndroidLogger::Log(const LogMessage &message) { } } +bool Android_IsContentUri(const std::string &filename) { + return startsWith(filename, "content://"); +} + +int Android_OpenContentUriFd(const std::string &filename) { + if (!nativeActivity) { + return -1; + } + auto env = getEnv(); + jstring param = env->NewStringUTF(filename.c_str()); + int fd = env->CallIntMethod(nativeActivity, openContentUri, param); + return fd; +} + +class ContentURIFileLoader : public ProxiedFileLoader { +public: + ContentURIFileLoader(const std::string &filename) + : ProxiedFileLoader(nullptr) { // we overwrite the nullptr below + int fd = Android_OpenContentUriFd(filename); + INFO_LOG(SYSTEM, "Fd %d for content URI: '%s'", fd, filename.c_str()); + backend_ = new LocalFileLoader(fd, filename); + } + + bool ExistsFast() override { + if (!nativeActivity) { + // Assume it does if we don't have a NativeActivity right now. + return true; + } + return backend_->ExistsFast(); + } +}; + +class AndroidContentLoaderFactory : public FileLoaderFactory { +public: + AndroidContentLoaderFactory() {} + FileLoader *ConstructFileLoader(const std::string &filename) override { + return new ContentURIFileLoader(filename); + } +}; + JNIEnv* getEnv() { JNIEnv *env; int status = gJvm->GetEnv((void**)&env, JNI_VERSION_1_6); @@ -432,16 +483,23 @@ bool System_GetPropertyBool(SystemProperty prop) { case SYSPROP_HAS_IMAGE_BROWSER: return true; case SYSPROP_HAS_FILE_BROWSER: - return false; // We kind of have but needs more work. + // It's only really needed with scoped storage, but why not make it available + // as far back as possible - works just fine. + return androidVersion >= 19; // when ACTION_OPEN_DOCUMENT was added case SYSPROP_HAS_FOLDER_BROWSER: // Uses OPEN_DOCUMENT_TREE to let you select a folder. - return androidVersion >= 21; + return androidVersion >= 21; // when ACTION_OPEN_DOCUMENT_TREE was added case SYSPROP_APP_GOLD: #ifdef GOLD return true; #else return false; #endif + case SYSPROP_CAN_JIT: + return true; + case SYSPROP_ANDROID_SCOPED_STORAGE: + if (useScopedStorageIfRequired && androidVersion >= 28) + return true; default: return false; } @@ -459,6 +517,7 @@ std::string GetJavaString(JNIEnv *env, jstring jstr) { extern "C" void Java_org_ppsspp_ppsspp_NativeActivity_registerCallbacks(JNIEnv *env, jobject obj) { nativeActivity = env->NewGlobalRef(obj); postCommand = env->GetMethodID(env->GetObjectClass(obj), "postCommand", "(Ljava/lang/String;Ljava/lang/String;)V"); + openContentUri = env->GetMethodID(env->GetObjectClass(obj), "openContentUri", "(Ljava/lang/String;)I"); } extern "C" void Java_org_ppsspp_ppsspp_NativeActivity_unregisterCallbacks(JNIEnv *env, jobject obj) { @@ -557,7 +616,7 @@ static void parse_args(std::vector &args, const std::string value) extern "C" void Java_org_ppsspp_ppsspp_NativeApp_init (JNIEnv *env, jclass, jstring jmodel, jint jdeviceType, jstring jlangRegion, jstring japkpath, - jstring jdataDir, jstring jexternalStorageDir, jstring jadditionalStorageDirs, jstring jlibraryDir, jstring jcacheDir, jstring jshortcutParam, + jstring jdataDir, jstring jexternalStorageDir, jstring jexternalFilesDir, jstring jadditionalStorageDirs, jstring jlibraryDir, jstring jcacheDir, jstring jshortcutParam, jint jAndroidVersion, jstring jboard) { setCurrentThreadName("androidInit"); @@ -567,6 +626,7 @@ extern "C" void Java_org_ppsspp_ppsspp_NativeApp_init EARLY_LOG("NativeApp.init() -- begin"); PROFILE_INIT(); + std::lock_guard guard(renderLock); renderer_inited = false; androidVersion = jAndroidVersion; deviceType = jdeviceType; @@ -588,6 +648,9 @@ extern "C" void Java_org_ppsspp_ppsspp_NativeApp_init std::string externalStorageDir = GetJavaString(env, jexternalStorageDir); std::string additionalStorageDirsString = GetJavaString(env, jadditionalStorageDirs); + std::string externalFilesDir = GetJavaString(env, jexternalFilesDir); + + g_extFilesDir = externalFilesDir; if (!additionalStorageDirsString.empty()) { SplitString(additionalStorageDirsString, ':', g_additionalStorageDirs); @@ -636,6 +699,11 @@ extern "C" void Java_org_ppsspp_ppsspp_NativeApp_init NativeInit((int)args.size(), &args[0], user_data_path.c_str(), externalStorageDir.c_str(), cacheDir.c_str()); + std::unique_ptr factory(new AndroidContentLoaderFactory()); + + // Register a content URI file loader. + RegisterFileLoaderFactory("content://", std::move(factory)); + // No need to use EARLY_LOG anymore. retry: @@ -776,9 +844,14 @@ extern "C" void Java_org_ppsspp_ppsspp_NativeApp_shutdown(JNIEnv *, jclass) { INFO_LOG(G3D, "Not shutting down renderer - not initialized"); } - inputBoxCallbacks.clear(); - NativeShutdown(); - VFSShutdown(); + { + std::lock_guard guard(renderLock); + inputBoxCallbacks.clear(); + NativeShutdown(); + VFSShutdown(); + } + + std::lock_guard guard(frameCommandLock); while (frameCommands.size()) frameCommands.pop(); INFO_LOG(SYSTEM, "NativeApp.shutdown() -- end"); @@ -929,10 +1002,14 @@ extern "C" void JNICALL Java_org_ppsspp_ppsspp_NativeApp_sendInputBox(JNIEnv *en NativeInputBoxReceived(entry->second, result, value); } -void UpdateRunLoopAndroid(JNIEnv *env) { +void LockedNativeUpdateRender() { + std::lock_guard renderGuard(renderLock); NativeUpdate(); - NativeRender(graphicsContext); +} + +void UpdateRunLoopAndroid(JNIEnv *env) { + LockedNativeUpdateRender(); std::lock_guard guard(frameCommandLock); if (!nativeActivity) { @@ -1352,10 +1429,7 @@ extern "C" bool JNICALL Java_org_ppsspp_ppsspp_NativeActivity_runEGLRenderLoop(J } } else { while (!exitRenderLoop) { - NativeUpdate(); - - NativeRender(graphicsContext); - + LockedNativeUpdateRender(); graphicsContext->SwapBuffers(); ProcessFrameCommands(env); diff --git a/android/jni/app-android.h b/android/jni/app-android.h index aa21dd9ade..3a4935d63b 100644 --- a/android/jni/app-android.h +++ b/android/jni/app-android.h @@ -2,6 +2,7 @@ #include "ppsspp_config.h" +#include #include "Common/LogManager.h" #if PPSSPP_PLATFORM(ANDROID) @@ -17,3 +18,5 @@ class AndroidLogger : public LogListener { public: void Log(const LogMessage &message) override; }; + +extern std::string g_extFilesDir; diff --git a/android/src/org/ppsspp/ppsspp/NativeActivity.java b/android/src/org/ppsspp/ppsspp/NativeActivity.java index 598bd3849f..4e22771325 100644 --- a/android/src/org/ppsspp/ppsspp/NativeActivity.java +++ b/android/src/org/ppsspp/ppsspp/NativeActivity.java @@ -402,8 +402,10 @@ public abstract class NativeActivity extends Activity { String extStorageState = Environment.getExternalStorageState(); String extStorageDir = Environment.getExternalStorageDirectory().getAbsolutePath(); + String externalFilesDir = this.getExternalFilesDir(null).getAbsolutePath(); Log.i(TAG, "Ext storage: " + extStorageState + " " + extStorageDir); + Log.i(TAG, "Ext files dir: " + externalFilesDir); String additionalStorageDirs = ""; try { @@ -442,7 +444,7 @@ public abstract class NativeActivity extends Activity { overrideShortcutParam = null; NativeApp.audioConfig(optimalFramesPerBuffer, optimalSampleRate); - NativeApp.init(model, deviceType, languageRegion, apkFilePath, dataDir, extStorageDir, additionalStorageDirs, libraryDir, cacheDir, shortcut, Build.VERSION.SDK_INT, Build.BOARD); + NativeApp.init(model, deviceType, languageRegion, apkFilePath, dataDir, extStorageDir, externalFilesDir, additionalStorageDirs, libraryDir, cacheDir, shortcut, Build.VERSION.SDK_INT, Build.BOARD); // Allow C++ to tell us to use JavaGL or not. javaGL = "true".equalsIgnoreCase(NativeApp.queryConfig("androidJavaGL")); @@ -782,8 +784,8 @@ public abstract class NativeActivity extends Activity { // I've seen crashes that seem to indicate that sometimes it hasn't... NativeApp.audioShutdown(); if (shuttingDown || isFinishing()) { - unregisterCallbacks(); NativeApp.shutdown(); + unregisterCallbacks(); initialized = false; } navigationCallbackView = null; @@ -1142,8 +1144,10 @@ public abstract class NativeActivity extends Activity { } else if (requestCode == RESULT_BROWSE_FILE) { Uri selectedFile = data.getData(); if (selectedFile != null) { - // NativeApp.sendMessage("br"); + // Grab permanent permission so we can show it in recents list etc. + getContentResolver().takePersistableUriPermission(selectedFile, Intent.FLAG_GRANT_READ_URI_PERMISSION); Log.i(TAG, "Browse file finished:" + selectedFile.toString()); + NativeApp.sendMessage("browse_fileSelect", selectedFile.toString()); } } else if (requestCode == RESULT_OPEN_DOCUMENT_TREE) { Uri selectedFile = data.getData(); @@ -1289,6 +1293,7 @@ public abstract class NativeActivity extends Activity { Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT); intent.addCategory(Intent.CATEGORY_OPENABLE); intent.setType("application/octet-stream"); + intent.addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION); //intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, pickerInitialUri); startActivityForResult(intent, RESULT_BROWSE_FILE); } catch (Exception e) { diff --git a/android/src/org/ppsspp/ppsspp/NativeApp.java b/android/src/org/ppsspp/ppsspp/NativeApp.java index 4a72d4c6a9..9442f9b809 100644 --- a/android/src/org/ppsspp/ppsspp/NativeApp.java +++ b/android/src/org/ppsspp/ppsspp/NativeApp.java @@ -12,7 +12,7 @@ public class NativeApp { public static final int DEVICE_TYPE_TV = 1; public static final int DEVICE_TYPE_DESKTOP = 2; - public static native void init(String model, int deviceType, String languageRegion, String apkPath, String dataDir, String externalStorageDir, String additionalStorageDirs, String libraryDir, String cacheDir, String shortcutParam, int androidVersion, String board); + public static native void init(String model, int deviceType, String languageRegion, String apkPath, String dataDir, String externalStorageDir, String extFilesDir, String additionalStorageDirs, String libraryDir, String cacheDir, String shortcutParam, int androidVersion, String board); public static native void audioInit(); public static native void audioShutdown(); public static native void audioConfig(int optimalFramesPerBuffer, int optimalSampleRate); diff --git a/android/src/org/ppsspp/ppsspp/PpssppActivity.java b/android/src/org/ppsspp/ppsspp/PpssppActivity.java index 4269f7f98b..d03fcdab47 100644 --- a/android/src/org/ppsspp/ppsspp/PpssppActivity.java +++ b/android/src/org/ppsspp/ppsspp/PpssppActivity.java @@ -6,6 +6,7 @@ import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.os.Looper; +import android.os.ParcelFileDescriptor; import android.util.Log; public class PpssppActivity extends NativeActivity { @@ -112,4 +113,19 @@ public class PpssppActivity extends NativeActivity { } }); } + + public int openContentUri(String uriString) { + try { + Uri uri = Uri.parse(uriString); + ParcelFileDescriptor filePfd = getContentResolver().openFileDescriptor(uri, "r"); + if (filePfd == null) { + Log.e(TAG, "Failed to get file descriptor for " + uriString); + return -1; + } + return filePfd.detachFd(); // Take ownership of the fd. + } catch (Exception e) { + Log.e(TAG, "Exception opening content uri: " + e.toString()); + return -1; + } + } } diff --git a/assets/compat.ini b/assets/compat.ini index 0b5286fad5..6eeae3ca30 100644 --- a/assets/compat.ini +++ b/assets/compat.ini @@ -600,6 +600,9 @@ NPJH50518 = true ULUS10103 = true ULES00381 = true ULJM05091 = true +# GachiTora! Abarenbou Kyoushi in High School need it to render shadows properly (see #14136) +ULJS00355 = true +NPJH50409 = true [DisableAccurateDepth] # Midnight Club: LA Remix @@ -786,6 +789,10 @@ ULJM06218 = true UCAS40346 = true UCAS40347 = true +# Boku no Natsuyasumi: Mushi Mushi Hakase to Teppen-yama no Himitsu!! +UCJS10038 = true +UCJS18013 = true + # Note! This whole flag is disabled temporarily by appending "Disabled" to its name). See 7914 [YugiohSaveFixDisabled] # The cause of Yu-gi-oh series 's bad save (cannot save) are load "save status" and use cwcheat, @@ -1029,6 +1036,17 @@ ULES00235 = true ULJM05225 = true CPCS01043 = true ULUS10062 = true +# LEGO Star Wars II: The Original Trilogy +ULES00479 = true +ULUS10155 = true +# Tony Hawk's Underground 2 Remix +ULES00033 = true +ULUS10014 = true +# Virtua Tennis: World Tour +ULES00126 = true +ULJM05079 = true +ULKS46023 = true +ULUS10037 = true [MemstickFixedFree] ULJM05571 = true @@ -1096,3 +1114,28 @@ UCJS10007 = true UCES00001 = true UCKS45008 = true NPJG00059 = true + +[MpegAvcWarmUp] +# God Eater issue #13527 ,It is custom mpeg library that required sceMpegGetAvcAu return ERROR_MPEG_NO_DATA but break FIFA 14 issue #14086 +# God Eater 1 +ULJS00237 = true +ULKS46238 = true + +# God Eater 2 +ULJS00597 = true +NPJH50832 = true +ULJS19093 = true +NPJH50832 = true + +# God Eater Burst +ULJS00351 = true +NPJH50352 = true +ULJS00350 = true +ULKS46263 = true +ULUS10563 = true +ULES01519 = true +ULJS19056 = true +NPJH50352 = true +ULUS10563FV = true +ULJS19081 = true +NPJH50352 = true diff --git a/assets/flash0/font/jpn0.pgf b/assets/flash0/font/jpn0.pgf index 05d560c691..0dcf86e909 100644 Binary files a/assets/flash0/font/jpn0.pgf and b/assets/flash0/font/jpn0.pgf differ diff --git a/assets/flash0/font/ltn0.pgf b/assets/flash0/font/ltn0.pgf index 4edba14734..a7ff949905 100644 Binary files a/assets/flash0/font/ltn0.pgf and b/assets/flash0/font/ltn0.pgf differ diff --git a/assets/flash0/font/ltn1.pgf b/assets/flash0/font/ltn1.pgf index 55da7bfe5c..80159ef0a1 100644 Binary files a/assets/flash0/font/ltn1.pgf and b/assets/flash0/font/ltn1.pgf differ diff --git a/assets/flash0/font/ltn10.pgf b/assets/flash0/font/ltn10.pgf index 8adc5a95c5..affcb5dbe1 100644 Binary files a/assets/flash0/font/ltn10.pgf and b/assets/flash0/font/ltn10.pgf differ diff --git a/assets/flash0/font/ltn11.pgf b/assets/flash0/font/ltn11.pgf index 80d674b896..7ede4b750b 100644 Binary files a/assets/flash0/font/ltn11.pgf and b/assets/flash0/font/ltn11.pgf differ diff --git a/assets/flash0/font/ltn12.pgf b/assets/flash0/font/ltn12.pgf index 770d9c591b..6f74068c29 100644 Binary files a/assets/flash0/font/ltn12.pgf and b/assets/flash0/font/ltn12.pgf differ diff --git a/assets/flash0/font/ltn13.pgf b/assets/flash0/font/ltn13.pgf index a9eb280d85..5396321a60 100644 Binary files a/assets/flash0/font/ltn13.pgf and b/assets/flash0/font/ltn13.pgf differ diff --git a/assets/flash0/font/ltn14.pgf b/assets/flash0/font/ltn14.pgf index 706dc11c5e..723feb795c 100644 Binary files a/assets/flash0/font/ltn14.pgf and b/assets/flash0/font/ltn14.pgf differ diff --git a/assets/flash0/font/ltn15.pgf b/assets/flash0/font/ltn15.pgf index 818bec4500..36ca826ab7 100644 Binary files a/assets/flash0/font/ltn15.pgf and b/assets/flash0/font/ltn15.pgf differ diff --git a/assets/flash0/font/ltn2.pgf b/assets/flash0/font/ltn2.pgf index eee46a6012..4cdbd38719 100644 Binary files a/assets/flash0/font/ltn2.pgf and b/assets/flash0/font/ltn2.pgf differ diff --git a/assets/flash0/font/ltn3.pgf b/assets/flash0/font/ltn3.pgf index 646945f84c..3be60de849 100644 Binary files a/assets/flash0/font/ltn3.pgf and b/assets/flash0/font/ltn3.pgf differ diff --git a/assets/flash0/font/ltn4.pgf b/assets/flash0/font/ltn4.pgf index 94e9e28f50..7910be8e81 100644 Binary files a/assets/flash0/font/ltn4.pgf and b/assets/flash0/font/ltn4.pgf differ diff --git a/assets/flash0/font/ltn5.pgf b/assets/flash0/font/ltn5.pgf index dff1364168..e65835455a 100644 Binary files a/assets/flash0/font/ltn5.pgf and b/assets/flash0/font/ltn5.pgf differ diff --git a/assets/flash0/font/ltn6.pgf b/assets/flash0/font/ltn6.pgf index 3782f74815..546c190371 100644 Binary files a/assets/flash0/font/ltn6.pgf and b/assets/flash0/font/ltn6.pgf differ diff --git a/assets/flash0/font/ltn7.pgf b/assets/flash0/font/ltn7.pgf index 13c86ca1ea..8a284ce907 100644 Binary files a/assets/flash0/font/ltn7.pgf and b/assets/flash0/font/ltn7.pgf differ diff --git a/assets/flash0/font/ltn8.pgf b/assets/flash0/font/ltn8.pgf index 1076299b5b..a58450c767 100644 Binary files a/assets/flash0/font/ltn8.pgf and b/assets/flash0/font/ltn8.pgf differ diff --git a/assets/flash0/font/ltn9.pgf b/assets/flash0/font/ltn9.pgf index d86906ef1c..1518008a67 100644 Binary files a/assets/flash0/font/ltn9.pgf and b/assets/flash0/font/ltn9.pgf differ diff --git a/assets/lang b/assets/lang index 17516d229e..ddb1246ec2 160000 --- a/assets/lang +++ b/assets/lang @@ -1 +1 @@ -Subproject commit 17516d229eb31e08d2029b225d4d831dc497376f +Subproject commit ddb1246ec2ad2548711f4b966a801e80b7fbe73f diff --git a/assets/shaders/defaultshaders.ini b/assets/shaders/defaultshaders.ini index fae7e560db..48cba6fc99 100644 --- a/assets/shaders/defaultshaders.ini +++ b/assets/shaders/defaultshaders.ini @@ -190,3 +190,9 @@ Type=Texture Name=4xBRZ Author=Hyllian Compute=tex_4xbrz.csh +[TexMMPX] +Type=Texture +Name=MMPX +Author=Morgan McGuire and Mara Gagiu +Compute=tex_mmpx.csh +MaxScale=2 diff --git a/assets/shaders/tex_mmpx.csh b/assets/shaders/tex_mmpx.csh new file mode 100644 index 0000000000..1ba45d1ffe --- /dev/null +++ b/assets/shaders/tex_mmpx.csh @@ -0,0 +1,117 @@ +/* MMPX.glc + Copyright 2020 Morgan McGuire & Mara Gagiu. + Provided under the Open Source MIT license https://opensource.org/licenses/MIT + + See js-demo.html for the commented source code. + This is an optimized GLSL port of that version + by Morgan McGuire and Mara Gagiu. +*/ + +#define ABGR8 uint + +ABGR8 src(int x, int y) { + return readColoru(uvec2(clamp(x, 0, params.width - 1), clamp(y, 0, params.height - 1))); +} + +uint luma(ABGR8 C) { + uint alpha = (C & 0xFF000000u) >> 24; + return (((C & 0x00FF0000u) >> 16) + ((C & 0x0000FF00u) >> 8) + (C & 0x000000FFu) + 1u) * (256u - alpha); +} + +bool all_eq2(ABGR8 B, ABGR8 A0, ABGR8 A1) { + return ((B ^ A0) | (B ^ A1)) == 0u; +} + +bool all_eq3(ABGR8 B, ABGR8 A0, ABGR8 A1, ABGR8 A2) { + return ((B ^ A0) | (B ^ A1) | (B ^ A2)) == 0u; +} + +bool all_eq4(ABGR8 B, ABGR8 A0, ABGR8 A1, ABGR8 A2, ABGR8 A3) { + return ((B ^ A0) | (B ^ A1) | (B ^ A2) | (B ^ A3)) == 0u; +} + +bool any_eq3(ABGR8 B, ABGR8 A0, ABGR8 A1, ABGR8 A2) { + return B == A0 || B == A1 || B == A2; +} + +bool none_eq2(ABGR8 B, ABGR8 A0, ABGR8 A1) { + return (B != A0) && (B != A1); +} + +bool none_eq4(ABGR8 B, ABGR8 A0, ABGR8 A1, ABGR8 A2, ABGR8 A3) { + return B != A0 && B != A1 && B != A2 && B != A3; +} + +uint applyScalingu(uvec2 origxy, uvec2 xy) { + int srcX = int(origxy.x); + int srcY = int(origxy.y); + + ABGR8 A = src(srcX - 1, srcY - 1), B = src(srcX, srcY - 1), C = src(srcX + 1, srcY - 1); + ABGR8 D = src(srcX - 1, srcY + 0), E = src(srcX, srcY + 0), F = src(srcX + 1, srcY + 0); + ABGR8 G = src(srcX - 1, srcY + 1), H = src(srcX, srcY + 1), I = src(srcX + 1, srcY + 1); + + ABGR8 J = E, K = E, L = E, M = E; + + if (((A ^ E) | (B ^ E) | (C ^ E) | (D ^ E) | (F ^ E) | (G ^ E) | (H ^ E) | (I ^ E)) != 0u) { + ABGR8 P = src(srcX, srcY - 2), S = src(srcX, srcY + 2); + ABGR8 Q = src(srcX - 2, srcY), R = src(srcX + 2, srcY); + ABGR8 Bl = luma(B), Dl = luma(D), El = luma(E), Fl = luma(F), Hl = luma(H); + + // 1:1 slope rules + if ((D == B && D != H && D != F) && (El >= Dl || E == A) && any_eq3(E, A, C, G) && ((El < Dl) || A != D || E != P || E != Q)) J = D; + if ((B == F && B != D && B != H) && (El >= Bl || E == C) && any_eq3(E, A, C, I) && ((El < Bl) || C != B || E != P || E != R)) K = B; + if ((H == D && H != F && H != B) && (El >= Hl || E == G) && any_eq3(E, A, G, I) && ((El < Hl) || G != H || E != S || E != Q)) L = H; + if ((F == H && F != B && F != D) && (El >= Fl || E == I) && any_eq3(E, C, G, I) && ((El < Fl) || I != H || E != R || E != S)) M = F; + + // Intersection rules + if ((E != F && all_eq4(E, C, I, D, Q) && all_eq2(F, B, H)) && (F != src(srcX + 3, srcY))) K = M = F; + if ((E != D && all_eq4(E, A, G, F, R) && all_eq2(D, B, H)) && (D != src(srcX - 3, srcY))) J = L = D; + if ((E != H && all_eq4(E, G, I, B, P) && all_eq2(H, D, F)) && (H != src(srcX, srcY + 3))) L = M = H; + if ((E != B && all_eq4(E, A, C, H, S) && all_eq2(B, D, F)) && (B != src(srcX, srcY - 3))) J = K = B; + if (Bl < El && all_eq4(E, G, H, I, S) && none_eq4(E, A, D, C, F)) J = K = B; + if (Hl < El && all_eq4(E, A, B, C, P) && none_eq4(E, D, G, I, F)) L = M = H; + if (Fl < El && all_eq4(E, A, D, G, Q) && none_eq4(E, B, C, I, H)) K = M = F; + if (Dl < El && all_eq4(E, C, F, I, R) && none_eq4(E, B, A, G, H)) J = L = D; + + // 2:1 slope rules + if (H != B) { + if (H != A && H != E && H != C) { + if (all_eq3(H, G, F, R) && none_eq2(H, D, src(srcX + 2, srcY - 1))) L = M; + if (all_eq3(H, I, D, Q) && none_eq2(H, F, src(srcX - 2, srcY - 1))) M = L; + } + + if (B != I && B != G && B != E) { + if (all_eq3(B, A, F, R) && none_eq2(B, D, src(srcX + 2, srcY + 1))) J = K; + if (all_eq3(B, C, D, Q) && none_eq2(B, F, src(srcX - 2, srcY + 1))) K = J; + } + } // H !== B + + if (F != D) { + if (D != I && D != E && D != C) { + if (all_eq3(D, A, H, S) && none_eq2(D, B, src(srcX + 1, srcY + 2))) J = L; + if (all_eq3(D, G, B, P) && none_eq2(D, H, src(srcX + 1, srcY - 2))) L = J; + } + + if (F != E && F != A && F != G) { + if (all_eq3(F, C, H, S) && none_eq2(F, B, src(srcX - 1, srcY + 2))) K = M; + if (all_eq3(F, I, B, P) && none_eq2(F, H, src(srcX - 1, srcY - 2))) M = K; + } + } // F !== D + } // not constant + + // TODO: Write four pixels at once. For now, 1/4x speed. + if ((xy.y & 1u) == 0u) { + if ((xy.x & 1u) == 0u) { + return J; + } + return K; + } + if ((xy.x & 1u) == 0u) { + return L; + } + return M; +} + +vec4 applyScalingf(uvec2 origxy, uvec2 xy) { + return unpackUnorm4x8(applyScalingu(origxy, xy)); +} diff --git a/b.sh b/b.sh index 55ab6e642d..83aad6ba02 100755 --- a/b.sh +++ b/b.sh @@ -61,7 +61,7 @@ do export CXX=/usr/bin/clang++ ;; --sanitize) echo "Enabling address-sanitizer if available" - CMAKE_ARGS="-DUSE_ADDRESS_SANITIZER=ON ${CMAKE_ARGS}" + CMAKE_ARGS="-DUSE_ASAN=ON ${CMAKE_ARGS}" ;; *) MAKE_OPT="$1 ${MAKE_OPT}" ;; diff --git a/build.gradle b/build.gradle index 11479e334c..c8274d0b58 100644 --- a/build.gradle +++ b/build.gradle @@ -5,7 +5,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:4.0.2' + classpath 'com.android.tools.build:gradle:4.1.2' } } diff --git a/cmake/Modules/FindFFmpeg.cmake b/cmake/Modules/FindFFmpeg.cmake index a8c482dc21..648c0bacf3 100644 --- a/cmake/Modules/FindFFmpeg.cmake +++ b/cmake/Modules/FindFFmpeg.cmake @@ -46,6 +46,15 @@ set(_FFmpeg_DEPS_postproc avutil) set(_FFmpeg_DEPS_swresample avutil) set(_FFmpeg_DEPS_swscale avutil) +set(_FFmpeg_HEADER_avcodec avcodec) +set(_FFmpeg_HEADER_avdevice avdevice) +set(_FFmpeg_HEADER_avfilter avfilter) +set(_FFmpeg_HEADER_avformat avformat) +set(_FFmpeg_HEADER_avutil avutil) +set(_FFmpeg_HEADER_postproc postprocess) +set(_FFmpeg_HEADER_swresample swresample) +set(_FFmpeg_HEADER_swscale swscale) + function(find_ffmpeg LIBNAME) if(DEFINED ENV{FFMPEG_DIR}) set(FFMPEG_DIR $ENV{FFMPEG_DIR}) @@ -84,7 +93,7 @@ function(find_ffmpeg LIBNAME) ) endif() - find_path(FFmpeg_INCLUDE_${LIBNAME} lib${LIBNAME}/${LIBNAME}.h + find_path(FFmpeg_INCLUDE_${LIBNAME} lib${LIBNAME}/${_FFmpeg_HEADER_${LIBNAME}}.h HINTS ${INCLUDE_PATHS} ) @@ -92,12 +101,14 @@ function(find_ffmpeg LIBNAME) HINTS ${LIB_PATHS} ) - if(NOT FFMPEG_DIR AND (NOT FFmpeg_LIBRARY_${LIBNAME} OR NOT FFmpeg_INCLUDE_${LIBNAME})) + if(NOT FFMPEG_DIR AND ( + FFmpeg_LIBRARY_${LIBNAME} STREQUAL FFmpeg_LIBRARY_${LIBNAME}-NOTFOUND + OR FFmpeg_INCLUDE_${LIBNAME} STREQUAL FFmpeg_INCLUDE_${LIBNAME}-NOTFOUND)) # Didn't find it in the usual paths, try pkg-config find_package(PkgConfig QUIET) pkg_check_modules(FFmpeg_PKGCONFIG_${LIBNAME} REQUIRED QUIET lib${LIBNAME}) - find_path(FFmpeg_INCLUDE_${LIBNAME} lib${LIBNAME}/${LIBNAME}.h + find_path(FFmpeg_INCLUDE_${LIBNAME} lib${LIBNAME}/${_FFmpeg_HEADER_${LIBNAME}}.h ${FFmpeg_PKGCONFIG_${LIBNAME}_INCLUDE_DIRS} ) diff --git a/ext/SPIRV-Cross b/ext/SPIRV-Cross index a1f7c8dc8e..9acb9ec31f 160000 --- a/ext/SPIRV-Cross +++ b/ext/SPIRV-Cross @@ -1 +1 @@ -Subproject commit a1f7c8dc8ea2f94443951ee27003bffa562c1f13 +Subproject commit 9acb9ec31f5a8ef80ea6b994bb77be787b08d3d1 diff --git a/ext/disarm.cpp b/ext/disarm.cpp index 57b34b0b53..8f665d0f9a 100644 --- a/ext/disarm.cpp +++ b/ext/disarm.cpp @@ -66,6 +66,7 @@ #pragma GCC diagnostic ignored "-Wstring-plus-int" #endif +#include "ppsspp_config.h" #include #include #include @@ -132,7 +133,7 @@ int GetVm(uint32_t op, bool quad = false, bool dbl = false) { // Horrible array of hacks but hey. Can be cleaned up later. bool DisasmVFP(uint32_t op, char *text) { -#if defined(__ANDROID__) && defined(_M_IX86) +#if defined(__ANDROID__) && PPSSPP_ARCH(X86) // Prevent linking errors with ArmEmitter which I've excluded on x86 android. strcpy(text, "ARM disasm not available"); #else diff --git a/ext/discord-rpc b/ext/discord-rpc index 3d3ae7129d..963aa9f3e5 160000 --- a/ext/discord-rpc +++ b/ext/discord-rpc @@ -1 +1 @@ -Subproject commit 3d3ae7129d17643bc706da0a2eea85aafd10ab3a +Subproject commit 963aa9f3e5ce81a4682c6ca3d136cddda614db33 diff --git a/ext/glslang b/ext/glslang index d0850f875e..dc11adde23 160000 --- a/ext/glslang +++ b/ext/glslang @@ -1 +1 @@ -Subproject commit d0850f875ec392a130ccf00018dab458b546f27c +Subproject commit dc11adde23c455a24e13dd54de9b4ede8bdd7db8 diff --git a/ext/glslang-build/Android.mk b/ext/glslang-build/Android.mk index 8125efa02b..f7d27de667 100644 --- a/ext/glslang-build/Android.mk +++ b/ext/glslang-build/Android.mk @@ -37,13 +37,13 @@ LOCAL_SRC_FILES := \ ../glslang/glslang/MachineIndependent/preprocessor/PpScanner.cpp \ ../glslang/glslang/MachineIndependent/preprocessor/PpTokens.cpp \ ../glslang/glslang/OSDependent/Unix/ossource.cpp \ - ../glslang/hlsl/hlslAttributes.cpp \ - ../glslang/hlsl/hlslGrammar.cpp \ - ../glslang/hlsl/hlslOpMap.cpp \ - ../glslang/hlsl/hlslParseables.cpp \ - ../glslang/hlsl/hlslParseHelper.cpp \ - ../glslang/hlsl/hlslScanContext.cpp \ - ../glslang/hlsl/hlslTokenStream.cpp \ + ../glslang/glslang/HLSL/hlslAttributes.cpp \ + ../glslang/glslang/HLSL/hlslGrammar.cpp \ + ../glslang/glslang/HLSL/hlslOpMap.cpp \ + ../glslang/glslang/HLSL/hlslParseables.cpp \ + ../glslang/glslang/HLSL/hlslParseHelper.cpp \ + ../glslang/glslang/HLSL/hlslScanContext.cpp \ + ../glslang/glslang/HLSL/hlslTokenStream.cpp \ ../glslang/SPIRV/disassemble.cpp \ ../glslang/SPIRV/doc.cpp \ ../glslang/SPIRV/GlslangToSpv.cpp \ @@ -52,6 +52,7 @@ LOCAL_SRC_FILES := \ ../glslang/SPIRV/SpvBuilder.cpp \ ../glslang/SPIRV/SPVRemapper.cpp \ ../glslang/SPIRV/SpvPostProcess.cpp \ + ../glslang/SPIRV/SpvTools.cpp \ ../glslang/OGLCompilersDLL/InitializeDll.cpp @@ -61,9 +62,9 @@ LOCAL_CPPFLAGS := -fno-exceptions -std=gnu++11 -fno-rtti -Wno-reorder LOCAL_C_INCLUDES := $(LOCAL_PATH)/.. $(LOCAL_PATH)/../libzip $(LOCAL_PATH)/../glslang .. ifeq ($(findstring armeabi-v7a,$(TARGET_ARCH_ABI)),armeabi-v7a) -LOCAL_CFLAGS := $(LOCAL_CFLAGS) -DARM -DARMEABI_V7A +LOCAL_CFLAGS := $(LOCAL_CFLAGS) else ifeq ($(TARGET_ARCH_ABI),armeabi) -LOCAL_CFLAGS := $(LOCAL_CFLAGS) -DARM -DARMEABI -march=armv6 +LOCAL_CFLAGS := $(LOCAL_CFLAGS) -march=armv6 else ifeq ($(TARGET_ARCH_ABI),x86) LOCAL_CFLAGS := $(LOCAL_CFLAGS) -D_M_IX86 else ifeq ($(TARGET_ARCH_ABI),x86_64) diff --git a/ext/glslang.vcxproj b/ext/glslang.vcxproj index 948d6daaff..5835c251ba 100644 --- a/ext/glslang.vcxproj +++ b/ext/glslang.vcxproj @@ -300,6 +300,7 @@ + @@ -330,13 +331,13 @@ - - - - - - - + + + + + + + @@ -348,6 +349,13 @@ + + + + + + + @@ -379,14 +387,14 @@ - - - - - - - - + + + + + + + + diff --git a/ext/glslang.vcxproj.filters b/ext/glslang.vcxproj.filters index b8a78092c3..479bd35e78 100644 --- a/ext/glslang.vcxproj.filters +++ b/ext/glslang.vcxproj.filters @@ -119,33 +119,36 @@ SPIRV - - HLSL - - - HLSL - - - HLSL - - - HLSL - - - HLSL - - - HLSL - glslang SPIRV - + HLSL + + HLSL + + + HLSL + + + HLSL + + + HLSL + + + HLSL + + + HLSL + + + SPIRV + @@ -278,33 +281,54 @@ SPIRV - - HLSL - - - HLSL - - - HLSL - - - HLSL - - - HLSL - - - HLSL - - - HLSL - glslang - + HLSL + + HLSL + + + HLSL + + + HLSL + + + HLSL + + + HLSL + + + HLSL + + + HLSL + + + SPIRV + + + SPIRV + + + SPIRV + + + SPIRV + + + HLSL + + + glslang + + + glslang + diff --git a/ext/jpge/README.md b/ext/jpge/README.md new file mode 100644 index 0000000000..98786710a4 --- /dev/null +++ b/ext/jpge/README.md @@ -0,0 +1,25 @@ +# jpeg-compressor + +This directory contains code from +https://github.com/richgel999/jpeg-compressor/tree/aeb7d3b463aa8228b87a28013c15ee50a7e6fcf3 +with the following patches: + +* https://github.com/richgel999/jpeg-compressor/pull/18 + +## License + +The license of jpgd.cpp/.h and jpge.cpp/.h is either Public Domain or Apache 2.0. Choose whatever you want. + +The license for the optional file jpgd_idct.h (and ONLY this file) is Copyright 2009 Intel Corporation: + +Permission is granted to use, copy, distribute and prepare derivative works of +this software for any purpose and without fee, provided, that the above +copyright notice and this statement appear in all copies. Intel makes no +representations about the suitability of this software for any purpose. THIS +SOFTWARE IS PROVIDED "AS IS." INTEL SPECIFICALLY DISCLAIMS ALL WARRANTIES, +EXPRESS OR IMPLIED, AND ALL LIABILITY, INCLUDING CONSEQUENTIAL AND OTHER +INDIRECT DAMAGES, FOR THE USE OF THIS SOFTWARE, INCLUDING LIABILITY FOR +INFRINGEMENT OF ANY PROPRIETARY RIGHTS, AND INCLUDING THE WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. Intel does not assume any +responsibility for any errors which may appear in this software nor any +responsibility to update it. diff --git a/ext/jpge/jpgd.cpp b/ext/jpge/jpgd.cpp index 4f1d57096d..baf6ea0484 100644 --- a/ext/jpge/jpgd.cpp +++ b/ext/jpge/jpgd.cpp @@ -1,27 +1,53 @@ -// jpgd.cpp - C++ class for JPEG decompression. -// Public domain, Rich Geldreich -// Alex Evans: Linear memory allocator (taken from jpge.h). -// v1.04, May. 19, 2012: Code tweaks to fix VS2008 static code analysis warnings (all looked harmless) -// +// jpgd.cpp - C++ class for JPEG decompression. Written by Richard Geldreich between 1994-2020. // Supports progressive and baseline sequential JPEG image files, and the most common chroma subsampling factors: Y, H1V1, H2V1, H1V2, and H2V2. +// Supports box and linear chroma upsampling. +// +// Released under two licenses. You are free to choose which license you want: +// License 1: +// Public Domain +// +// License 2: +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Alex Evans: Linear memory allocator (taken from jpge.h). +// v1.04, May. 19, 2012: Code tweaks to fix VS2008 static code analysis warnings +// v2.00, March 20, 2020: Fuzzed with zzuf and afl. Fixed several issues, converted most assert()'s to run-time checks. Added chroma upsampling. Removed freq. domain upsampling. gcc/clang warnings. +// +// Important: +// #define JPGD_USE_SSE2 to 0 to completely disable SSE2 usage. // -// Chroma upsampling quality: H2V2 is upsampled in the frequency domain, H2V1 and H1V2 are upsampled using point sampling. -// Chroma upsampling reference: "Fast Scheme for Image Size Change in the Compressed Domain" -// http://vision.ai.uiuc.edu/~dugad/research/dct/index.html - #include "jpgd.h" #include - +#include #include -#define JPGD_ASSERT(x) assert(x) #ifdef _MSC_VER #pragma warning (disable : 4611) // warning C4611: interaction between '_setjmp' and C++ object destruction is non-portable #endif -// Set to 1 to enable freq. domain chroma upsampling on images using H2V2 subsampling (0=faster nearest neighbor sampling). -// This is slower, but results in higher quality on images with highly saturated colors. -#define JPGD_SUPPORT_FREQ_DOMAIN_UPSAMPLING 1 +#ifndef JPGD_USE_SSE2 + + #if defined(__GNUC__) + #if defined(__SSE2__) + #define JPGD_USE_SSE2 (1) + #endif + #elif defined(_MSC_VER) + #if defined(_M_X64) + #define JPGD_USE_SSE2 (1) + #endif + #endif + +#endif #define JPGD_TRUE (1) #define JPGD_FALSE (0) @@ -31,22 +57,26 @@ namespace jpgd { -static inline void *jpgd_malloc(size_t nSize) { return malloc(nSize); } -static inline void jpgd_free(void *p) { free(p); } + static inline void* jpgd_malloc(size_t nSize) { return malloc(nSize); } + static inline void jpgd_free(void* p) { free(p); } -// DCT coefficients are stored in this sequence. -static int g_ZAG[64] = { 0,1,8,16,9,2,3,10,17,24,32,25,18,11,4,5,12,19,26,33,40,48,41,34,27,20,13,6,7,14,21,28,35,42,49,56,57,50,43,36,29,22,15,23,30,37,44,51,58,59,52,45,38,31,39,46,53,60,61,54,47,55,62,63 }; + // DCT coefficients are stored in this sequence. + static int g_ZAG[64] = { 0,1,8,16,9,2,3,10,17,24,32,25,18,11,4,5,12,19,26,33,40,48,41,34,27,20,13,6,7,14,21,28,35,42,49,56,57,50,43,36,29,22,15,23,30,37,44,51,58,59,52,45,38,31,39,46,53,60,61,54,47,55,62,63 }; -enum JPEG_MARKER -{ - M_SOF0 = 0xC0, M_SOF1 = 0xC1, M_SOF2 = 0xC2, M_SOF3 = 0xC3, M_SOF5 = 0xC5, M_SOF6 = 0xC6, M_SOF7 = 0xC7, M_JPG = 0xC8, - M_SOF9 = 0xC9, M_SOF10 = 0xCA, M_SOF11 = 0xCB, M_SOF13 = 0xCD, M_SOF14 = 0xCE, M_SOF15 = 0xCF, M_DHT = 0xC4, M_DAC = 0xCC, - M_RST0 = 0xD0, M_RST1 = 0xD1, M_RST2 = 0xD2, M_RST3 = 0xD3, M_RST4 = 0xD4, M_RST5 = 0xD5, M_RST6 = 0xD6, M_RST7 = 0xD7, - M_SOI = 0xD8, M_EOI = 0xD9, M_SOS = 0xDA, M_DQT = 0xDB, M_DNL = 0xDC, M_DRI = 0xDD, M_DHP = 0xDE, M_EXP = 0xDF, - M_APP0 = 0xE0, M_APP15 = 0xEF, M_JPG0 = 0xF0, M_JPG13 = 0xFD, M_COM = 0xFE, M_TEM = 0x01, M_ERROR = 0x100, RST0 = 0xD0 -}; + enum JPEG_MARKER + { + M_SOF0 = 0xC0, M_SOF1 = 0xC1, M_SOF2 = 0xC2, M_SOF3 = 0xC3, M_SOF5 = 0xC5, M_SOF6 = 0xC6, M_SOF7 = 0xC7, M_JPG = 0xC8, + M_SOF9 = 0xC9, M_SOF10 = 0xCA, M_SOF11 = 0xCB, M_SOF13 = 0xCD, M_SOF14 = 0xCE, M_SOF15 = 0xCF, M_DHT = 0xC4, M_DAC = 0xCC, + M_RST0 = 0xD0, M_RST1 = 0xD1, M_RST2 = 0xD2, M_RST3 = 0xD3, M_RST4 = 0xD4, M_RST5 = 0xD5, M_RST6 = 0xD6, M_RST7 = 0xD7, + M_SOI = 0xD8, M_EOI = 0xD9, M_SOS = 0xDA, M_DQT = 0xDB, M_DNL = 0xDC, M_DRI = 0xDD, M_DHP = 0xDE, M_EXP = 0xDF, + M_APP0 = 0xE0, M_APP15 = 0xEF, M_JPG0 = 0xF0, M_JPG13 = 0xFD, M_COM = 0xFE, M_TEM = 0x01, M_ERROR = 0x100, RST0 = 0xD0 + }; -enum JPEG_SUBSAMPLING { JPGD_GRAYSCALE = 0, JPGD_YH1V1, JPGD_YH2V1, JPGD_YH1V2, JPGD_YH2V2 }; + enum JPEG_SUBSAMPLING { JPGD_GRAYSCALE = 0, JPGD_YH1V1, JPGD_YH2V1, JPGD_YH1V2, JPGD_YH2V2 }; + +#if JPGD_USE_SSE2 +#include "jpgd_idct.h" +#endif #define CONST_BITS 13 #define PASS1_BITS 2 @@ -72,3101 +102,3182 @@ enum JPEG_SUBSAMPLING { JPGD_GRAYSCALE = 0, JPGD_YH1V1, JPGD_YH2V1, JPGD_YH1V2, #define CLAMP(i) ((static_cast(i) > 255) ? (((~i) >> 31) & 0xFF) : (i)) -// Compiler creates a fast path 1D IDCT for X non-zero columns -template -struct Row -{ - static void idct(int* pTemp, const jpgd_block_t* pSrc) - { - // ACCESS_COL() will be optimized at compile time to either an array access, or 0. - #define ACCESS_COL(x) (((x) < NONZERO_COLS) ? (int)pSrc[x] : 0) + static inline int left_shifti(int val, uint32_t bits) + { + return static_cast(static_cast(val) << bits); + } - const int z2 = ACCESS_COL(2), z3 = ACCESS_COL(6); + // Compiler creates a fast path 1D IDCT for X non-zero columns + template + struct Row + { + static void idct(int* pTemp, const jpgd_block_coeff_t* pSrc) + { + // ACCESS_COL() will be optimized at compile time to either an array access, or 0. Good compilers will then optimize out muls against 0. +#define ACCESS_COL(x) (((x) < NONZERO_COLS) ? (int)pSrc[x] : 0) - const int z1 = MULTIPLY(z2 + z3, FIX_0_541196100); - const int tmp2 = z1 + MULTIPLY(z3, - FIX_1_847759065); - const int tmp3 = z1 + MULTIPLY(z2, FIX_0_765366865); + const int z2 = ACCESS_COL(2), z3 = ACCESS_COL(6); - const int tmp0 = (ACCESS_COL(0) + ACCESS_COL(4)) << CONST_BITS; - const int tmp1 = (ACCESS_COL(0) - ACCESS_COL(4)) << CONST_BITS; + const int z1 = MULTIPLY(z2 + z3, FIX_0_541196100); + const int tmp2 = z1 + MULTIPLY(z3, -FIX_1_847759065); + const int tmp3 = z1 + MULTIPLY(z2, FIX_0_765366865); - const int tmp10 = tmp0 + tmp3, tmp13 = tmp0 - tmp3, tmp11 = tmp1 + tmp2, tmp12 = tmp1 - tmp2; + const int tmp0 = left_shifti(ACCESS_COL(0) + ACCESS_COL(4), CONST_BITS); + const int tmp1 = left_shifti(ACCESS_COL(0) - ACCESS_COL(4), CONST_BITS); - const int atmp0 = ACCESS_COL(7), atmp1 = ACCESS_COL(5), atmp2 = ACCESS_COL(3), atmp3 = ACCESS_COL(1); + const int tmp10 = tmp0 + tmp3, tmp13 = tmp0 - tmp3, tmp11 = tmp1 + tmp2, tmp12 = tmp1 - tmp2; - const int bz1 = atmp0 + atmp3, bz2 = atmp1 + atmp2, bz3 = atmp0 + atmp2, bz4 = atmp1 + atmp3; - const int bz5 = MULTIPLY(bz3 + bz4, FIX_1_175875602); + const int atmp0 = ACCESS_COL(7), atmp1 = ACCESS_COL(5), atmp2 = ACCESS_COL(3), atmp3 = ACCESS_COL(1); - const int az1 = MULTIPLY(bz1, - FIX_0_899976223); - const int az2 = MULTIPLY(bz2, - FIX_2_562915447); - const int az3 = MULTIPLY(bz3, - FIX_1_961570560) + bz5; - const int az4 = MULTIPLY(bz4, - FIX_0_390180644) + bz5; + const int bz1 = atmp0 + atmp3, bz2 = atmp1 + atmp2, bz3 = atmp0 + atmp2, bz4 = atmp1 + atmp3; + const int bz5 = MULTIPLY(bz3 + bz4, FIX_1_175875602); - const int btmp0 = MULTIPLY(atmp0, FIX_0_298631336) + az1 + az3; - const int btmp1 = MULTIPLY(atmp1, FIX_2_053119869) + az2 + az4; - const int btmp2 = MULTIPLY(atmp2, FIX_3_072711026) + az2 + az3; - const int btmp3 = MULTIPLY(atmp3, FIX_1_501321110) + az1 + az4; + const int az1 = MULTIPLY(bz1, -FIX_0_899976223); + const int az2 = MULTIPLY(bz2, -FIX_2_562915447); + const int az3 = MULTIPLY(bz3, -FIX_1_961570560) + bz5; + const int az4 = MULTIPLY(bz4, -FIX_0_390180644) + bz5; - pTemp[0] = DESCALE(tmp10 + btmp3, CONST_BITS-PASS1_BITS); - pTemp[7] = DESCALE(tmp10 - btmp3, CONST_BITS-PASS1_BITS); - pTemp[1] = DESCALE(tmp11 + btmp2, CONST_BITS-PASS1_BITS); - pTemp[6] = DESCALE(tmp11 - btmp2, CONST_BITS-PASS1_BITS); - pTemp[2] = DESCALE(tmp12 + btmp1, CONST_BITS-PASS1_BITS); - pTemp[5] = DESCALE(tmp12 - btmp1, CONST_BITS-PASS1_BITS); - pTemp[3] = DESCALE(tmp13 + btmp0, CONST_BITS-PASS1_BITS); - pTemp[4] = DESCALE(tmp13 - btmp0, CONST_BITS-PASS1_BITS); - } -}; + const int btmp0 = MULTIPLY(atmp0, FIX_0_298631336) + az1 + az3; + const int btmp1 = MULTIPLY(atmp1, FIX_2_053119869) + az2 + az4; + const int btmp2 = MULTIPLY(atmp2, FIX_3_072711026) + az2 + az3; + const int btmp3 = MULTIPLY(atmp3, FIX_1_501321110) + az1 + az4; -template <> -struct Row<0> -{ - static void idct(int* pTemp, const jpgd_block_t* pSrc) - { -#ifdef _MSC_VER - pTemp; pSrc; + pTemp[0] = DESCALE(tmp10 + btmp3, CONST_BITS - PASS1_BITS); + pTemp[7] = DESCALE(tmp10 - btmp3, CONST_BITS - PASS1_BITS); + pTemp[1] = DESCALE(tmp11 + btmp2, CONST_BITS - PASS1_BITS); + pTemp[6] = DESCALE(tmp11 - btmp2, CONST_BITS - PASS1_BITS); + pTemp[2] = DESCALE(tmp12 + btmp1, CONST_BITS - PASS1_BITS); + pTemp[5] = DESCALE(tmp12 - btmp1, CONST_BITS - PASS1_BITS); + pTemp[3] = DESCALE(tmp13 + btmp0, CONST_BITS - PASS1_BITS); + pTemp[4] = DESCALE(tmp13 - btmp0, CONST_BITS - PASS1_BITS); + } + }; + + template <> + struct Row<0> + { + static void idct(int* pTemp, const jpgd_block_coeff_t* pSrc) + { + (void)pTemp; + (void)pSrc; + } + }; + + template <> + struct Row<1> + { + static void idct(int* pTemp, const jpgd_block_coeff_t* pSrc) + { + const int dcval = left_shifti(pSrc[0], PASS1_BITS); + + pTemp[0] = dcval; + pTemp[1] = dcval; + pTemp[2] = dcval; + pTemp[3] = dcval; + pTemp[4] = dcval; + pTemp[5] = dcval; + pTemp[6] = dcval; + pTemp[7] = dcval; + } + }; + + // Compiler creates a fast path 1D IDCT for X non-zero rows + template + struct Col + { + static void idct(uint8* pDst_ptr, const int* pTemp) + { + // ACCESS_ROW() will be optimized at compile time to either an array access, or 0. +#define ACCESS_ROW(x) (((x) < NONZERO_ROWS) ? pTemp[x * 8] : 0) + + const int z2 = ACCESS_ROW(2); + const int z3 = ACCESS_ROW(6); + + const int z1 = MULTIPLY(z2 + z3, FIX_0_541196100); + const int tmp2 = z1 + MULTIPLY(z3, -FIX_1_847759065); + const int tmp3 = z1 + MULTIPLY(z2, FIX_0_765366865); + + const int tmp0 = left_shifti(ACCESS_ROW(0) + ACCESS_ROW(4), CONST_BITS); + const int tmp1 = left_shifti(ACCESS_ROW(0) - ACCESS_ROW(4), CONST_BITS); + + const int tmp10 = tmp0 + tmp3, tmp13 = tmp0 - tmp3, tmp11 = tmp1 + tmp2, tmp12 = tmp1 - tmp2; + + const int atmp0 = ACCESS_ROW(7), atmp1 = ACCESS_ROW(5), atmp2 = ACCESS_ROW(3), atmp3 = ACCESS_ROW(1); + + const int bz1 = atmp0 + atmp3, bz2 = atmp1 + atmp2, bz3 = atmp0 + atmp2, bz4 = atmp1 + atmp3; + const int bz5 = MULTIPLY(bz3 + bz4, FIX_1_175875602); + + const int az1 = MULTIPLY(bz1, -FIX_0_899976223); + const int az2 = MULTIPLY(bz2, -FIX_2_562915447); + const int az3 = MULTIPLY(bz3, -FIX_1_961570560) + bz5; + const int az4 = MULTIPLY(bz4, -FIX_0_390180644) + bz5; + + const int btmp0 = MULTIPLY(atmp0, FIX_0_298631336) + az1 + az3; + const int btmp1 = MULTIPLY(atmp1, FIX_2_053119869) + az2 + az4; + const int btmp2 = MULTIPLY(atmp2, FIX_3_072711026) + az2 + az3; + const int btmp3 = MULTIPLY(atmp3, FIX_1_501321110) + az1 + az4; + + int i = DESCALE_ZEROSHIFT(tmp10 + btmp3, CONST_BITS + PASS1_BITS + 3); + pDst_ptr[8 * 0] = (uint8)CLAMP(i); + + i = DESCALE_ZEROSHIFT(tmp10 - btmp3, CONST_BITS + PASS1_BITS + 3); + pDst_ptr[8 * 7] = (uint8)CLAMP(i); + + i = DESCALE_ZEROSHIFT(tmp11 + btmp2, CONST_BITS + PASS1_BITS + 3); + pDst_ptr[8 * 1] = (uint8)CLAMP(i); + + i = DESCALE_ZEROSHIFT(tmp11 - btmp2, CONST_BITS + PASS1_BITS + 3); + pDst_ptr[8 * 6] = (uint8)CLAMP(i); + + i = DESCALE_ZEROSHIFT(tmp12 + btmp1, CONST_BITS + PASS1_BITS + 3); + pDst_ptr[8 * 2] = (uint8)CLAMP(i); + + i = DESCALE_ZEROSHIFT(tmp12 - btmp1, CONST_BITS + PASS1_BITS + 3); + pDst_ptr[8 * 5] = (uint8)CLAMP(i); + + i = DESCALE_ZEROSHIFT(tmp13 + btmp0, CONST_BITS + PASS1_BITS + 3); + pDst_ptr[8 * 3] = (uint8)CLAMP(i); + + i = DESCALE_ZEROSHIFT(tmp13 - btmp0, CONST_BITS + PASS1_BITS + 3); + pDst_ptr[8 * 4] = (uint8)CLAMP(i); + } + }; + + template <> + struct Col<1> + { + static void idct(uint8* pDst_ptr, const int* pTemp) + { + int dcval = DESCALE_ZEROSHIFT(pTemp[0], PASS1_BITS + 3); + const uint8 dcval_clamped = (uint8)CLAMP(dcval); + pDst_ptr[0 * 8] = dcval_clamped; + pDst_ptr[1 * 8] = dcval_clamped; + pDst_ptr[2 * 8] = dcval_clamped; + pDst_ptr[3 * 8] = dcval_clamped; + pDst_ptr[4 * 8] = dcval_clamped; + pDst_ptr[5 * 8] = dcval_clamped; + pDst_ptr[6 * 8] = dcval_clamped; + pDst_ptr[7 * 8] = dcval_clamped; + } + }; + + static const uint8 s_idct_row_table[] = + { + 1,0,0,0,0,0,0,0, 2,0,0,0,0,0,0,0, 2,1,0,0,0,0,0,0, 2,1,1,0,0,0,0,0, 2,2,1,0,0,0,0,0, 3,2,1,0,0,0,0,0, 4,2,1,0,0,0,0,0, 4,3,1,0,0,0,0,0, + 4,3,2,0,0,0,0,0, 4,3,2,1,0,0,0,0, 4,3,2,1,1,0,0,0, 4,3,2,2,1,0,0,0, 4,3,3,2,1,0,0,0, 4,4,3,2,1,0,0,0, 5,4,3,2,1,0,0,0, 6,4,3,2,1,0,0,0, + 6,5,3,2,1,0,0,0, 6,5,4,2,1,0,0,0, 6,5,4,3,1,0,0,0, 6,5,4,3,2,0,0,0, 6,5,4,3,2,1,0,0, 6,5,4,3,2,1,1,0, 6,5,4,3,2,2,1,0, 6,5,4,3,3,2,1,0, + 6,5,4,4,3,2,1,0, 6,5,5,4,3,2,1,0, 6,6,5,4,3,2,1,0, 7,6,5,4,3,2,1,0, 8,6,5,4,3,2,1,0, 8,7,5,4,3,2,1,0, 8,7,6,4,3,2,1,0, 8,7,6,5,3,2,1,0, + 8,7,6,5,4,2,1,0, 8,7,6,5,4,3,1,0, 8,7,6,5,4,3,2,0, 8,7,6,5,4,3,2,1, 8,7,6,5,4,3,2,2, 8,7,6,5,4,3,3,2, 8,7,6,5,4,4,3,2, 8,7,6,5,5,4,3,2, + 8,7,6,6,5,4,3,2, 8,7,7,6,5,4,3,2, 8,8,7,6,5,4,3,2, 8,8,8,6,5,4,3,2, 8,8,8,7,5,4,3,2, 8,8,8,7,6,4,3,2, 8,8,8,7,6,5,3,2, 8,8,8,7,6,5,4,2, + 8,8,8,7,6,5,4,3, 8,8,8,7,6,5,4,4, 8,8,8,7,6,5,5,4, 8,8,8,7,6,6,5,4, 8,8,8,7,7,6,5,4, 8,8,8,8,7,6,5,4, 8,8,8,8,8,6,5,4, 8,8,8,8,8,7,5,4, + 8,8,8,8,8,7,6,4, 8,8,8,8,8,7,6,5, 8,8,8,8,8,7,6,6, 8,8,8,8,8,7,7,6, 8,8,8,8,8,8,7,6, 8,8,8,8,8,8,8,6, 8,8,8,8,8,8,8,7, 8,8,8,8,8,8,8,8, + }; + + static const uint8 s_idct_col_table[] = + { + 1, 1, 2, 3, 3, 3, 3, 3, 3, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8 + }; + + // Scalar "fast pathing" IDCT. + static void idct(const jpgd_block_coeff_t* pSrc_ptr, uint8* pDst_ptr, int block_max_zag, bool use_simd) + { + (void)use_simd; + + assert(block_max_zag >= 1); + assert(block_max_zag <= 64); + + if (block_max_zag <= 1) + { + int k = ((pSrc_ptr[0] + 4) >> 3) + 128; + k = CLAMP(k); + k = k | (k << 8); + k = k | (k << 16); + + for (int i = 8; i > 0; i--) + { + *(int*)&pDst_ptr[0] = k; + *(int*)&pDst_ptr[4] = k; + pDst_ptr += 8; + } + return; + } + +#if JPGD_USE_SSE2 + if (use_simd) + { + assert((((uintptr_t)pSrc_ptr) & 15) == 0); + assert((((uintptr_t)pDst_ptr) & 15) == 0); + idctSSEShortU8(pSrc_ptr, pDst_ptr); + return; + } #endif - } -}; - -template <> -struct Row<1> -{ - static void idct(int* pTemp, const jpgd_block_t* pSrc) - { - const int dcval = (pSrc[0] << PASS1_BITS); - - pTemp[0] = dcval; - pTemp[1] = dcval; - pTemp[2] = dcval; - pTemp[3] = dcval; - pTemp[4] = dcval; - pTemp[5] = dcval; - pTemp[6] = dcval; - pTemp[7] = dcval; - } -}; - -// Compiler creates a fast path 1D IDCT for X non-zero rows -template -struct Col -{ - static void idct(uint8* pDst_ptr, const int* pTemp) - { - // ACCESS_ROW() will be optimized at compile time to either an array access, or 0. - #define ACCESS_ROW(x) (((x) < NONZERO_ROWS) ? pTemp[x * 8] : 0) - - const int z2 = ACCESS_ROW(2); - const int z3 = ACCESS_ROW(6); - - const int z1 = MULTIPLY(z2 + z3, FIX_0_541196100); - const int tmp2 = z1 + MULTIPLY(z3, - FIX_1_847759065); - const int tmp3 = z1 + MULTIPLY(z2, FIX_0_765366865); - - const int tmp0 = (ACCESS_ROW(0) + ACCESS_ROW(4)) << CONST_BITS; - const int tmp1 = (ACCESS_ROW(0) - ACCESS_ROW(4)) << CONST_BITS; - - const int tmp10 = tmp0 + tmp3, tmp13 = tmp0 - tmp3, tmp11 = tmp1 + tmp2, tmp12 = tmp1 - tmp2; - - const int atmp0 = ACCESS_ROW(7), atmp1 = ACCESS_ROW(5), atmp2 = ACCESS_ROW(3), atmp3 = ACCESS_ROW(1); - - const int bz1 = atmp0 + atmp3, bz2 = atmp1 + atmp2, bz3 = atmp0 + atmp2, bz4 = atmp1 + atmp3; - const int bz5 = MULTIPLY(bz3 + bz4, FIX_1_175875602); - - const int az1 = MULTIPLY(bz1, - FIX_0_899976223); - const int az2 = MULTIPLY(bz2, - FIX_2_562915447); - const int az3 = MULTIPLY(bz3, - FIX_1_961570560) + bz5; - const int az4 = MULTIPLY(bz4, - FIX_0_390180644) + bz5; - - const int btmp0 = MULTIPLY(atmp0, FIX_0_298631336) + az1 + az3; - const int btmp1 = MULTIPLY(atmp1, FIX_2_053119869) + az2 + az4; - const int btmp2 = MULTIPLY(atmp2, FIX_3_072711026) + az2 + az3; - const int btmp3 = MULTIPLY(atmp3, FIX_1_501321110) + az1 + az4; - - int i = DESCALE_ZEROSHIFT(tmp10 + btmp3, CONST_BITS+PASS1_BITS+3); - pDst_ptr[8*0] = (uint8)CLAMP(i); - - i = DESCALE_ZEROSHIFT(tmp10 - btmp3, CONST_BITS+PASS1_BITS+3); - pDst_ptr[8*7] = (uint8)CLAMP(i); - - i = DESCALE_ZEROSHIFT(tmp11 + btmp2, CONST_BITS+PASS1_BITS+3); - pDst_ptr[8*1] = (uint8)CLAMP(i); - - i = DESCALE_ZEROSHIFT(tmp11 - btmp2, CONST_BITS+PASS1_BITS+3); - pDst_ptr[8*6] = (uint8)CLAMP(i); - - i = DESCALE_ZEROSHIFT(tmp12 + btmp1, CONST_BITS+PASS1_BITS+3); - pDst_ptr[8*2] = (uint8)CLAMP(i); - - i = DESCALE_ZEROSHIFT(tmp12 - btmp1, CONST_BITS+PASS1_BITS+3); - pDst_ptr[8*5] = (uint8)CLAMP(i); - - i = DESCALE_ZEROSHIFT(tmp13 + btmp0, CONST_BITS+PASS1_BITS+3); - pDst_ptr[8*3] = (uint8)CLAMP(i); - - i = DESCALE_ZEROSHIFT(tmp13 - btmp0, CONST_BITS+PASS1_BITS+3); - pDst_ptr[8*4] = (uint8)CLAMP(i); - } -}; - -template <> -struct Col<1> -{ - static void idct(uint8* pDst_ptr, const int* pTemp) - { - int dcval = DESCALE_ZEROSHIFT(pTemp[0], PASS1_BITS+3); - const uint8 dcval_clamped = (uint8)CLAMP(dcval); - pDst_ptr[0*8] = dcval_clamped; - pDst_ptr[1*8] = dcval_clamped; - pDst_ptr[2*8] = dcval_clamped; - pDst_ptr[3*8] = dcval_clamped; - pDst_ptr[4*8] = dcval_clamped; - pDst_ptr[5*8] = dcval_clamped; - pDst_ptr[6*8] = dcval_clamped; - pDst_ptr[7*8] = dcval_clamped; - } -}; - -static const uint8 s_idct_row_table[] = -{ - 1,0,0,0,0,0,0,0, 2,0,0,0,0,0,0,0, 2,1,0,0,0,0,0,0, 2,1,1,0,0,0,0,0, 2,2,1,0,0,0,0,0, 3,2,1,0,0,0,0,0, 4,2,1,0,0,0,0,0, 4,3,1,0,0,0,0,0, - 4,3,2,0,0,0,0,0, 4,3,2,1,0,0,0,0, 4,3,2,1,1,0,0,0, 4,3,2,2,1,0,0,0, 4,3,3,2,1,0,0,0, 4,4,3,2,1,0,0,0, 5,4,3,2,1,0,0,0, 6,4,3,2,1,0,0,0, - 6,5,3,2,1,0,0,0, 6,5,4,2,1,0,0,0, 6,5,4,3,1,0,0,0, 6,5,4,3,2,0,0,0, 6,5,4,3,2,1,0,0, 6,5,4,3,2,1,1,0, 6,5,4,3,2,2,1,0, 6,5,4,3,3,2,1,0, - 6,5,4,4,3,2,1,0, 6,5,5,4,3,2,1,0, 6,6,5,4,3,2,1,0, 7,6,5,4,3,2,1,0, 8,6,5,4,3,2,1,0, 8,7,5,4,3,2,1,0, 8,7,6,4,3,2,1,0, 8,7,6,5,3,2,1,0, - 8,7,6,5,4,2,1,0, 8,7,6,5,4,3,1,0, 8,7,6,5,4,3,2,0, 8,7,6,5,4,3,2,1, 8,7,6,5,4,3,2,2, 8,7,6,5,4,3,3,2, 8,7,6,5,4,4,3,2, 8,7,6,5,5,4,3,2, - 8,7,6,6,5,4,3,2, 8,7,7,6,5,4,3,2, 8,8,7,6,5,4,3,2, 8,8,8,6,5,4,3,2, 8,8,8,7,5,4,3,2, 8,8,8,7,6,4,3,2, 8,8,8,7,6,5,3,2, 8,8,8,7,6,5,4,2, - 8,8,8,7,6,5,4,3, 8,8,8,7,6,5,4,4, 8,8,8,7,6,5,5,4, 8,8,8,7,6,6,5,4, 8,8,8,7,7,6,5,4, 8,8,8,8,7,6,5,4, 8,8,8,8,8,6,5,4, 8,8,8,8,8,7,5,4, - 8,8,8,8,8,7,6,4, 8,8,8,8,8,7,6,5, 8,8,8,8,8,7,6,6, 8,8,8,8,8,7,7,6, 8,8,8,8,8,8,7,6, 8,8,8,8,8,8,8,6, 8,8,8,8,8,8,8,7, 8,8,8,8,8,8,8,8, -}; - -static const uint8 s_idct_col_table[] = { 1, 1, 2, 3, 3, 3, 3, 3, 3, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8 }; - -void idct(const jpgd_block_t* pSrc_ptr, uint8* pDst_ptr, int block_max_zag) -{ - JPGD_ASSERT(block_max_zag >= 1); - JPGD_ASSERT(block_max_zag <= 64); - - if (block_max_zag <= 1) - { - int k = ((pSrc_ptr[0] + 4) >> 3) + 128; - k = CLAMP(k); - k = k | (k<<8); - k = k | (k<<16); - - for (int i = 8; i > 0; i--) - { - *(int*)&pDst_ptr[0] = k; - *(int*)&pDst_ptr[4] = k; - pDst_ptr += 8; - } - return; - } - - int temp[64]; - - const jpgd_block_t* pSrc = pSrc_ptr; - int* pTemp = temp; - - const uint8* pRow_tab = &s_idct_row_table[(block_max_zag - 1) * 8]; - int i; - for (i = 8; i > 0; i--, pRow_tab++) - { - switch (*pRow_tab) - { - case 0: Row<0>::idct(pTemp, pSrc); break; - case 1: Row<1>::idct(pTemp, pSrc); break; - case 2: Row<2>::idct(pTemp, pSrc); break; - case 3: Row<3>::idct(pTemp, pSrc); break; - case 4: Row<4>::idct(pTemp, pSrc); break; - case 5: Row<5>::idct(pTemp, pSrc); break; - case 6: Row<6>::idct(pTemp, pSrc); break; - case 7: Row<7>::idct(pTemp, pSrc); break; - case 8: Row<8>::idct(pTemp, pSrc); break; - } - - pSrc += 8; - pTemp += 8; - } - - pTemp = temp; - - const int nonzero_rows = s_idct_col_table[block_max_zag - 1]; - for (i = 8; i > 0; i--) - { - switch (nonzero_rows) - { - case 1: Col<1>::idct(pDst_ptr, pTemp); break; - case 2: Col<2>::idct(pDst_ptr, pTemp); break; - case 3: Col<3>::idct(pDst_ptr, pTemp); break; - case 4: Col<4>::idct(pDst_ptr, pTemp); break; - case 5: Col<5>::idct(pDst_ptr, pTemp); break; - case 6: Col<6>::idct(pDst_ptr, pTemp); break; - case 7: Col<7>::idct(pDst_ptr, pTemp); break; - case 8: Col<8>::idct(pDst_ptr, pTemp); break; - } - - pTemp++; - pDst_ptr++; - } -} - -void idct_4x4(const jpgd_block_t* pSrc_ptr, uint8* pDst_ptr) -{ - int temp[64]; - int* pTemp = temp; - const jpgd_block_t* pSrc = pSrc_ptr; - - for (int i = 4; i > 0; i--) - { - Row<4>::idct(pTemp, pSrc); - pSrc += 8; - pTemp += 8; - } - - pTemp = temp; - for (int i = 8; i > 0; i--) - { - Col<4>::idct(pDst_ptr, pTemp); - pTemp++; - pDst_ptr++; - } -} - -// Retrieve one character from the input stream. -inline uint jpeg_decoder::get_char() -{ - // Any bytes remaining in buffer? - if (!m_in_buf_left) - { - // Try to get more bytes. - prep_in_buffer(); - // Still nothing to get? - if (!m_in_buf_left) - { - // Pad the end of the stream with 0xFF 0xD9 (EOI marker) - int t = m_tem_flag; - m_tem_flag ^= 1; - if (t) - return 0xD9; - else - return 0xFF; - } - } - - uint c = *m_pIn_buf_ofs++; - m_in_buf_left--; - - return c; -} - -// Same as previous method, except can indicate if the character is a pad character or not. -inline uint jpeg_decoder::get_char(bool *pPadding_flag) -{ - if (!m_in_buf_left) - { - prep_in_buffer(); - if (!m_in_buf_left) - { - *pPadding_flag = true; - int t = m_tem_flag; - m_tem_flag ^= 1; - if (t) - return 0xD9; - else - return 0xFF; - } - } - - *pPadding_flag = false; - - uint c = *m_pIn_buf_ofs++; - m_in_buf_left--; - - return c; -} - -// Inserts a previously retrieved character back into the input buffer. -inline void jpeg_decoder::stuff_char(uint8 q) -{ - *(--m_pIn_buf_ofs) = q; - m_in_buf_left++; -} - -// Retrieves one character from the input stream, but does not read past markers. Will continue to return 0xFF when a marker is encountered. -inline uint8 jpeg_decoder::get_octet() -{ - bool padding_flag; - int c = get_char(&padding_flag); - - if (c == 0xFF) - { - if (padding_flag) - return 0xFF; - - c = get_char(&padding_flag); - if (padding_flag) - { - stuff_char(0xFF); - return 0xFF; - } - - if (c == 0x00) - return 0xFF; - else - { - stuff_char(static_cast(c)); - stuff_char(0xFF); - return 0xFF; - } - } - - return static_cast(c); -} - -// Retrieves a variable number of bits from the input stream. Does not recognize markers. -inline uint jpeg_decoder::get_bits(int num_bits) -{ - if (!num_bits) - return 0; - - uint i = m_bit_buf >> (32 - num_bits); - - if ((m_bits_left -= num_bits) <= 0) - { - m_bit_buf <<= (num_bits += m_bits_left); - - uint c1 = get_char(); - uint c2 = get_char(); - m_bit_buf = (m_bit_buf & 0xFFFF0000) | (c1 << 8) | c2; - - m_bit_buf <<= -m_bits_left; - - m_bits_left += 16; - - JPGD_ASSERT(m_bits_left >= 0); - } - else - m_bit_buf <<= num_bits; - - return i; -} - -// Retrieves a variable number of bits from the input stream. Markers will not be read into the input bit buffer. Instead, an infinite number of all 1's will be returned when a marker is encountered. -inline uint jpeg_decoder::get_bits_no_markers(int num_bits) -{ - if (!num_bits) - return 0; - - uint i = m_bit_buf >> (32 - num_bits); - - if ((m_bits_left -= num_bits) <= 0) - { - m_bit_buf <<= (num_bits += m_bits_left); - - if ((m_in_buf_left < 2) || (m_pIn_buf_ofs[0] == 0xFF) || (m_pIn_buf_ofs[1] == 0xFF)) - { - uint c1 = get_octet(); - uint c2 = get_octet(); - m_bit_buf |= (c1 << 8) | c2; - } - else - { - m_bit_buf |= ((uint)m_pIn_buf_ofs[0] << 8) | m_pIn_buf_ofs[1]; - m_in_buf_left -= 2; - m_pIn_buf_ofs += 2; - } - - m_bit_buf <<= -m_bits_left; - - m_bits_left += 16; - - JPGD_ASSERT(m_bits_left >= 0); - } - else - m_bit_buf <<= num_bits; - - return i; -} - -// Decodes a Huffman encoded symbol. -inline int jpeg_decoder::huff_decode(huff_tables *pH) -{ - int symbol; - - // Check first 8-bits: do we have a complete symbol? - if ((symbol = pH->look_up[m_bit_buf >> 24]) < 0) - { - // Decode more bits, use a tree traversal to find symbol. - int ofs = 23; - do - { - symbol = pH->tree[-(int)(symbol + ((m_bit_buf >> ofs) & 1))]; - ofs--; - } while (symbol < 0); - - get_bits_no_markers(8 + (23 - ofs)); - } - else - get_bits_no_markers(pH->code_size[symbol]); - - return symbol; -} - -// Decodes a Huffman encoded symbol. -inline int jpeg_decoder::huff_decode(huff_tables *pH, int& extra_bits) -{ - int symbol; - - // Check first 8-bits: do we have a complete symbol? - if ((symbol = pH->look_up2[m_bit_buf >> 24]) < 0) - { - // Use a tree traversal to find symbol. - int ofs = 23; - do - { - symbol = pH->tree[-(int)(symbol + ((m_bit_buf >> ofs) & 1))]; - ofs--; - } while (symbol < 0); - - get_bits_no_markers(8 + (23 - ofs)); - - extra_bits = get_bits_no_markers(symbol & 0xF); - } - else - { - JPGD_ASSERT(((symbol >> 8) & 31) == pH->code_size[symbol & 255] + ((symbol & 0x8000) ? (symbol & 15) : 0)); - - if (symbol & 0x8000) - { - get_bits_no_markers((symbol >> 8) & 31); - extra_bits = symbol >> 16; - } - else - { - int code_size = (symbol >> 8) & 31; - int num_extra_bits = symbol & 0xF; - int bits = code_size + num_extra_bits; - if (bits <= (m_bits_left + 16)) - extra_bits = get_bits_no_markers(bits) & ((1 << num_extra_bits) - 1); - else - { - get_bits_no_markers(code_size); - extra_bits = get_bits_no_markers(num_extra_bits); - } - } - - symbol &= 0xFF; - } - - return symbol; -} - -// Tables and macro used to fully decode the DPCM differences. -static const int s_extend_test[16] = { 0, 0x0001, 0x0002, 0x0004, 0x0008, 0x0010, 0x0020, 0x0040, 0x0080, 0x0100, 0x0200, 0x0400, 0x0800, 0x1000, 0x2000, 0x4000 }; -static const int s_extend_offset[16] = { 0, -(1<<1) + 1, -(1<<2) + 1, -(1<<3) + 1, -(1<<4) + 1, -(1<<5) + 1, -(1<<6) + 1, -(1<<7) + 1, -(1<<8) + 1, -(1<<9) + 1, -(1<<10) + 1, -(1<<11) + 1, -(1<<12) + 1, -(1<<13) + 1, -(1<<14) + 1, -(1<<15) + 1 }; -//static const int s_extend_mask[] = { 0, (1<<0), (1<<1), (1<<2), (1<<3), (1<<4), (1<<5), (1<<6), (1<<7), (1<<8), (1<<9), (1<<10), (1<<11), (1<<12), (1<<13), (1<<14), (1<<15), (1<<16) }; -// The logical AND's in this macro are to shut up static code analysis (aren't really necessary - couldn't find another way to do this) + + int temp[64]; + + const jpgd_block_coeff_t* pSrc = pSrc_ptr; + int* pTemp = temp; + + const uint8* pRow_tab = &s_idct_row_table[(block_max_zag - 1) * 8]; + int i; + for (i = 8; i > 0; i--, pRow_tab++) + { + switch (*pRow_tab) + { + case 0: Row<0>::idct(pTemp, pSrc); break; + case 1: Row<1>::idct(pTemp, pSrc); break; + case 2: Row<2>::idct(pTemp, pSrc); break; + case 3: Row<3>::idct(pTemp, pSrc); break; + case 4: Row<4>::idct(pTemp, pSrc); break; + case 5: Row<5>::idct(pTemp, pSrc); break; + case 6: Row<6>::idct(pTemp, pSrc); break; + case 7: Row<7>::idct(pTemp, pSrc); break; + case 8: Row<8>::idct(pTemp, pSrc); break; + } + + pSrc += 8; + pTemp += 8; + } + + pTemp = temp; + + const int nonzero_rows = s_idct_col_table[block_max_zag - 1]; + for (i = 8; i > 0; i--) + { + switch (nonzero_rows) + { + case 1: Col<1>::idct(pDst_ptr, pTemp); break; + case 2: Col<2>::idct(pDst_ptr, pTemp); break; + case 3: Col<3>::idct(pDst_ptr, pTemp); break; + case 4: Col<4>::idct(pDst_ptr, pTemp); break; + case 5: Col<5>::idct(pDst_ptr, pTemp); break; + case 6: Col<6>::idct(pDst_ptr, pTemp); break; + case 7: Col<7>::idct(pDst_ptr, pTemp); break; + case 8: Col<8>::idct(pDst_ptr, pTemp); break; + } + + pTemp++; + pDst_ptr++; + } + } + + // Retrieve one character from the input stream. + inline uint jpeg_decoder::get_char() + { + // Any bytes remaining in buffer? + if (!m_in_buf_left) + { + // Try to get more bytes. + prep_in_buffer(); + // Still nothing to get? + if (!m_in_buf_left) + { + // Pad the end of the stream with 0xFF 0xD9 (EOI marker) + int t = m_tem_flag; + m_tem_flag ^= 1; + if (t) + return 0xD9; + else + return 0xFF; + } + } + + uint c = *m_pIn_buf_ofs++; + m_in_buf_left--; + + return c; + } + + // Same as previous method, except can indicate if the character is a pad character or not. + inline uint jpeg_decoder::get_char(bool* pPadding_flag) + { + if (!m_in_buf_left) + { + prep_in_buffer(); + if (!m_in_buf_left) + { + *pPadding_flag = true; + int t = m_tem_flag; + m_tem_flag ^= 1; + if (t) + return 0xD9; + else + return 0xFF; + } + } + + *pPadding_flag = false; + + uint c = *m_pIn_buf_ofs++; + m_in_buf_left--; + + return c; + } + + // Inserts a previously retrieved character back into the input buffer. + inline void jpeg_decoder::stuff_char(uint8 q) + { + // This could write before the input buffer, but we've placed another array there. + *(--m_pIn_buf_ofs) = q; + m_in_buf_left++; + } + + // Retrieves one character from the input stream, but does not read past markers. Will continue to return 0xFF when a marker is encountered. + inline uint8 jpeg_decoder::get_octet() + { + bool padding_flag; + int c = get_char(&padding_flag); + + if (c == 0xFF) + { + if (padding_flag) + return 0xFF; + + c = get_char(&padding_flag); + if (padding_flag) + { + stuff_char(0xFF); + return 0xFF; + } + + if (c == 0x00) + return 0xFF; + else + { + stuff_char(static_cast(c)); + stuff_char(0xFF); + return 0xFF; + } + } + + return static_cast(c); + } + + // Retrieves a variable number of bits from the input stream. Does not recognize markers. + inline uint jpeg_decoder::get_bits(int num_bits) + { + if (!num_bits) + return 0; + + uint i = m_bit_buf >> (32 - num_bits); + + if ((m_bits_left -= num_bits) <= 0) + { + m_bit_buf <<= (num_bits += m_bits_left); + + uint c1 = get_char(); + uint c2 = get_char(); + m_bit_buf = (m_bit_buf & 0xFFFF0000) | (c1 << 8) | c2; + + m_bit_buf <<= -m_bits_left; + + m_bits_left += 16; + + assert(m_bits_left >= 0); + } + else + m_bit_buf <<= num_bits; + + return i; + } + + // Retrieves a variable number of bits from the input stream. Markers will not be read into the input bit buffer. Instead, an infinite number of all 1's will be returned when a marker is encountered. + inline uint jpeg_decoder::get_bits_no_markers(int num_bits) + { + if (!num_bits) + return 0; + + assert(num_bits <= 16); + + uint i = m_bit_buf >> (32 - num_bits); + + if ((m_bits_left -= num_bits) <= 0) + { + m_bit_buf <<= (num_bits += m_bits_left); + + if ((m_in_buf_left < 2) || (m_pIn_buf_ofs[0] == 0xFF) || (m_pIn_buf_ofs[1] == 0xFF)) + { + uint c1 = get_octet(); + uint c2 = get_octet(); + m_bit_buf |= (c1 << 8) | c2; + } + else + { + m_bit_buf |= ((uint)m_pIn_buf_ofs[0] << 8) | m_pIn_buf_ofs[1]; + m_in_buf_left -= 2; + m_pIn_buf_ofs += 2; + } + + m_bit_buf <<= -m_bits_left; + + m_bits_left += 16; + + assert(m_bits_left >= 0); + } + else + m_bit_buf <<= num_bits; + + return i; + } + + // Decodes a Huffman encoded symbol. + inline int jpeg_decoder::huff_decode(huff_tables* pH) + { + if (!pH) + stop_decoding(JPGD_DECODE_ERROR); + + int symbol; + // Check first 8-bits: do we have a complete symbol? + if ((symbol = pH->look_up[m_bit_buf >> 24]) < 0) + { + // Decode more bits, use a tree traversal to find symbol. + int ofs = 23; + do + { + unsigned int idx = -(int)(symbol + ((m_bit_buf >> ofs) & 1)); + + // This should never happen, but to be safe I'm turning these asserts into a run-time check. + if ((idx >= JPGD_HUFF_TREE_MAX_LENGTH) || (ofs < 0)) + stop_decoding(JPGD_DECODE_ERROR); + + symbol = pH->tree[idx]; + ofs--; + } while (symbol < 0); + + get_bits_no_markers(8 + (23 - ofs)); + } + else + { + assert(symbol < JPGD_HUFF_CODE_SIZE_MAX_LENGTH); + get_bits_no_markers(pH->code_size[symbol]); + } + + return symbol; + } + + // Decodes a Huffman encoded symbol. + inline int jpeg_decoder::huff_decode(huff_tables* pH, int& extra_bits) + { + int symbol; + + if (!pH) + stop_decoding(JPGD_DECODE_ERROR); + + // Check first 8-bits: do we have a complete symbol? + if ((symbol = pH->look_up2[m_bit_buf >> 24]) < 0) + { + // Use a tree traversal to find symbol. + int ofs = 23; + do + { + unsigned int idx = -(int)(symbol + ((m_bit_buf >> ofs) & 1)); + + // This should never happen, but to be safe I'm turning these asserts into a run-time check. + if ((idx >= JPGD_HUFF_TREE_MAX_LENGTH) || (ofs < 0)) + stop_decoding(JPGD_DECODE_ERROR); + + symbol = pH->tree[idx]; + ofs--; + } while (symbol < 0); + + get_bits_no_markers(8 + (23 - ofs)); + + extra_bits = get_bits_no_markers(symbol & 0xF); + } + else + { + if (symbol & 0x8000) + { + //get_bits_no_markers((symbol >> 8) & 31); + assert(((symbol >> 8) & 31) <= 15); + get_bits_no_markers((symbol >> 8) & 15); + extra_bits = symbol >> 16; + } + else + { + int code_size = (symbol >> 8) & 31; + int num_extra_bits = symbol & 0xF; + int bits = code_size + num_extra_bits; + + if (bits <= 16) + extra_bits = get_bits_no_markers(bits) & ((1 << num_extra_bits) - 1); + else + { + get_bits_no_markers(code_size); + extra_bits = get_bits_no_markers(num_extra_bits); + } + } + + symbol &= 0xFF; + } + + return symbol; + } + + // Tables and macro used to fully decode the DPCM differences. + static const int s_extend_test[16] = { 0, 0x0001, 0x0002, 0x0004, 0x0008, 0x0010, 0x0020, 0x0040, 0x0080, 0x0100, 0x0200, 0x0400, 0x0800, 0x1000, 0x2000, 0x4000 }; + static const int s_extend_offset[16] = { 0, -1, -3, -7, -15, -31, -63, -127, -255, -511, -1023, -2047, -4095, -8191, -16383, -32767 }; + //static const int s_extend_mask[] = { 0, (1 << 0), (1 << 1), (1 << 2), (1 << 3), (1 << 4), (1 << 5), (1 << 6), (1 << 7), (1 << 8), (1 << 9), (1 << 10), (1 << 11), (1 << 12), (1 << 13), (1 << 14), (1 << 15), (1 << 16) }; + #define JPGD_HUFF_EXTEND(x, s) (((x) < s_extend_test[s & 15]) ? ((x) + s_extend_offset[s & 15]) : (x)) -// Clamps a value between 0-255. -inline uint8 jpeg_decoder::clamp(int i) -{ - if (static_cast(i) > 255) - i = (((~i) >> 31) & 0xFF); - - return static_cast(i); -} - -namespace DCT_Upsample -{ - struct Matrix44 - { - typedef int Element_Type; - enum { NUM_ROWS = 4, NUM_COLS = 4 }; - - Element_Type v[NUM_ROWS][NUM_COLS]; - - inline int rows() const { return NUM_ROWS; } - inline int cols() const { return NUM_COLS; } - - inline const Element_Type & at(int r, int c) const { return v[r][c]; } - inline Element_Type & at(int r, int c) { return v[r][c]; } - - inline Matrix44() { } - - inline Matrix44& operator += (const Matrix44& a) - { - for (int r = 0; r < NUM_ROWS; r++) - { - at(r, 0) += a.at(r, 0); - at(r, 1) += a.at(r, 1); - at(r, 2) += a.at(r, 2); - at(r, 3) += a.at(r, 3); - } - return *this; - } - - inline Matrix44& operator -= (const Matrix44& a) - { - for (int r = 0; r < NUM_ROWS; r++) - { - at(r, 0) -= a.at(r, 0); - at(r, 1) -= a.at(r, 1); - at(r, 2) -= a.at(r, 2); - at(r, 3) -= a.at(r, 3); - } - return *this; - } - - friend inline Matrix44 operator + (const Matrix44& a, const Matrix44& b) - { - Matrix44 ret; - for (int r = 0; r < NUM_ROWS; r++) - { - ret.at(r, 0) = a.at(r, 0) + b.at(r, 0); - ret.at(r, 1) = a.at(r, 1) + b.at(r, 1); - ret.at(r, 2) = a.at(r, 2) + b.at(r, 2); - ret.at(r, 3) = a.at(r, 3) + b.at(r, 3); - } - return ret; - } - - friend inline Matrix44 operator - (const Matrix44& a, const Matrix44& b) - { - Matrix44 ret; - for (int r = 0; r < NUM_ROWS; r++) - { - ret.at(r, 0) = a.at(r, 0) - b.at(r, 0); - ret.at(r, 1) = a.at(r, 1) - b.at(r, 1); - ret.at(r, 2) = a.at(r, 2) - b.at(r, 2); - ret.at(r, 3) = a.at(r, 3) - b.at(r, 3); - } - return ret; - } - - static inline void add_and_store(jpgd_block_t* pDst, const Matrix44& a, const Matrix44& b) - { - for (int r = 0; r < 4; r++) - { - pDst[0*8 + r] = static_cast(a.at(r, 0) + b.at(r, 0)); - pDst[1*8 + r] = static_cast(a.at(r, 1) + b.at(r, 1)); - pDst[2*8 + r] = static_cast(a.at(r, 2) + b.at(r, 2)); - pDst[3*8 + r] = static_cast(a.at(r, 3) + b.at(r, 3)); - } - } - - static inline void sub_and_store(jpgd_block_t* pDst, const Matrix44& a, const Matrix44& b) - { - for (int r = 0; r < 4; r++) - { - pDst[0*8 + r] = static_cast(a.at(r, 0) - b.at(r, 0)); - pDst[1*8 + r] = static_cast(a.at(r, 1) - b.at(r, 1)); - pDst[2*8 + r] = static_cast(a.at(r, 2) - b.at(r, 2)); - pDst[3*8 + r] = static_cast(a.at(r, 3) - b.at(r, 3)); - } - } - }; - - const int FRACT_BITS = 10; - const int SCALE = 1 << FRACT_BITS; - - typedef int Temp_Type; - #define D(i) (((i) + (SCALE >> 1)) >> FRACT_BITS) - #define F(i) ((int)((i) * SCALE + .5f)) - - // Any decent C++ compiler will optimize this at compile time to a 0, or an array access. - #define AT(c, r) ((((c)>=NUM_COLS)||((r)>=NUM_ROWS)) ? 0 : pSrc[(c)+(r)*8]) - - // NUM_ROWS/NUM_COLS = # of non-zero rows/cols in input matrix - template - struct P_Q - { - static void calc(Matrix44& P, Matrix44& Q, const jpgd_block_t* pSrc) - { - // 4x8 = 4x8 times 8x8, matrix 0 is constant - const Temp_Type X000 = AT(0, 0); - const Temp_Type X001 = AT(0, 1); - const Temp_Type X002 = AT(0, 2); - const Temp_Type X003 = AT(0, 3); - const Temp_Type X004 = AT(0, 4); - const Temp_Type X005 = AT(0, 5); - const Temp_Type X006 = AT(0, 6); - const Temp_Type X007 = AT(0, 7); - const Temp_Type X010 = D(F(0.415735f) * AT(1, 0) + F(0.791065f) * AT(3, 0) + F(-0.352443f) * AT(5, 0) + F(0.277785f) * AT(7, 0)); - const Temp_Type X011 = D(F(0.415735f) * AT(1, 1) + F(0.791065f) * AT(3, 1) + F(-0.352443f) * AT(5, 1) + F(0.277785f) * AT(7, 1)); - const Temp_Type X012 = D(F(0.415735f) * AT(1, 2) + F(0.791065f) * AT(3, 2) + F(-0.352443f) * AT(5, 2) + F(0.277785f) * AT(7, 2)); - const Temp_Type X013 = D(F(0.415735f) * AT(1, 3) + F(0.791065f) * AT(3, 3) + F(-0.352443f) * AT(5, 3) + F(0.277785f) * AT(7, 3)); - const Temp_Type X014 = D(F(0.415735f) * AT(1, 4) + F(0.791065f) * AT(3, 4) + F(-0.352443f) * AT(5, 4) + F(0.277785f) * AT(7, 4)); - const Temp_Type X015 = D(F(0.415735f) * AT(1, 5) + F(0.791065f) * AT(3, 5) + F(-0.352443f) * AT(5, 5) + F(0.277785f) * AT(7, 5)); - const Temp_Type X016 = D(F(0.415735f) * AT(1, 6) + F(0.791065f) * AT(3, 6) + F(-0.352443f) * AT(5, 6) + F(0.277785f) * AT(7, 6)); - const Temp_Type X017 = D(F(0.415735f) * AT(1, 7) + F(0.791065f) * AT(3, 7) + F(-0.352443f) * AT(5, 7) + F(0.277785f) * AT(7, 7)); - const Temp_Type X020 = AT(4, 0); - const Temp_Type X021 = AT(4, 1); - const Temp_Type X022 = AT(4, 2); - const Temp_Type X023 = AT(4, 3); - const Temp_Type X024 = AT(4, 4); - const Temp_Type X025 = AT(4, 5); - const Temp_Type X026 = AT(4, 6); - const Temp_Type X027 = AT(4, 7); - const Temp_Type X030 = D(F(0.022887f) * AT(1, 0) + F(-0.097545f) * AT(3, 0) + F(0.490393f) * AT(5, 0) + F(0.865723f) * AT(7, 0)); - const Temp_Type X031 = D(F(0.022887f) * AT(1, 1) + F(-0.097545f) * AT(3, 1) + F(0.490393f) * AT(5, 1) + F(0.865723f) * AT(7, 1)); - const Temp_Type X032 = D(F(0.022887f) * AT(1, 2) + F(-0.097545f) * AT(3, 2) + F(0.490393f) * AT(5, 2) + F(0.865723f) * AT(7, 2)); - const Temp_Type X033 = D(F(0.022887f) * AT(1, 3) + F(-0.097545f) * AT(3, 3) + F(0.490393f) * AT(5, 3) + F(0.865723f) * AT(7, 3)); - const Temp_Type X034 = D(F(0.022887f) * AT(1, 4) + F(-0.097545f) * AT(3, 4) + F(0.490393f) * AT(5, 4) + F(0.865723f) * AT(7, 4)); - const Temp_Type X035 = D(F(0.022887f) * AT(1, 5) + F(-0.097545f) * AT(3, 5) + F(0.490393f) * AT(5, 5) + F(0.865723f) * AT(7, 5)); - const Temp_Type X036 = D(F(0.022887f) * AT(1, 6) + F(-0.097545f) * AT(3, 6) + F(0.490393f) * AT(5, 6) + F(0.865723f) * AT(7, 6)); - const Temp_Type X037 = D(F(0.022887f) * AT(1, 7) + F(-0.097545f) * AT(3, 7) + F(0.490393f) * AT(5, 7) + F(0.865723f) * AT(7, 7)); - - // 4x4 = 4x8 times 8x4, matrix 1 is constant - P.at(0, 0) = X000; - P.at(0, 1) = D(X001 * F(0.415735f) + X003 * F(0.791065f) + X005 * F(-0.352443f) + X007 * F(0.277785f)); - P.at(0, 2) = X004; - P.at(0, 3) = D(X001 * F(0.022887f) + X003 * F(-0.097545f) + X005 * F(0.490393f) + X007 * F(0.865723f)); - P.at(1, 0) = X010; - P.at(1, 1) = D(X011 * F(0.415735f) + X013 * F(0.791065f) + X015 * F(-0.352443f) + X017 * F(0.277785f)); - P.at(1, 2) = X014; - P.at(1, 3) = D(X011 * F(0.022887f) + X013 * F(-0.097545f) + X015 * F(0.490393f) + X017 * F(0.865723f)); - P.at(2, 0) = X020; - P.at(2, 1) = D(X021 * F(0.415735f) + X023 * F(0.791065f) + X025 * F(-0.352443f) + X027 * F(0.277785f)); - P.at(2, 2) = X024; - P.at(2, 3) = D(X021 * F(0.022887f) + X023 * F(-0.097545f) + X025 * F(0.490393f) + X027 * F(0.865723f)); - P.at(3, 0) = X030; - P.at(3, 1) = D(X031 * F(0.415735f) + X033 * F(0.791065f) + X035 * F(-0.352443f) + X037 * F(0.277785f)); - P.at(3, 2) = X034; - P.at(3, 3) = D(X031 * F(0.022887f) + X033 * F(-0.097545f) + X035 * F(0.490393f) + X037 * F(0.865723f)); - // 40 muls 24 adds - - // 4x4 = 4x8 times 8x4, matrix 1 is constant - Q.at(0, 0) = D(X001 * F(0.906127f) + X003 * F(-0.318190f) + X005 * F(0.212608f) + X007 * F(-0.180240f)); - Q.at(0, 1) = X002; - Q.at(0, 2) = D(X001 * F(-0.074658f) + X003 * F(0.513280f) + X005 * F(0.768178f) + X007 * F(-0.375330f)); - Q.at(0, 3) = X006; - Q.at(1, 0) = D(X011 * F(0.906127f) + X013 * F(-0.318190f) + X015 * F(0.212608f) + X017 * F(-0.180240f)); - Q.at(1, 1) = X012; - Q.at(1, 2) = D(X011 * F(-0.074658f) + X013 * F(0.513280f) + X015 * F(0.768178f) + X017 * F(-0.375330f)); - Q.at(1, 3) = X016; - Q.at(2, 0) = D(X021 * F(0.906127f) + X023 * F(-0.318190f) + X025 * F(0.212608f) + X027 * F(-0.180240f)); - Q.at(2, 1) = X022; - Q.at(2, 2) = D(X021 * F(-0.074658f) + X023 * F(0.513280f) + X025 * F(0.768178f) + X027 * F(-0.375330f)); - Q.at(2, 3) = X026; - Q.at(3, 0) = D(X031 * F(0.906127f) + X033 * F(-0.318190f) + X035 * F(0.212608f) + X037 * F(-0.180240f)); - Q.at(3, 1) = X032; - Q.at(3, 2) = D(X031 * F(-0.074658f) + X033 * F(0.513280f) + X035 * F(0.768178f) + X037 * F(-0.375330f)); - Q.at(3, 3) = X036; - // 40 muls 24 adds - } - }; - - template - struct R_S - { - static void calc(Matrix44& R, Matrix44& S, const jpgd_block_t* pSrc) - { - // 4x8 = 4x8 times 8x8, matrix 0 is constant - const Temp_Type X100 = D(F(0.906127f) * AT(1, 0) + F(-0.318190f) * AT(3, 0) + F(0.212608f) * AT(5, 0) + F(-0.180240f) * AT(7, 0)); - const Temp_Type X101 = D(F(0.906127f) * AT(1, 1) + F(-0.318190f) * AT(3, 1) + F(0.212608f) * AT(5, 1) + F(-0.180240f) * AT(7, 1)); - const Temp_Type X102 = D(F(0.906127f) * AT(1, 2) + F(-0.318190f) * AT(3, 2) + F(0.212608f) * AT(5, 2) + F(-0.180240f) * AT(7, 2)); - const Temp_Type X103 = D(F(0.906127f) * AT(1, 3) + F(-0.318190f) * AT(3, 3) + F(0.212608f) * AT(5, 3) + F(-0.180240f) * AT(7, 3)); - const Temp_Type X104 = D(F(0.906127f) * AT(1, 4) + F(-0.318190f) * AT(3, 4) + F(0.212608f) * AT(5, 4) + F(-0.180240f) * AT(7, 4)); - const Temp_Type X105 = D(F(0.906127f) * AT(1, 5) + F(-0.318190f) * AT(3, 5) + F(0.212608f) * AT(5, 5) + F(-0.180240f) * AT(7, 5)); - const Temp_Type X106 = D(F(0.906127f) * AT(1, 6) + F(-0.318190f) * AT(3, 6) + F(0.212608f) * AT(5, 6) + F(-0.180240f) * AT(7, 6)); - const Temp_Type X107 = D(F(0.906127f) * AT(1, 7) + F(-0.318190f) * AT(3, 7) + F(0.212608f) * AT(5, 7) + F(-0.180240f) * AT(7, 7)); - const Temp_Type X110 = AT(2, 0); - const Temp_Type X111 = AT(2, 1); - const Temp_Type X112 = AT(2, 2); - const Temp_Type X113 = AT(2, 3); - const Temp_Type X114 = AT(2, 4); - const Temp_Type X115 = AT(2, 5); - const Temp_Type X116 = AT(2, 6); - const Temp_Type X117 = AT(2, 7); - const Temp_Type X120 = D(F(-0.074658f) * AT(1, 0) + F(0.513280f) * AT(3, 0) + F(0.768178f) * AT(5, 0) + F(-0.375330f) * AT(7, 0)); - const Temp_Type X121 = D(F(-0.074658f) * AT(1, 1) + F(0.513280f) * AT(3, 1) + F(0.768178f) * AT(5, 1) + F(-0.375330f) * AT(7, 1)); - const Temp_Type X122 = D(F(-0.074658f) * AT(1, 2) + F(0.513280f) * AT(3, 2) + F(0.768178f) * AT(5, 2) + F(-0.375330f) * AT(7, 2)); - const Temp_Type X123 = D(F(-0.074658f) * AT(1, 3) + F(0.513280f) * AT(3, 3) + F(0.768178f) * AT(5, 3) + F(-0.375330f) * AT(7, 3)); - const Temp_Type X124 = D(F(-0.074658f) * AT(1, 4) + F(0.513280f) * AT(3, 4) + F(0.768178f) * AT(5, 4) + F(-0.375330f) * AT(7, 4)); - const Temp_Type X125 = D(F(-0.074658f) * AT(1, 5) + F(0.513280f) * AT(3, 5) + F(0.768178f) * AT(5, 5) + F(-0.375330f) * AT(7, 5)); - const Temp_Type X126 = D(F(-0.074658f) * AT(1, 6) + F(0.513280f) * AT(3, 6) + F(0.768178f) * AT(5, 6) + F(-0.375330f) * AT(7, 6)); - const Temp_Type X127 = D(F(-0.074658f) * AT(1, 7) + F(0.513280f) * AT(3, 7) + F(0.768178f) * AT(5, 7) + F(-0.375330f) * AT(7, 7)); - const Temp_Type X130 = AT(6, 0); - const Temp_Type X131 = AT(6, 1); - const Temp_Type X132 = AT(6, 2); - const Temp_Type X133 = AT(6, 3); - const Temp_Type X134 = AT(6, 4); - const Temp_Type X135 = AT(6, 5); - const Temp_Type X136 = AT(6, 6); - const Temp_Type X137 = AT(6, 7); - // 80 muls 48 adds - - // 4x4 = 4x8 times 8x4, matrix 1 is constant - R.at(0, 0) = X100; - R.at(0, 1) = D(X101 * F(0.415735f) + X103 * F(0.791065f) + X105 * F(-0.352443f) + X107 * F(0.277785f)); - R.at(0, 2) = X104; - R.at(0, 3) = D(X101 * F(0.022887f) + X103 * F(-0.097545f) + X105 * F(0.490393f) + X107 * F(0.865723f)); - R.at(1, 0) = X110; - R.at(1, 1) = D(X111 * F(0.415735f) + X113 * F(0.791065f) + X115 * F(-0.352443f) + X117 * F(0.277785f)); - R.at(1, 2) = X114; - R.at(1, 3) = D(X111 * F(0.022887f) + X113 * F(-0.097545f) + X115 * F(0.490393f) + X117 * F(0.865723f)); - R.at(2, 0) = X120; - R.at(2, 1) = D(X121 * F(0.415735f) + X123 * F(0.791065f) + X125 * F(-0.352443f) + X127 * F(0.277785f)); - R.at(2, 2) = X124; - R.at(2, 3) = D(X121 * F(0.022887f) + X123 * F(-0.097545f) + X125 * F(0.490393f) + X127 * F(0.865723f)); - R.at(3, 0) = X130; - R.at(3, 1) = D(X131 * F(0.415735f) + X133 * F(0.791065f) + X135 * F(-0.352443f) + X137 * F(0.277785f)); - R.at(3, 2) = X134; - R.at(3, 3) = D(X131 * F(0.022887f) + X133 * F(-0.097545f) + X135 * F(0.490393f) + X137 * F(0.865723f)); - // 40 muls 24 adds - // 4x4 = 4x8 times 8x4, matrix 1 is constant - S.at(0, 0) = D(X101 * F(0.906127f) + X103 * F(-0.318190f) + X105 * F(0.212608f) + X107 * F(-0.180240f)); - S.at(0, 1) = X102; - S.at(0, 2) = D(X101 * F(-0.074658f) + X103 * F(0.513280f) + X105 * F(0.768178f) + X107 * F(-0.375330f)); - S.at(0, 3) = X106; - S.at(1, 0) = D(X111 * F(0.906127f) + X113 * F(-0.318190f) + X115 * F(0.212608f) + X117 * F(-0.180240f)); - S.at(1, 1) = X112; - S.at(1, 2) = D(X111 * F(-0.074658f) + X113 * F(0.513280f) + X115 * F(0.768178f) + X117 * F(-0.375330f)); - S.at(1, 3) = X116; - S.at(2, 0) = D(X121 * F(0.906127f) + X123 * F(-0.318190f) + X125 * F(0.212608f) + X127 * F(-0.180240f)); - S.at(2, 1) = X122; - S.at(2, 2) = D(X121 * F(-0.074658f) + X123 * F(0.513280f) + X125 * F(0.768178f) + X127 * F(-0.375330f)); - S.at(2, 3) = X126; - S.at(3, 0) = D(X131 * F(0.906127f) + X133 * F(-0.318190f) + X135 * F(0.212608f) + X137 * F(-0.180240f)); - S.at(3, 1) = X132; - S.at(3, 2) = D(X131 * F(-0.074658f) + X133 * F(0.513280f) + X135 * F(0.768178f) + X137 * F(-0.375330f)); - S.at(3, 3) = X136; - // 40 muls 24 adds - } - }; -} // end namespace DCT_Upsample - -// Unconditionally frees all allocated m_blocks. -void jpeg_decoder::free_all_blocks() -{ - m_pStream = NULL; - for (mem_block *b = m_pMem_blocks; b; ) - { - mem_block *n = b->m_pNext; - jpgd_free(b); - b = n; - } - m_pMem_blocks = NULL; -} - -// This method handles all errors. It will never return. -// It could easily be changed to use C++ exceptions. -JPGD_NORETURN void jpeg_decoder::stop_decoding(jpgd_status status) -{ - m_error_code = status; - free_all_blocks(); - longjmp(m_jmp_state, status); -} - -void *jpeg_decoder::alloc(size_t nSize, bool zero) -{ - nSize = (JPGD_MAX(nSize, 1) + 3) & ~3; - char *rv = NULL; - for (mem_block *b = m_pMem_blocks; b; b = b->m_pNext) - { - if ((b->m_used_count + nSize) <= b->m_size) - { - rv = b->m_data + b->m_used_count; - b->m_used_count += nSize; - break; - } - } - if (!rv) - { - int capacity = (int)JPGD_MAX(32768 - 256, (nSize + 2047) & ~2047); - mem_block *b = (mem_block*)jpgd_malloc(sizeof(mem_block) + capacity); - if (!b) { stop_decoding(JPGD_NOTENOUGHMEM); } - b->m_pNext = m_pMem_blocks; m_pMem_blocks = b; - b->m_used_count = nSize; - b->m_size = capacity; - rv = b->m_data; - } - if (zero) memset(rv, 0, nSize); - return rv; -} - -void jpeg_decoder::word_clear(void *p, uint16 c, uint n) -{ - uint8 *pD = (uint8*)p; - const uint8 l = c & 0xFF, h = (c >> 8) & 0xFF; - while (n) - { - pD[0] = l; pD[1] = h; pD += 2; - n--; - } -} - -// Refill the input buffer. -// This method will sit in a loop until (A) the buffer is full or (B) -// the stream's read() method reports and end of file condition. -void jpeg_decoder::prep_in_buffer() -{ - m_in_buf_left = 0; - m_pIn_buf_ofs = m_in_buf; - - if (m_eof_flag) - return; - - do - { - int bytes_read = m_pStream->read(m_in_buf + m_in_buf_left, JPGD_IN_BUF_SIZE - m_in_buf_left, &m_eof_flag); - if (bytes_read == -1) - stop_decoding(JPGD_STREAM_READ); - - m_in_buf_left += bytes_read; - } while ((m_in_buf_left < JPGD_IN_BUF_SIZE) && (!m_eof_flag)); - - m_total_bytes_read += m_in_buf_left; - - // Pad the end of the block with M_EOI (prevents the decompressor from going off the rails if the stream is invalid). - // (This dates way back to when this decompressor was written in C/asm, and the all-asm Huffman decoder did some fancy things to increase perf.) - word_clear(m_pIn_buf_ofs + m_in_buf_left, 0xD9FF, 64); -} - -// Read a Huffman code table. -void jpeg_decoder::read_dht_marker() -{ - int i, index, count; - uint8 huff_num[17]; - uint8 huff_val[256]; - - uint num_left = get_bits(16); - - if (num_left < 2) - stop_decoding(JPGD_BAD_DHT_MARKER); - - num_left -= 2; - - while (num_left) - { - index = get_bits(8); - - huff_num[0] = 0; - - count = 0; - - for (i = 1; i <= 16; i++) - { - huff_num[i] = static_cast(get_bits(8)); - count += huff_num[i]; - } - - if (count > 255) - stop_decoding(JPGD_BAD_DHT_COUNTS); - - for (i = 0; i < count; i++) - huff_val[i] = static_cast(get_bits(8)); + // Unconditionally frees all allocated m_blocks. + void jpeg_decoder::free_all_blocks() + { + m_pStream = nullptr; + for (mem_block* b = m_pMem_blocks; b; ) + { + mem_block* n = b->m_pNext; + jpgd_free(b); + b = n; + } + m_pMem_blocks = nullptr; + } + + // This method handles all errors. It will never return. + // It could easily be changed to use C++ exceptions. + JPGD_NORETURN void jpeg_decoder::stop_decoding(jpgd_status status) + { + m_error_code = status; + free_all_blocks(); + longjmp(m_jmp_state, status); + } + + void* jpeg_decoder::alloc(size_t nSize, bool zero) + { + nSize = (JPGD_MAX(nSize, 1) + 3) & ~3; + char* rv = nullptr; + for (mem_block* b = m_pMem_blocks; b; b = b->m_pNext) + { + if ((b->m_used_count + nSize) <= b->m_size) + { + rv = b->m_data + b->m_used_count; + b->m_used_count += nSize; + break; + } + } + if (!rv) + { + int capacity = JPGD_MAX(32768 - 256, (nSize + 2047) & ~2047); + mem_block* b = (mem_block*)jpgd_malloc(sizeof(mem_block) + capacity); + if (!b) + { + stop_decoding(JPGD_NOTENOUGHMEM); + } + + b->m_pNext = m_pMem_blocks; + m_pMem_blocks = b; + b->m_used_count = nSize; + b->m_size = capacity; + rv = b->m_data; + } + if (zero) memset(rv, 0, nSize); + return rv; + } + + void* jpeg_decoder::alloc_aligned(size_t nSize, uint32_t align, bool zero) + { + assert((align >= 1U) && ((align & (align - 1U)) == 0U)); + void *p = alloc(nSize + align - 1U, zero); + p = (void *)( ((uintptr_t)p + (align - 1U)) & ~((uintptr_t)(align - 1U)) ); + return p; + } + + void jpeg_decoder::word_clear(void* p, uint16 c, uint n) + { + uint8* pD = (uint8*)p; + const uint8 l = c & 0xFF, h = (c >> 8) & 0xFF; + while (n) + { + pD[0] = l; + pD[1] = h; + pD += 2; + n--; + } + } + + // Refill the input buffer. + // This method will sit in a loop until (A) the buffer is full or (B) + // the stream's read() method reports and end of file condition. + void jpeg_decoder::prep_in_buffer() + { + m_in_buf_left = 0; + m_pIn_buf_ofs = m_in_buf; + + if (m_eof_flag) + return; + + do + { + int bytes_read = m_pStream->read(m_in_buf + m_in_buf_left, JPGD_IN_BUF_SIZE - m_in_buf_left, &m_eof_flag); + if (bytes_read == -1) + stop_decoding(JPGD_STREAM_READ); + + m_in_buf_left += bytes_read; + } while ((m_in_buf_left < JPGD_IN_BUF_SIZE) && (!m_eof_flag)); + + m_total_bytes_read += m_in_buf_left; + + // Pad the end of the block with M_EOI (prevents the decompressor from going off the rails if the stream is invalid). + // (This dates way back to when this decompressor was written in C/asm, and the all-asm Huffman decoder did some fancy things to increase perf.) + word_clear(m_pIn_buf_ofs + m_in_buf_left, 0xD9FF, 64); + } + + // Read a Huffman code table. + void jpeg_decoder::read_dht_marker() + { + int i, index, count; + uint8 huff_num[17]; + uint8 huff_val[256]; + + uint num_left = get_bits(16); + + if (num_left < 2) + stop_decoding(JPGD_BAD_DHT_MARKER); + + num_left -= 2; + + while (num_left) + { + index = get_bits(8); + + huff_num[0] = 0; + + count = 0; + + for (i = 1; i <= 16; i++) + { + huff_num[i] = static_cast(get_bits(8)); + count += huff_num[i]; + } + + if (count > 255) + stop_decoding(JPGD_BAD_DHT_COUNTS); + + bool symbol_present[256]; + memset(symbol_present, 0, sizeof(symbol_present)); + + for (i = 0; i < count; i++) + { + const int s = get_bits(8); - i = 1 + 16 + count; + // Check for obviously bogus tables. + if (symbol_present[s]) + stop_decoding(JPGD_BAD_DHT_COUNTS); - if (num_left < (uint)i) - stop_decoding(JPGD_BAD_DHT_MARKER); + huff_val[i] = static_cast(s); + symbol_present[s] = true; + } - num_left -= i; + i = 1 + 16 + count; - if ((index & 0x10) > 0x10) - stop_decoding(JPGD_BAD_DHT_INDEX); + if (num_left < (uint)i) + stop_decoding(JPGD_BAD_DHT_MARKER); - index = (index & 0x0F) + ((index & 0x10) >> 4) * (JPGD_MAX_HUFF_TABLES >> 1); + num_left -= i; - if (index >= JPGD_MAX_HUFF_TABLES) - stop_decoding(JPGD_BAD_DHT_INDEX); + if ((index & 0x10) > 0x10) + stop_decoding(JPGD_BAD_DHT_INDEX); - if (!m_huff_num[index]) - m_huff_num[index] = (uint8 *)alloc(17); + index = (index & 0x0F) + ((index & 0x10) >> 4) * (JPGD_MAX_HUFF_TABLES >> 1); - if (!m_huff_val[index]) - m_huff_val[index] = (uint8 *)alloc(256); + if (index >= JPGD_MAX_HUFF_TABLES) + stop_decoding(JPGD_BAD_DHT_INDEX); - m_huff_ac[index] = (index & 0x10) != 0; - memcpy(m_huff_num[index], huff_num, 17); - memcpy(m_huff_val[index], huff_val, 256); - } -} + if (!m_huff_num[index]) + m_huff_num[index] = (uint8*)alloc(17); -// Read a quantization table. -void jpeg_decoder::read_dqt_marker() -{ - int n, i, prec; - uint num_left; - uint temp; + if (!m_huff_val[index]) + m_huff_val[index] = (uint8*)alloc(256); - num_left = get_bits(16); + m_huff_ac[index] = (index & 0x10) != 0; + memcpy(m_huff_num[index], huff_num, 17); + memcpy(m_huff_val[index], huff_val, 256); + } + } - if (num_left < 2) - stop_decoding(JPGD_BAD_DQT_MARKER); + // Read a quantization table. + void jpeg_decoder::read_dqt_marker() + { + int n, i, prec; + uint num_left; + uint temp; - num_left -= 2; + num_left = get_bits(16); - while (num_left) - { - n = get_bits(8); - prec = n >> 4; - n &= 0x0F; + if (num_left < 2) + stop_decoding(JPGD_BAD_DQT_MARKER); - if (n >= JPGD_MAX_QUANT_TABLES) - stop_decoding(JPGD_BAD_DQT_TABLE); + num_left -= 2; - if (!m_quant[n]) - m_quant[n] = (jpgd_quant_t *)alloc(64 * sizeof(jpgd_quant_t)); + while (num_left) + { + n = get_bits(8); + prec = n >> 4; + n &= 0x0F; - // read quantization entries, in zag order - for (i = 0; i < 64; i++) - { - temp = get_bits(8); + if (n >= JPGD_MAX_QUANT_TABLES) + stop_decoding(JPGD_BAD_DQT_TABLE); - if (prec) - temp = (temp << 8) + get_bits(8); + if (!m_quant[n]) + m_quant[n] = (jpgd_quant_t*)alloc(64 * sizeof(jpgd_quant_t)); - m_quant[n][i] = static_cast(temp); - } + // read quantization entries, in zag order + for (i = 0; i < 64; i++) + { + temp = get_bits(8); - i = 64 + 1; + if (prec) + temp = (temp << 8) + get_bits(8); - if (prec) - i += 64; + m_quant[n][i] = static_cast(temp); + } - if (num_left < (uint)i) - stop_decoding(JPGD_BAD_DQT_LENGTH); + i = 64 + 1; - num_left -= i; - } -} + if (prec) + i += 64; -// Read the start of frame (SOF) marker. -void jpeg_decoder::read_sof_marker() -{ - int i; - uint num_left; + if (num_left < (uint)i) + stop_decoding(JPGD_BAD_DQT_LENGTH); - num_left = get_bits(16); + num_left -= i; + } + } - if (get_bits(8) != 8) /* precision: sorry, only 8-bit precision is supported right now */ - stop_decoding(JPGD_BAD_PRECISION); + // Read the start of frame (SOF) marker. + void jpeg_decoder::read_sof_marker() + { + int i; + uint num_left; - m_image_y_size = get_bits(16); + num_left = get_bits(16); - if ((m_image_y_size < 1) || (m_image_y_size > JPGD_MAX_HEIGHT)) - stop_decoding(JPGD_BAD_HEIGHT); + /* precision: sorry, only 8-bit precision is supported */ + if (get_bits(8) != 8) + stop_decoding(JPGD_BAD_PRECISION); - m_image_x_size = get_bits(16); + m_image_y_size = get_bits(16); - if ((m_image_x_size < 1) || (m_image_x_size > JPGD_MAX_WIDTH)) - stop_decoding(JPGD_BAD_WIDTH); + if ((m_image_y_size < 1) || (m_image_y_size > JPGD_MAX_HEIGHT)) + stop_decoding(JPGD_BAD_HEIGHT); - m_comps_in_frame = get_bits(8); + m_image_x_size = get_bits(16); - if (m_comps_in_frame > JPGD_MAX_COMPONENTS) - stop_decoding(JPGD_TOO_MANY_COMPONENTS); + if ((m_image_x_size < 1) || (m_image_x_size > JPGD_MAX_WIDTH)) + stop_decoding(JPGD_BAD_WIDTH); - if (num_left != (uint)(m_comps_in_frame * 3 + 8)) - stop_decoding(JPGD_BAD_SOF_LENGTH); + m_comps_in_frame = get_bits(8); - for (i = 0; i < m_comps_in_frame; i++) - { - m_comp_ident[i] = get_bits(8); - m_comp_h_samp[i] = get_bits(4); - m_comp_v_samp[i] = get_bits(4); - m_comp_quant[i] = get_bits(8); - } -} + if (m_comps_in_frame > JPGD_MAX_COMPONENTS) + stop_decoding(JPGD_TOO_MANY_COMPONENTS); -// Used to skip unrecognized markers. -void jpeg_decoder::skip_variable_marker() -{ - uint num_left; + if (num_left != (uint)(m_comps_in_frame * 3 + 8)) + stop_decoding(JPGD_BAD_SOF_LENGTH); - num_left = get_bits(16); + for (i = 0; i < m_comps_in_frame; i++) + { + m_comp_ident[i] = get_bits(8); + m_comp_h_samp[i] = get_bits(4); + m_comp_v_samp[i] = get_bits(4); - if (num_left < 2) - stop_decoding(JPGD_BAD_VARIABLE_MARKER); + if (!m_comp_h_samp[i] || !m_comp_v_samp[i] || (m_comp_h_samp[i] > 2) || (m_comp_v_samp[i] > 2)) + stop_decoding(JPGD_UNSUPPORTED_SAMP_FACTORS); - num_left -= 2; + m_comp_quant[i] = get_bits(8); + if (m_comp_quant[i] >= JPGD_MAX_QUANT_TABLES) + stop_decoding(JPGD_DECODE_ERROR); + } + } - while (num_left) - { - get_bits(8); - num_left--; - } -} + // Used to skip unrecognized markers. + void jpeg_decoder::skip_variable_marker() + { + uint num_left; -// Read a define restart interval (DRI) marker. -void jpeg_decoder::read_dri_marker() -{ - if (get_bits(16) != 4) - stop_decoding(JPGD_BAD_DRI_LENGTH); + num_left = get_bits(16); - m_restart_interval = get_bits(16); -} + if (num_left < 2) + stop_decoding(JPGD_BAD_VARIABLE_MARKER); -// Read a start of scan (SOS) marker. -void jpeg_decoder::read_sos_marker() -{ - uint num_left; - int i, ci, n, c, cc; + num_left -= 2; - num_left = get_bits(16); + while (num_left) + { + get_bits(8); + num_left--; + } + } - n = get_bits(8); - - m_comps_in_scan = n; - - num_left -= 3; - - if ( (num_left != (uint)(n * 2 + 3)) || (n < 1) || (n > JPGD_MAX_COMPS_IN_SCAN) ) - stop_decoding(JPGD_BAD_SOS_LENGTH); - - for (i = 0; i < n; i++) - { - cc = get_bits(8); - c = get_bits(8); - num_left -= 2; - - for (ci = 0; ci < m_comps_in_frame; ci++) - if (cc == m_comp_ident[ci]) - break; - - if (ci >= m_comps_in_frame) - stop_decoding(JPGD_BAD_SOS_COMP_ID); - - m_comp_list[i] = ci; - m_comp_dc_tab[ci] = (c >> 4) & 15; - m_comp_ac_tab[ci] = (c & 15) + (JPGD_MAX_HUFF_TABLES >> 1); - } - - m_spectral_start = get_bits(8); - m_spectral_end = get_bits(8); - m_successive_high = get_bits(4); - m_successive_low = get_bits(4); - - if (!m_progressive_flag) - { - m_spectral_start = 0; - m_spectral_end = 63; - } - - num_left -= 3; - - while (num_left) /* read past whatever is num_left */ - { - get_bits(8); - num_left--; - } -} - -// Finds the next marker. -int jpeg_decoder::next_marker() -{ - uint c, bytes; - - bytes = 0; - - do - { - do - { - bytes++; - c = get_bits(8); - } while (c != 0xFF); - - do - { - c = get_bits(8); - } while (c == 0xFF); - - } while (c == 0); - - // If bytes > 0 here, there where extra bytes before the marker (not good). - - return c; -} - -// Process markers. Returns when an SOFx, SOI, EOI, or SOS marker is -// encountered. -int jpeg_decoder::process_markers() -{ - int c; - - for ( ; ; ) - { - c = next_marker(); - - switch (c) - { - case M_SOF0: - case M_SOF1: - case M_SOF2: - case M_SOF3: - case M_SOF5: - case M_SOF6: - case M_SOF7: -// case M_JPG: - case M_SOF9: - case M_SOF10: - case M_SOF11: - case M_SOF13: - case M_SOF14: - case M_SOF15: - case M_SOI: - case M_EOI: - case M_SOS: - { - return c; - } - case M_DHT: - { - read_dht_marker(); - break; - } - // No arithmitic support - dumb patents! - case M_DAC: - { - stop_decoding(JPGD_NO_ARITHMITIC_SUPPORT); - break; - } - case M_DQT: - { - read_dqt_marker(); - break; - } - case M_DRI: - { - read_dri_marker(); - break; - } - //case M_APP0: /* no need to read the JFIF marker */ - - case M_JPG: - case M_RST0: /* no parameters */ - case M_RST1: - case M_RST2: - case M_RST3: - case M_RST4: - case M_RST5: - case M_RST6: - case M_RST7: - case M_TEM: - { - stop_decoding(JPGD_UNEXPECTED_MARKER); - break; - } - default: /* must be DNL, DHP, EXP, APPn, JPGn, COM, or RESn or APP0 */ - { - skip_variable_marker(); - break; - } - } - } -} - -// Finds the start of image (SOI) marker. -// This code is rather defensive: it only checks the first 512 bytes to avoid -// false positives. -void jpeg_decoder::locate_soi_marker() -{ - uint lastchar, thischar; - uint bytesleft; - - lastchar = get_bits(8); - - thischar = get_bits(8); - - /* ok if it's a normal JPEG file without a special header */ - - if ((lastchar == 0xFF) && (thischar == M_SOI)) - return; - - bytesleft = 4096; //512; - - for ( ; ; ) - { - if (--bytesleft == 0) - stop_decoding(JPGD_NOT_JPEG); - - lastchar = thischar; - - thischar = get_bits(8); - - if (lastchar == 0xFF) - { - if (thischar == M_SOI) - break; - else if (thischar == M_EOI) // get_bits will keep returning M_EOI if we read past the end - stop_decoding(JPGD_NOT_JPEG); - } - } - - // Check the next character after marker: if it's not 0xFF, it can't be the start of the next marker, so the file is bad. - thischar = (m_bit_buf >> 24) & 0xFF; - - if (thischar != 0xFF) - stop_decoding(JPGD_NOT_JPEG); -} - -// Find a start of frame (SOF) marker. -void jpeg_decoder::locate_sof_marker() -{ - locate_soi_marker(); - - int c = process_markers(); - - switch (c) - { - case M_SOF2: - m_progressive_flag = JPGD_TRUE; - case M_SOF0: /* baseline DCT */ - case M_SOF1: /* extended sequential DCT */ - { - read_sof_marker(); - break; - } - case M_SOF9: /* Arithmitic coding */ - { - stop_decoding(JPGD_NO_ARITHMITIC_SUPPORT); - break; - } - default: - { - stop_decoding(JPGD_UNSUPPORTED_MARKER); - break; - } - } -} - -// Find a start of scan (SOS) marker. -int jpeg_decoder::locate_sos_marker() -{ - int c; - - c = process_markers(); - - if (c == M_EOI) - return JPGD_FALSE; - else if (c != M_SOS) - stop_decoding(JPGD_UNEXPECTED_MARKER); - - read_sos_marker(); - - return JPGD_TRUE; -} - -// Reset everything to default/uninitialized state. -void jpeg_decoder::init(jpeg_decoder_stream *pStream) -{ - m_pMem_blocks = NULL; - m_error_code = JPGD_SUCCESS; - m_ready_flag = false; - m_image_x_size = m_image_y_size = 0; - m_pStream = pStream; - m_progressive_flag = JPGD_FALSE; - - memset(m_huff_ac, 0, sizeof(m_huff_ac)); - memset(m_huff_num, 0, sizeof(m_huff_num)); - memset(m_huff_val, 0, sizeof(m_huff_val)); - memset(m_quant, 0, sizeof(m_quant)); - - m_scan_type = 0; - m_comps_in_frame = 0; - - memset(m_comp_h_samp, 0, sizeof(m_comp_h_samp)); - memset(m_comp_v_samp, 0, sizeof(m_comp_v_samp)); - memset(m_comp_quant, 0, sizeof(m_comp_quant)); - memset(m_comp_ident, 0, sizeof(m_comp_ident)); - memset(m_comp_h_blocks, 0, sizeof(m_comp_h_blocks)); - memset(m_comp_v_blocks, 0, sizeof(m_comp_v_blocks)); - - m_comps_in_scan = 0; - memset(m_comp_list, 0, sizeof(m_comp_list)); - memset(m_comp_dc_tab, 0, sizeof(m_comp_dc_tab)); - memset(m_comp_ac_tab, 0, sizeof(m_comp_ac_tab)); - - m_spectral_start = 0; - m_spectral_end = 0; - m_successive_low = 0; - m_successive_high = 0; - m_max_mcu_x_size = 0; - m_max_mcu_y_size = 0; - m_blocks_per_mcu = 0; - m_max_blocks_per_row = 0; - m_mcus_per_row = 0; - m_mcus_per_col = 0; - m_expanded_blocks_per_component = 0; - m_expanded_blocks_per_mcu = 0; - m_expanded_blocks_per_row = 0; - m_freq_domain_chroma_upsample = false; - - memset(m_mcu_org, 0, sizeof(m_mcu_org)); - - m_total_lines_left = 0; - m_mcu_lines_left = 0; - m_real_dest_bytes_per_scan_line = 0; - m_dest_bytes_per_scan_line = 0; - m_dest_bytes_per_pixel = 0; - - memset(m_pHuff_tabs, 0, sizeof(m_pHuff_tabs)); - - memset(m_dc_coeffs, 0, sizeof(m_dc_coeffs)); - memset(m_ac_coeffs, 0, sizeof(m_ac_coeffs)); - memset(m_block_y_mcu, 0, sizeof(m_block_y_mcu)); - - m_eob_run = 0; - - memset(m_block_y_mcu, 0, sizeof(m_block_y_mcu)); - - m_pIn_buf_ofs = m_in_buf; - m_in_buf_left = 0; - m_eof_flag = false; - m_tem_flag = 0; - - memset(m_in_buf_pad_start, 0, sizeof(m_in_buf_pad_start)); - memset(m_in_buf, 0, sizeof(m_in_buf)); - memset(m_in_buf_pad_end, 0, sizeof(m_in_buf_pad_end)); - - m_restart_interval = 0; - m_restarts_left = 0; - m_next_restart_num = 0; - - m_max_mcus_per_row = 0; - m_max_blocks_per_mcu = 0; - m_max_mcus_per_col = 0; - - memset(m_last_dc_val, 0, sizeof(m_last_dc_val)); - m_pMCU_coefficients = NULL; - m_pSample_buf = NULL; - - m_total_bytes_read = 0; - - m_pScan_line_0 = NULL; - m_pScan_line_1 = NULL; - - // Ready the input buffer. - prep_in_buffer(); - - // Prime the bit buffer. - m_bits_left = 16; - m_bit_buf = 0; - - get_bits(16); - get_bits(16); - - for (int i = 0; i < JPGD_MAX_BLOCKS_PER_MCU; i++) - m_mcu_block_max_zag[i] = 64; -} + // Read a define restart interval (DRI) marker. + void jpeg_decoder::read_dri_marker() + { + if (get_bits(16) != 4) + stop_decoding(JPGD_BAD_DRI_LENGTH); + + m_restart_interval = get_bits(16); + } + + // Read a start of scan (SOS) marker. + void jpeg_decoder::read_sos_marker() + { + uint num_left; + int i, ci, n, c, cc; + + num_left = get_bits(16); + + n = get_bits(8); + + m_comps_in_scan = n; + + num_left -= 3; + + if ((num_left != (uint)(n * 2 + 3)) || (n < 1) || (n > JPGD_MAX_COMPS_IN_SCAN)) + stop_decoding(JPGD_BAD_SOS_LENGTH); + + for (i = 0; i < n; i++) + { + cc = get_bits(8); + c = get_bits(8); + num_left -= 2; + + for (ci = 0; ci < m_comps_in_frame; ci++) + if (cc == m_comp_ident[ci]) + break; + + if (ci >= m_comps_in_frame) + stop_decoding(JPGD_BAD_SOS_COMP_ID); + + if (ci >= JPGD_MAX_COMPONENTS) + stop_decoding(JPGD_DECODE_ERROR); + + m_comp_list[i] = ci; + + m_comp_dc_tab[ci] = (c >> 4) & 15; + m_comp_ac_tab[ci] = (c & 15) + (JPGD_MAX_HUFF_TABLES >> 1); + + if (m_comp_dc_tab[ci] >= JPGD_MAX_HUFF_TABLES) + stop_decoding(JPGD_DECODE_ERROR); + + if (m_comp_ac_tab[ci] >= JPGD_MAX_HUFF_TABLES) + stop_decoding(JPGD_DECODE_ERROR); + } + + m_spectral_start = get_bits(8); + m_spectral_end = get_bits(8); + m_successive_high = get_bits(4); + m_successive_low = get_bits(4); + + if (!m_progressive_flag) + { + m_spectral_start = 0; + m_spectral_end = 63; + } + + num_left -= 3; + + /* read past whatever is num_left */ + while (num_left) + { + get_bits(8); + num_left--; + } + } + + // Finds the next marker. + int jpeg_decoder::next_marker() + { + uint c, bytes; + + bytes = 0; + + do + { + do + { + bytes++; + c = get_bits(8); + } while (c != 0xFF); + + do + { + c = get_bits(8); + } while (c == 0xFF); + + } while (c == 0); + + // If bytes > 0 here, there where extra bytes before the marker (not good). + + return c; + } + + // Process markers. Returns when an SOFx, SOI, EOI, or SOS marker is + // encountered. + int jpeg_decoder::process_markers() + { + int c; + + for (; ; ) + { + c = next_marker(); + + switch (c) + { + case M_SOF0: + case M_SOF1: + case M_SOF2: + case M_SOF3: + case M_SOF5: + case M_SOF6: + case M_SOF7: + // case M_JPG: + case M_SOF9: + case M_SOF10: + case M_SOF11: + case M_SOF13: + case M_SOF14: + case M_SOF15: + case M_SOI: + case M_EOI: + case M_SOS: + { + return c; + } + case M_DHT: + { + read_dht_marker(); + break; + } + // No arithmitic support - dumb patents! + case M_DAC: + { + stop_decoding(JPGD_NO_ARITHMITIC_SUPPORT); + break; + } + case M_DQT: + { + read_dqt_marker(); + break; + } + case M_DRI: + { + read_dri_marker(); + break; + } + //case M_APP0: /* no need to read the JFIF marker */ + case M_JPG: + case M_RST0: /* no parameters */ + case M_RST1: + case M_RST2: + case M_RST3: + case M_RST4: + case M_RST5: + case M_RST6: + case M_RST7: + case M_TEM: + { + stop_decoding(JPGD_UNEXPECTED_MARKER); + break; + } + default: /* must be DNL, DHP, EXP, APPn, JPGn, COM, or RESn or APP0 */ + { + skip_variable_marker(); + break; + } + } + } + } + + // Finds the start of image (SOI) marker. + void jpeg_decoder::locate_soi_marker() + { + uint lastchar, thischar; + uint bytesleft; + + lastchar = get_bits(8); + + thischar = get_bits(8); + + /* ok if it's a normal JPEG file without a special header */ + + if ((lastchar == 0xFF) && (thischar == M_SOI)) + return; + + bytesleft = 4096; + + for (; ; ) + { + if (--bytesleft == 0) + stop_decoding(JPGD_NOT_JPEG); + + lastchar = thischar; + + thischar = get_bits(8); + + if (lastchar == 0xFF) + { + if (thischar == M_SOI) + break; + else if (thischar == M_EOI) // get_bits will keep returning M_EOI if we read past the end + stop_decoding(JPGD_NOT_JPEG); + } + } + + // Check the next character after marker: if it's not 0xFF, it can't be the start of the next marker, so the file is bad. + thischar = (m_bit_buf >> 24) & 0xFF; + + if (thischar != 0xFF) + stop_decoding(JPGD_NOT_JPEG); + } + + // Find a start of frame (SOF) marker. + void jpeg_decoder::locate_sof_marker() + { + locate_soi_marker(); + + int c = process_markers(); + + switch (c) + { + case M_SOF2: + { + m_progressive_flag = JPGD_TRUE; + read_sof_marker(); + break; + } + case M_SOF0: /* baseline DCT */ + case M_SOF1: /* extended sequential DCT */ + { + read_sof_marker(); + break; + } + case M_SOF9: /* Arithmitic coding */ + { + stop_decoding(JPGD_NO_ARITHMITIC_SUPPORT); + break; + } + default: + { + stop_decoding(JPGD_UNSUPPORTED_MARKER); + break; + } + } + } + + // Find a start of scan (SOS) marker. + int jpeg_decoder::locate_sos_marker() + { + int c; + + c = process_markers(); + + if (c == M_EOI) + return JPGD_FALSE; + else if (c != M_SOS) + stop_decoding(JPGD_UNEXPECTED_MARKER); + + read_sos_marker(); + + return JPGD_TRUE; + } + + // Reset everything to default/uninitialized state. + void jpeg_decoder::init(jpeg_decoder_stream* pStream, uint32_t flags) + { + m_flags = flags; + m_pMem_blocks = nullptr; + m_error_code = JPGD_SUCCESS; + m_ready_flag = false; + m_image_x_size = m_image_y_size = 0; + m_pStream = pStream; + m_progressive_flag = JPGD_FALSE; + + memset(m_huff_ac, 0, sizeof(m_huff_ac)); + memset(m_huff_num, 0, sizeof(m_huff_num)); + memset(m_huff_val, 0, sizeof(m_huff_val)); + memset(m_quant, 0, sizeof(m_quant)); + + m_scan_type = 0; + m_comps_in_frame = 0; + + memset(m_comp_h_samp, 0, sizeof(m_comp_h_samp)); + memset(m_comp_v_samp, 0, sizeof(m_comp_v_samp)); + memset(m_comp_quant, 0, sizeof(m_comp_quant)); + memset(m_comp_ident, 0, sizeof(m_comp_ident)); + memset(m_comp_h_blocks, 0, sizeof(m_comp_h_blocks)); + memset(m_comp_v_blocks, 0, sizeof(m_comp_v_blocks)); + + m_comps_in_scan = 0; + memset(m_comp_list, 0, sizeof(m_comp_list)); + memset(m_comp_dc_tab, 0, sizeof(m_comp_dc_tab)); + memset(m_comp_ac_tab, 0, sizeof(m_comp_ac_tab)); + + m_spectral_start = 0; + m_spectral_end = 0; + m_successive_low = 0; + m_successive_high = 0; + m_max_mcu_x_size = 0; + m_max_mcu_y_size = 0; + m_blocks_per_mcu = 0; + m_max_blocks_per_row = 0; + m_mcus_per_row = 0; + m_mcus_per_col = 0; + + memset(m_mcu_org, 0, sizeof(m_mcu_org)); + + m_total_lines_left = 0; + m_mcu_lines_left = 0; + m_num_buffered_scanlines = 0; + m_real_dest_bytes_per_scan_line = 0; + m_dest_bytes_per_scan_line = 0; + m_dest_bytes_per_pixel = 0; + + memset(m_pHuff_tabs, 0, sizeof(m_pHuff_tabs)); + + memset(m_dc_coeffs, 0, sizeof(m_dc_coeffs)); + memset(m_ac_coeffs, 0, sizeof(m_ac_coeffs)); + memset(m_block_y_mcu, 0, sizeof(m_block_y_mcu)); + + m_eob_run = 0; + + m_pIn_buf_ofs = m_in_buf; + m_in_buf_left = 0; + m_eof_flag = false; + m_tem_flag = 0; + + memset(m_in_buf_pad_start, 0, sizeof(m_in_buf_pad_start)); + memset(m_in_buf, 0, sizeof(m_in_buf)); + memset(m_in_buf_pad_end, 0, sizeof(m_in_buf_pad_end)); + + m_restart_interval = 0; + m_restarts_left = 0; + m_next_restart_num = 0; + + m_max_mcus_per_row = 0; + m_max_blocks_per_mcu = 0; + m_max_mcus_per_col = 0; + + memset(m_last_dc_val, 0, sizeof(m_last_dc_val)); + m_pMCU_coefficients = nullptr; + m_pSample_buf = nullptr; + m_pSample_buf_prev = nullptr; + m_sample_buf_prev_valid = false; + + m_total_bytes_read = 0; + + m_pScan_line_0 = nullptr; + m_pScan_line_1 = nullptr; + + // Ready the input buffer. + prep_in_buffer(); + + // Prime the bit buffer. + m_bits_left = 16; + m_bit_buf = 0; + + get_bits(16); + get_bits(16); + + for (int i = 0; i < JPGD_MAX_BLOCKS_PER_MCU; i++) + m_mcu_block_max_zag[i] = 64; + + m_has_sse2 = false; + +#if JPGD_USE_SSE2 +#ifdef _MSC_VER + int cpu_info[4]; + __cpuid(cpu_info, 1); + const int cpu_info3 = cpu_info[3]; + m_has_sse2 = ((cpu_info3 >> 26U) & 1U) != 0U; +#else + m_has_sse2 = true; +#endif +#endif + } #define SCALEBITS 16 #define ONE_HALF ((int) 1 << (SCALEBITS-1)) #define FIX(x) ((int) ((x) * (1L<> SCALEBITS; - m_cbb[i] = ( FIX(1.77200f) * k + ONE_HALF) >> SCALEBITS; - m_crg[i] = (-FIX(0.71414f)) * k; - m_cbg[i] = (-FIX(0.34414f)) * k + ONE_HALF; - } -} - -// This method throws back into the stream any bytes that where read -// into the bit buffer during initial marker scanning. -void jpeg_decoder::fix_in_buffer() -{ - // In case any 0xFF's where pulled into the buffer during marker scanning. - JPGD_ASSERT((m_bits_left & 7) == 0); - - if (m_bits_left == 16) - stuff_char( (uint8)(m_bit_buf & 0xFF)); - - if (m_bits_left >= 8) - stuff_char( (uint8)((m_bit_buf >> 8) & 0xFF)); - - stuff_char((uint8)((m_bit_buf >> 16) & 0xFF)); - stuff_char((uint8)((m_bit_buf >> 24) & 0xFF)); - - m_bits_left = 16; - get_bits_no_markers(16); - get_bits_no_markers(16); -} - -void jpeg_decoder::transform_mcu(int mcu_row) -{ - jpgd_block_t* pSrc_ptr = m_pMCU_coefficients; - uint8* pDst_ptr = m_pSample_buf + mcu_row * m_blocks_per_mcu * 64; - - for (int mcu_block = 0; mcu_block < m_blocks_per_mcu; mcu_block++) - { - idct(pSrc_ptr, pDst_ptr, m_mcu_block_max_zag[mcu_block]); - pSrc_ptr += 64; - pDst_ptr += 64; - } -} - -static const uint8 s_max_rc[64] = -{ - 17, 18, 34, 50, 50, 51, 52, 52, 52, 68, 84, 84, 84, 84, 85, 86, 86, 86, 86, 86, - 102, 118, 118, 118, 118, 118, 118, 119, 120, 120, 120, 120, 120, 120, 120, 136, - 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, - 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136 -}; - -void jpeg_decoder::transform_mcu_expand(int mcu_row) -{ - jpgd_block_t* pSrc_ptr = m_pMCU_coefficients; - uint8* pDst_ptr = m_pSample_buf + mcu_row * m_expanded_blocks_per_mcu * 64; - - // Y IDCT - int mcu_block; - for (mcu_block = 0; mcu_block < m_expanded_blocks_per_component; mcu_block++) - { - idct(pSrc_ptr, pDst_ptr, m_mcu_block_max_zag[mcu_block]); - pSrc_ptr += 64; - pDst_ptr += 64; - } - - // Chroma IDCT, with upsampling - jpgd_block_t temp_block[64]; - - for (int i = 0; i < 2; i++) - { - DCT_Upsample::Matrix44 P, Q, R, S; - - JPGD_ASSERT(m_mcu_block_max_zag[mcu_block] >= 1); - JPGD_ASSERT(m_mcu_block_max_zag[mcu_block] <= 64); - - int max_zag = m_mcu_block_max_zag[mcu_block++] - 1; - if (max_zag <= 0) max_zag = 0; // should never happen, only here to shut up static analysis - switch (s_max_rc[max_zag]) - { - case 1*16+1: - DCT_Upsample::P_Q<1, 1>::calc(P, Q, pSrc_ptr); - DCT_Upsample::R_S<1, 1>::calc(R, S, pSrc_ptr); - break; - case 1*16+2: - DCT_Upsample::P_Q<1, 2>::calc(P, Q, pSrc_ptr); - DCT_Upsample::R_S<1, 2>::calc(R, S, pSrc_ptr); - break; - case 2*16+2: - DCT_Upsample::P_Q<2, 2>::calc(P, Q, pSrc_ptr); - DCT_Upsample::R_S<2, 2>::calc(R, S, pSrc_ptr); - break; - case 3*16+2: - DCT_Upsample::P_Q<3, 2>::calc(P, Q, pSrc_ptr); - DCT_Upsample::R_S<3, 2>::calc(R, S, pSrc_ptr); - break; - case 3*16+3: - DCT_Upsample::P_Q<3, 3>::calc(P, Q, pSrc_ptr); - DCT_Upsample::R_S<3, 3>::calc(R, S, pSrc_ptr); - break; - case 3*16+4: - DCT_Upsample::P_Q<3, 4>::calc(P, Q, pSrc_ptr); - DCT_Upsample::R_S<3, 4>::calc(R, S, pSrc_ptr); - break; - case 4*16+4: - DCT_Upsample::P_Q<4, 4>::calc(P, Q, pSrc_ptr); - DCT_Upsample::R_S<4, 4>::calc(R, S, pSrc_ptr); - break; - case 5*16+4: - DCT_Upsample::P_Q<5, 4>::calc(P, Q, pSrc_ptr); - DCT_Upsample::R_S<5, 4>::calc(R, S, pSrc_ptr); - break; - case 5*16+5: - DCT_Upsample::P_Q<5, 5>::calc(P, Q, pSrc_ptr); - DCT_Upsample::R_S<5, 5>::calc(R, S, pSrc_ptr); - break; - case 5*16+6: - DCT_Upsample::P_Q<5, 6>::calc(P, Q, pSrc_ptr); - DCT_Upsample::R_S<5, 6>::calc(R, S, pSrc_ptr); - break; - case 6*16+6: - DCT_Upsample::P_Q<6, 6>::calc(P, Q, pSrc_ptr); - DCT_Upsample::R_S<6, 6>::calc(R, S, pSrc_ptr); - break; - case 7*16+6: - DCT_Upsample::P_Q<7, 6>::calc(P, Q, pSrc_ptr); - DCT_Upsample::R_S<7, 6>::calc(R, S, pSrc_ptr); - break; - case 7*16+7: - DCT_Upsample::P_Q<7, 7>::calc(P, Q, pSrc_ptr); - DCT_Upsample::R_S<7, 7>::calc(R, S, pSrc_ptr); - break; - case 7*16+8: - DCT_Upsample::P_Q<7, 8>::calc(P, Q, pSrc_ptr); - DCT_Upsample::R_S<7, 8>::calc(R, S, pSrc_ptr); - break; - case 8*16+8: - DCT_Upsample::P_Q<8, 8>::calc(P, Q, pSrc_ptr); - DCT_Upsample::R_S<8, 8>::calc(R, S, pSrc_ptr); - break; - default: - JPGD_ASSERT(false); - } - - DCT_Upsample::Matrix44 a(P + Q); P -= Q; - DCT_Upsample::Matrix44& b = P; - DCT_Upsample::Matrix44 c(R + S); R -= S; - DCT_Upsample::Matrix44& d = R; - - DCT_Upsample::Matrix44::add_and_store(temp_block, a, c); - idct_4x4(temp_block, pDst_ptr); - pDst_ptr += 64; - - DCT_Upsample::Matrix44::sub_and_store(temp_block, a, c); - idct_4x4(temp_block, pDst_ptr); - pDst_ptr += 64; - - DCT_Upsample::Matrix44::add_and_store(temp_block, b, d); - idct_4x4(temp_block, pDst_ptr); - pDst_ptr += 64; - - DCT_Upsample::Matrix44::sub_and_store(temp_block, b, d); - idct_4x4(temp_block, pDst_ptr); - pDst_ptr += 64; - - pSrc_ptr += 64; - } -} - -// Loads and dequantizes the next row of (already decoded) coefficients. -// Progressive images only. -void jpeg_decoder::load_next_row() -{ - int i; - jpgd_block_t *p; - jpgd_quant_t *q; - int mcu_row, mcu_block, row_block = 0; - int component_num, component_id; - int block_x_mcu[JPGD_MAX_COMPONENTS]; - - memset(block_x_mcu, 0, JPGD_MAX_COMPONENTS * sizeof(int)); - - for (mcu_row = 0; mcu_row < m_mcus_per_row; mcu_row++) - { - int block_x_mcu_ofs = 0, block_y_mcu_ofs = 0; - - for (mcu_block = 0; mcu_block < m_blocks_per_mcu; mcu_block++) - { - component_id = m_mcu_org[mcu_block]; - q = m_quant[m_comp_quant[component_id]]; - - p = m_pMCU_coefficients + 64 * mcu_block; - - jpgd_block_t* pAC = coeff_buf_getp(m_ac_coeffs[component_id], block_x_mcu[component_id] + block_x_mcu_ofs, m_block_y_mcu[component_id] + block_y_mcu_ofs); - jpgd_block_t* pDC = coeff_buf_getp(m_dc_coeffs[component_id], block_x_mcu[component_id] + block_x_mcu_ofs, m_block_y_mcu[component_id] + block_y_mcu_ofs); - p[0] = pDC[0]; - memcpy(&p[1], &pAC[1], 63 * sizeof(jpgd_block_t)); - - for (i = 63; i > 0; i--) - if (p[g_ZAG[i]]) - break; - - m_mcu_block_max_zag[mcu_block] = i + 1; - - for ( ; i >= 0; i--) - if (p[g_ZAG[i]]) - p[g_ZAG[i]] = static_cast(p[g_ZAG[i]] * q[i]); - - row_block++; - - if (m_comps_in_scan == 1) - block_x_mcu[component_id]++; - else - { - if (++block_x_mcu_ofs == m_comp_h_samp[component_id]) - { - block_x_mcu_ofs = 0; - - if (++block_y_mcu_ofs == m_comp_v_samp[component_id]) - { - block_y_mcu_ofs = 0; - - block_x_mcu[component_id] += m_comp_h_samp[component_id]; - } - } - } - } - - if (m_freq_domain_chroma_upsample) - transform_mcu_expand(mcu_row); - else - transform_mcu(mcu_row); - } - - if (m_comps_in_scan == 1) - m_block_y_mcu[m_comp_list[0]]++; - else - { - for (component_num = 0; component_num < m_comps_in_scan; component_num++) - { - component_id = m_comp_list[component_num]; - - m_block_y_mcu[component_id] += m_comp_v_samp[component_id]; - } - } -} - -// Restart interval processing. -void jpeg_decoder::process_restart() -{ - int i; - int c = 0; - - // Align to a byte boundry - // FIXME: Is this really necessary? get_bits_no_markers() never reads in markers! - //get_bits_no_markers(m_bits_left & 7); - - // Let's scan a little bit to find the marker, but not _too_ far. - // 1536 is a "fudge factor" that determines how much to scan. - for (i = 1536; i > 0; i--) - if (get_char() == 0xFF) - break; - - if (i == 0) - stop_decoding(JPGD_BAD_RESTART_MARKER); - - for ( ; i > 0; i--) - if ((c = get_char()) != 0xFF) - break; - - if (i == 0) - stop_decoding(JPGD_BAD_RESTART_MARKER); - - // Is it the expected marker? If not, something bad happened. - if (c != (m_next_restart_num + M_RST0)) - stop_decoding(JPGD_BAD_RESTART_MARKER); - - // Reset each component's DC prediction values. - memset(&m_last_dc_val, 0, m_comps_in_frame * sizeof(uint)); - - m_eob_run = 0; - - m_restarts_left = m_restart_interval; - - m_next_restart_num = (m_next_restart_num + 1) & 7; - - // Get the bit buffer going again... - - m_bits_left = 16; - get_bits_no_markers(16); - get_bits_no_markers(16); -} - -static inline int dequantize_ac(int c, int q) { c *= q; return c; } - -// Decodes and dequantizes the next row of coefficients. -void jpeg_decoder::decode_next_row() -{ - int row_block = 0; - - for (int mcu_row = 0; mcu_row < m_mcus_per_row; mcu_row++) - { - if ((m_restart_interval) && (m_restarts_left == 0)) - process_restart(); - - jpgd_block_t* p = m_pMCU_coefficients; - for (int mcu_block = 0; mcu_block < m_blocks_per_mcu; mcu_block++, p += 64) - { - int component_id = m_mcu_org[mcu_block]; - jpgd_quant_t* q = m_quant[m_comp_quant[component_id]]; - - int r, s; - s = huff_decode(m_pHuff_tabs[m_comp_dc_tab[component_id]], r); - s = JPGD_HUFF_EXTEND(r, s); - - m_last_dc_val[component_id] = (s += m_last_dc_val[component_id]); - - p[0] = static_cast(s * q[0]); - - int prev_num_set = m_mcu_block_max_zag[mcu_block]; - - huff_tables *pH = m_pHuff_tabs[m_comp_ac_tab[component_id]]; - - int k; - for (k = 1; k < 64; k++) - { - int extra_bits; - s = huff_decode(pH, extra_bits); - - r = s >> 4; - s &= 15; - - if (s) - { - if (r) - { - if ((k + r) > 63) - stop_decoding(JPGD_DECODE_ERROR); - - if (k < prev_num_set) - { - int n = JPGD_MIN(r, prev_num_set - k); - int kt = k; - while (n--) - p[g_ZAG[kt++]] = 0; - } - - k += r; - } - - s = JPGD_HUFF_EXTEND(extra_bits, s); - - JPGD_ASSERT(k < 64); - - p[g_ZAG[k]] = static_cast(dequantize_ac(s, q[k])); //s * q[k]; - } - else - { - if (r == 15) - { - if ((k + 16) > 64) - stop_decoding(JPGD_DECODE_ERROR); - - if (k < prev_num_set) - { - int n = JPGD_MIN(16, prev_num_set - k); - int kt = k; - while (n--) - { - JPGD_ASSERT(kt <= 63); - p[g_ZAG[kt++]] = 0; - } - } - - k += 16 - 1; // - 1 because the loop counter is k - JPGD_ASSERT(p[g_ZAG[k]] == 0); - } - else - break; - } - } - - if (k < prev_num_set) - { - int kt = k; - while (kt < prev_num_set) - p[g_ZAG[kt++]] = 0; - } - - m_mcu_block_max_zag[mcu_block] = k; - - row_block++; - } - - if (m_freq_domain_chroma_upsample) - transform_mcu_expand(mcu_row); - else - transform_mcu(mcu_row); - - m_restarts_left--; - } -} - -// YCbCr H1V1 (1x1:1:1, 3 m_blocks per MCU) to RGB -void jpeg_decoder::H1V1Convert() -{ - int row = m_max_mcu_y_size - m_mcu_lines_left; - uint8 *d = m_pScan_line_0; - uint8 *s = m_pSample_buf + row * 8; - - for (int i = m_max_mcus_per_row; i > 0; i--) - { - for (int j = 0; j < 8; j++) - { - int y = s[j]; - int cb = s[64+j]; - int cr = s[128+j]; - - d[0] = clamp(y + m_crr[cr]); - d[1] = clamp(y + ((m_crg[cr] + m_cbg[cb]) >> 16)); - d[2] = clamp(y + m_cbb[cb]); - d[3] = 255; - - d += 4; - } - - s += 64*3; - } -} - -// YCbCr H2V1 (2x1:1:1, 4 m_blocks per MCU) to RGB -void jpeg_decoder::H2V1Convert() -{ - int row = m_max_mcu_y_size - m_mcu_lines_left; - uint8 *d0 = m_pScan_line_0; - uint8 *y = m_pSample_buf + row * 8; - uint8 *c = m_pSample_buf + 2*64 + row * 8; - - for (int i = m_max_mcus_per_row; i > 0; i--) - { - for (int l = 0; l < 2; l++) - { - for (int j = 0; j < 4; j++) - { - int cb = c[0]; - int cr = c[64]; - - int rc = m_crr[cr]; - int gc = ((m_crg[cr] + m_cbg[cb]) >> 16); - int bc = m_cbb[cb]; - - int yy = y[j<<1]; - d0[0] = clamp(yy+rc); - d0[1] = clamp(yy+gc); - d0[2] = clamp(yy+bc); - d0[3] = 255; - - yy = y[(j<<1)+1]; - d0[4] = clamp(yy+rc); - d0[5] = clamp(yy+gc); - d0[6] = clamp(yy+bc); - d0[7] = 255; - - d0 += 8; - - c++; - } - y += 64; - } - - y += 64*4 - 64*2; - c += 64*4 - 8; - } -} - -// YCbCr H2V1 (1x2:1:1, 4 m_blocks per MCU) to RGB -void jpeg_decoder::H1V2Convert() -{ - int row = m_max_mcu_y_size - m_mcu_lines_left; - uint8 *d0 = m_pScan_line_0; - uint8 *d1 = m_pScan_line_1; - uint8 *y; - uint8 *c; - - if (row < 8) - y = m_pSample_buf + row * 8; - else - y = m_pSample_buf + 64*1 + (row & 7) * 8; - - c = m_pSample_buf + 64*2 + (row >> 1) * 8; - - for (int i = m_max_mcus_per_row; i > 0; i--) - { - for (int j = 0; j < 8; j++) - { - int cb = c[0+j]; - int cr = c[64+j]; - - int rc = m_crr[cr]; - int gc = ((m_crg[cr] + m_cbg[cb]) >> 16); - int bc = m_cbb[cb]; - - int yy = y[j]; - d0[0] = clamp(yy+rc); - d0[1] = clamp(yy+gc); - d0[2] = clamp(yy+bc); - d0[3] = 255; - - yy = y[8+j]; - d1[0] = clamp(yy+rc); - d1[1] = clamp(yy+gc); - d1[2] = clamp(yy+bc); - d1[3] = 255; - - d0 += 4; - d1 += 4; - } - - y += 64*4; - c += 64*4; - } -} - -// YCbCr H2V2 (2x2:1:1, 6 m_blocks per MCU) to RGB -void jpeg_decoder::H2V2Convert() -{ - int row = m_max_mcu_y_size - m_mcu_lines_left; - uint8 *d0 = m_pScan_line_0; - uint8 *d1 = m_pScan_line_1; - uint8 *y; - uint8 *c; - - if (row < 8) - y = m_pSample_buf + row * 8; - else - y = m_pSample_buf + 64*2 + (row & 7) * 8; - - c = m_pSample_buf + 64*4 + (row >> 1) * 8; - - for (int i = m_max_mcus_per_row; i > 0; i--) + // Create a few tables that allow us to quickly convert YCbCr to RGB. + void jpeg_decoder::create_look_ups() { - for (int l = 0; l < 2; l++) + for (int i = 0; i <= 255; i++) { - for (int j = 0; j < 8; j += 2) + int k = i - 128; + m_crr[i] = (FIX(1.40200f) * k + ONE_HALF) >> SCALEBITS; + m_cbb[i] = (FIX(1.77200f) * k + ONE_HALF) >> SCALEBITS; + m_crg[i] = (-FIX(0.71414f)) * k; + m_cbg[i] = (-FIX(0.34414f)) * k + ONE_HALF; + } + } + + // This method throws back into the stream any bytes that where read + // into the bit buffer during initial marker scanning. + void jpeg_decoder::fix_in_buffer() + { + // In case any 0xFF's where pulled into the buffer during marker scanning. + assert((m_bits_left & 7) == 0); + + if (m_bits_left == 16) + stuff_char((uint8)(m_bit_buf & 0xFF)); + + if (m_bits_left >= 8) + stuff_char((uint8)((m_bit_buf >> 8) & 0xFF)); + + stuff_char((uint8)((m_bit_buf >> 16) & 0xFF)); + stuff_char((uint8)((m_bit_buf >> 24) & 0xFF)); + + m_bits_left = 16; + get_bits_no_markers(16); + get_bits_no_markers(16); + } + + void jpeg_decoder::transform_mcu(int mcu_row) + { + jpgd_block_coeff_t* pSrc_ptr = m_pMCU_coefficients; + if (mcu_row * m_blocks_per_mcu >= m_max_blocks_per_row) + stop_decoding(JPGD_DECODE_ERROR); + + uint8* pDst_ptr = m_pSample_buf + mcu_row * m_blocks_per_mcu * 64; + + for (int mcu_block = 0; mcu_block < m_blocks_per_mcu; mcu_block++) + { + idct(pSrc_ptr, pDst_ptr, m_mcu_block_max_zag[mcu_block], ((m_flags & cFlagDisableSIMD) == 0) && m_has_sse2); + pSrc_ptr += 64; + pDst_ptr += 64; + } + } + + // Loads and dequantizes the next row of (already decoded) coefficients. + // Progressive images only. + void jpeg_decoder::load_next_row() + { + int i; + jpgd_block_coeff_t* p; + jpgd_quant_t* q; + int mcu_row, mcu_block, row_block = 0; + int component_num, component_id; + int block_x_mcu[JPGD_MAX_COMPONENTS]; + + memset(block_x_mcu, 0, JPGD_MAX_COMPONENTS * sizeof(int)); + + for (mcu_row = 0; mcu_row < m_mcus_per_row; mcu_row++) + { + int block_x_mcu_ofs = 0, block_y_mcu_ofs = 0; + + for (mcu_block = 0; mcu_block < m_blocks_per_mcu; mcu_block++) { - int cb = c[0]; - int cr = c[64]; + component_id = m_mcu_org[mcu_block]; + if (m_comp_quant[component_id] >= JPGD_MAX_QUANT_TABLES) + stop_decoding(JPGD_DECODE_ERROR); + + q = m_quant[m_comp_quant[component_id]]; + + p = m_pMCU_coefficients + 64 * mcu_block; + + jpgd_block_coeff_t* pAC = coeff_buf_getp(m_ac_coeffs[component_id], block_x_mcu[component_id] + block_x_mcu_ofs, m_block_y_mcu[component_id] + block_y_mcu_ofs); + jpgd_block_coeff_t* pDC = coeff_buf_getp(m_dc_coeffs[component_id], block_x_mcu[component_id] + block_x_mcu_ofs, m_block_y_mcu[component_id] + block_y_mcu_ofs); + p[0] = pDC[0]; + memcpy(&p[1], &pAC[1], 63 * sizeof(jpgd_block_coeff_t)); + + for (i = 63; i > 0; i--) + if (p[g_ZAG[i]]) + break; + + m_mcu_block_max_zag[mcu_block] = i + 1; + + for (; i >= 0; i--) + if (p[g_ZAG[i]]) + p[g_ZAG[i]] = static_cast(p[g_ZAG[i]] * q[i]); + + row_block++; + + if (m_comps_in_scan == 1) + block_x_mcu[component_id]++; + else + { + if (++block_x_mcu_ofs == m_comp_h_samp[component_id]) + { + block_x_mcu_ofs = 0; + + if (++block_y_mcu_ofs == m_comp_v_samp[component_id]) + { + block_y_mcu_ofs = 0; + + block_x_mcu[component_id] += m_comp_h_samp[component_id]; + } + } + } + } + + transform_mcu(mcu_row); + } + + if (m_comps_in_scan == 1) + m_block_y_mcu[m_comp_list[0]]++; + else + { + for (component_num = 0; component_num < m_comps_in_scan; component_num++) + { + component_id = m_comp_list[component_num]; + + m_block_y_mcu[component_id] += m_comp_v_samp[component_id]; + } + } + } + + // Restart interval processing. + void jpeg_decoder::process_restart() + { + int i; + int c = 0; + + // Align to a byte boundry + // FIXME: Is this really necessary? get_bits_no_markers() never reads in markers! + //get_bits_no_markers(m_bits_left & 7); + + // Let's scan a little bit to find the marker, but not _too_ far. + // 1536 is a "fudge factor" that determines how much to scan. + for (i = 1536; i > 0; i--) + if (get_char() == 0xFF) + break; + + if (i == 0) + stop_decoding(JPGD_BAD_RESTART_MARKER); + + for (; i > 0; i--) + if ((c = get_char()) != 0xFF) + break; + + if (i == 0) + stop_decoding(JPGD_BAD_RESTART_MARKER); + + // Is it the expected marker? If not, something bad happened. + if (c != (m_next_restart_num + M_RST0)) + stop_decoding(JPGD_BAD_RESTART_MARKER); + + // Reset each component's DC prediction values. + memset(&m_last_dc_val, 0, m_comps_in_frame * sizeof(uint)); + + m_eob_run = 0; + + m_restarts_left = m_restart_interval; + + m_next_restart_num = (m_next_restart_num + 1) & 7; + + // Get the bit buffer going again... + + m_bits_left = 16; + get_bits_no_markers(16); + get_bits_no_markers(16); + } + + static inline int dequantize_ac(int c, int q) { c *= q; return c; } + + // Decodes and dequantizes the next row of coefficients. + void jpeg_decoder::decode_next_row() + { + int row_block = 0; + + for (int mcu_row = 0; mcu_row < m_mcus_per_row; mcu_row++) + { + if ((m_restart_interval) && (m_restarts_left == 0)) + process_restart(); + + jpgd_block_coeff_t* p = m_pMCU_coefficients; + for (int mcu_block = 0; mcu_block < m_blocks_per_mcu; mcu_block++, p += 64) + { + int component_id = m_mcu_org[mcu_block]; + if (m_comp_quant[component_id] >= JPGD_MAX_QUANT_TABLES) + stop_decoding(JPGD_DECODE_ERROR); + + jpgd_quant_t* q = m_quant[m_comp_quant[component_id]]; + + int r, s; + s = huff_decode(m_pHuff_tabs[m_comp_dc_tab[component_id]], r); + if (s >= 16) + stop_decoding(JPGD_DECODE_ERROR); + + s = JPGD_HUFF_EXTEND(r, s); + + m_last_dc_val[component_id] = (s += m_last_dc_val[component_id]); + + p[0] = static_cast(s * q[0]); + + int prev_num_set = m_mcu_block_max_zag[mcu_block]; + + huff_tables* pH = m_pHuff_tabs[m_comp_ac_tab[component_id]]; + + int k; + for (k = 1; k < 64; k++) + { + int extra_bits; + s = huff_decode(pH, extra_bits); + + r = s >> 4; + s &= 15; + + if (s) + { + if (r) + { + if ((k + r) > 63) + stop_decoding(JPGD_DECODE_ERROR); + + if (k < prev_num_set) + { + int n = JPGD_MIN(r, prev_num_set - k); + int kt = k; + while (n--) + p[g_ZAG[kt++]] = 0; + } + + k += r; + } + + s = JPGD_HUFF_EXTEND(extra_bits, s); + + if (k >= 64) + stop_decoding(JPGD_DECODE_ERROR); + + p[g_ZAG[k]] = static_cast(dequantize_ac(s, q[k])); //s * q[k]; + } + else + { + if (r == 15) + { + if ((k + 16) > 64) + stop_decoding(JPGD_DECODE_ERROR); + + if (k < prev_num_set) + { + int n = JPGD_MIN(16, prev_num_set - k); + int kt = k; + while (n--) + { + if (kt > 63) + stop_decoding(JPGD_DECODE_ERROR); + p[g_ZAG[kt++]] = 0; + } + } + + k += 16 - 1; // - 1 because the loop counter is k + + if (p[g_ZAG[k & 63]] != 0) + stop_decoding(JPGD_DECODE_ERROR); + } + else + break; + } + } + + if (k < prev_num_set) + { + int kt = k; + while (kt < prev_num_set) + p[g_ZAG[kt++]] = 0; + } + + m_mcu_block_max_zag[mcu_block] = k; + + row_block++; + } + + transform_mcu(mcu_row); + + m_restarts_left--; + } + } + + // YCbCr H1V1 (1x1:1:1, 3 m_blocks per MCU) to RGB + void jpeg_decoder::H1V1Convert() + { + int row = m_max_mcu_y_size - m_mcu_lines_left; + uint8* d = m_pScan_line_0; + uint8* s = m_pSample_buf + row * 8; + + for (int i = m_max_mcus_per_row; i > 0; i--) + { + for (int j = 0; j < 8; j++) + { + int y = s[j]; + int cb = s[64 + j]; + int cr = s[128 + j]; + + d[0] = clamp(y + m_crr[cr]); + d[1] = clamp(y + ((m_crg[cr] + m_cbg[cb]) >> 16)); + d[2] = clamp(y + m_cbb[cb]); + d[3] = 255; + + d += 4; + } + + s += 64 * 3; + } + } + + // YCbCr H2V1 (2x1:1:1, 4 m_blocks per MCU) to RGB + void jpeg_decoder::H2V1Convert() + { + int row = m_max_mcu_y_size - m_mcu_lines_left; + uint8* d0 = m_pScan_line_0; + uint8* y = m_pSample_buf + row * 8; + uint8* c = m_pSample_buf + 2 * 64 + row * 8; + + for (int i = m_max_mcus_per_row; i > 0; i--) + { + for (int l = 0; l < 2; l++) + { + for (int j = 0; j < 4; j++) + { + int cb = c[0]; + int cr = c[64]; + + int rc = m_crr[cr]; + int gc = ((m_crg[cr] + m_cbg[cb]) >> 16); + int bc = m_cbb[cb]; + + int yy = y[j << 1]; + d0[0] = clamp(yy + rc); + d0[1] = clamp(yy + gc); + d0[2] = clamp(yy + bc); + d0[3] = 255; + + yy = y[(j << 1) + 1]; + d0[4] = clamp(yy + rc); + d0[5] = clamp(yy + gc); + d0[6] = clamp(yy + bc); + d0[7] = 255; + + d0 += 8; + + c++; + } + y += 64; + } + + y += 64 * 4 - 64 * 2; + c += 64 * 4 - 8; + } + } + + // YCbCr H2V1 (2x1:1:1, 4 m_blocks per MCU) to RGB + void jpeg_decoder::H2V1ConvertFiltered() + { + const uint BLOCKS_PER_MCU = 4; + int row = m_max_mcu_y_size - m_mcu_lines_left; + uint8* d0 = m_pScan_line_0; + + const int half_image_x_size = (m_image_x_size >> 1) - 1; + const int row_x8 = row * 8; + + for (int x = 0; x < m_image_x_size; x++) + { + int y = m_pSample_buf[check_sample_buf_ofs((x >> 4) * BLOCKS_PER_MCU * 64 + ((x & 8) ? 64 : 0) + (x & 7) + row_x8)]; + + int c_x0 = (x - 1) >> 1; + int c_x1 = JPGD_MIN(c_x0 + 1, half_image_x_size); + c_x0 = JPGD_MAX(c_x0, 0); + + int a = (c_x0 >> 3) * BLOCKS_PER_MCU * 64 + (c_x0 & 7) + row_x8 + 128; + int cb0 = m_pSample_buf[check_sample_buf_ofs(a)]; + int cr0 = m_pSample_buf[check_sample_buf_ofs(a + 64)]; + + int b = (c_x1 >> 3) * BLOCKS_PER_MCU * 64 + (c_x1 & 7) + row_x8 + 128; + int cb1 = m_pSample_buf[check_sample_buf_ofs(b)]; + int cr1 = m_pSample_buf[check_sample_buf_ofs(b + 64)]; + + int w0 = (x & 1) ? 3 : 1; + int w1 = (x & 1) ? 1 : 3; + + int cb = (cb0 * w0 + cb1 * w1 + 2) >> 2; + int cr = (cr0 * w0 + cr1 * w1 + 2) >> 2; + + int rc = m_crr[cr]; + int gc = ((m_crg[cr] + m_cbg[cb]) >> 16); + int bc = m_cbb[cb]; + + d0[0] = clamp(y + rc); + d0[1] = clamp(y + gc); + d0[2] = clamp(y + bc); + d0[3] = 255; + + d0 += 4; + } + } + + // YCbCr H2V1 (1x2:1:1, 4 m_blocks per MCU) to RGB + void jpeg_decoder::H1V2Convert() + { + int row = m_max_mcu_y_size - m_mcu_lines_left; + uint8* d0 = m_pScan_line_0; + uint8* d1 = m_pScan_line_1; + uint8* y; + uint8* c; + + if (row < 8) + y = m_pSample_buf + row * 8; + else + y = m_pSample_buf + 64 * 1 + (row & 7) * 8; + + c = m_pSample_buf + 64 * 2 + (row >> 1) * 8; + + for (int i = m_max_mcus_per_row; i > 0; i--) + { + for (int j = 0; j < 8; j++) + { + int cb = c[0 + j]; + int cr = c[64 + j]; int rc = m_crr[cr]; int gc = ((m_crg[cr] + m_cbg[cb]) >> 16); int bc = m_cbb[cb]; int yy = y[j]; - d0[0] = clamp(yy+rc); - d0[1] = clamp(yy+gc); - d0[2] = clamp(yy+bc); + d0[0] = clamp(yy + rc); + d0[1] = clamp(yy + gc); + d0[2] = clamp(yy + bc); d0[3] = 255; - yy = y[j+1]; - d0[4] = clamp(yy+rc); - d0[5] = clamp(yy+gc); - d0[6] = clamp(yy+bc); - d0[7] = 255; - - yy = y[j+8]; - d1[0] = clamp(yy+rc); - d1[1] = clamp(yy+gc); - d1[2] = clamp(yy+bc); + yy = y[8 + j]; + d1[0] = clamp(yy + rc); + d1[1] = clamp(yy + gc); + d1[2] = clamp(yy + bc); d1[3] = 255; - yy = y[j+8+1]; - d1[4] = clamp(yy+rc); - d1[5] = clamp(yy+gc); - d1[6] = clamp(yy+bc); - d1[7] = 255; - - d0 += 8; - d1 += 8; - - c++; + d0 += 4; + d1 += 4; } - y += 64; + + y += 64 * 4; + c += 64 * 4; + } + } + + // YCbCr H2V1 (1x2:1:1, 4 m_blocks per MCU) to RGB + void jpeg_decoder::H1V2ConvertFiltered() + { + const uint BLOCKS_PER_MCU = 4; + int y = m_image_y_size - m_total_lines_left; + int row = y & 15; + + const int half_image_y_size = (m_image_y_size >> 1) - 1; + + uint8* d0 = m_pScan_line_0; + + const int w0 = (row & 1) ? 3 : 1; + const int w1 = (row & 1) ? 1 : 3; + + int c_y0 = (y - 1) >> 1; + int c_y1 = JPGD_MIN(c_y0 + 1, half_image_y_size); + + const uint8_t* p_YSamples = m_pSample_buf; + const uint8_t* p_C0Samples = m_pSample_buf; + if ((c_y0 >= 0) && (((row & 15) == 0) || ((row & 15) == 15)) && (m_total_lines_left > 1)) + { + assert(y > 0); + assert(m_sample_buf_prev_valid); + + if ((row & 15) == 15) + p_YSamples = m_pSample_buf_prev; + + p_C0Samples = m_pSample_buf_prev; } - y += 64*6 - 64*2; - c += 64*6 - 8; + const int y_sample_base_ofs = ((row & 8) ? 64 : 0) + (row & 7) * 8; + const int y0_base = (c_y0 & 7) * 8 + 128; + const int y1_base = (c_y1 & 7) * 8 + 128; + + for (int x = 0; x < m_image_x_size; x++) + { + const int base_ofs = (x >> 3) * BLOCKS_PER_MCU * 64 + (x & 7); + + int y_sample = p_YSamples[check_sample_buf_ofs(base_ofs + y_sample_base_ofs)]; + + int a = base_ofs + y0_base; + int cb0_sample = p_C0Samples[check_sample_buf_ofs(a)]; + int cr0_sample = p_C0Samples[check_sample_buf_ofs(a + 64)]; + + int b = base_ofs + y1_base; + int cb1_sample = m_pSample_buf[check_sample_buf_ofs(b)]; + int cr1_sample = m_pSample_buf[check_sample_buf_ofs(b + 64)]; + + int cb = (cb0_sample * w0 + cb1_sample * w1 + 2) >> 2; + int cr = (cr0_sample * w0 + cr1_sample * w1 + 2) >> 2; + + int rc = m_crr[cr]; + int gc = ((m_crg[cr] + m_cbg[cb]) >> 16); + int bc = m_cbb[cb]; + + d0[0] = clamp(y_sample + rc); + d0[1] = clamp(y_sample + gc); + d0[2] = clamp(y_sample + bc); + d0[3] = 255; + + d0 += 4; + } } -} - -// Y (1 block per MCU) to 8-bit grayscale -void jpeg_decoder::gray_convert() -{ - int row = m_max_mcu_y_size - m_mcu_lines_left; - uint8 *d = m_pScan_line_0; - uint8 *s = m_pSample_buf + row * 8; - - for (int i = m_max_mcus_per_row; i > 0; i--) - { - *(uint *)d = *(uint *)s; - *(uint *)(&d[4]) = *(uint *)(&s[4]); - - s += 64; - d += 8; - } -} - -void jpeg_decoder::expanded_convert() -{ - int row = m_max_mcu_y_size - m_mcu_lines_left; - - uint8* Py = m_pSample_buf + (row / 8) * 64 * m_comp_h_samp[0] + (row & 7) * 8; - - uint8* d = m_pScan_line_0; - - for (int i = m_max_mcus_per_row; i > 0; i--) - { - for (int k = 0; k < m_max_mcu_x_size; k += 8) - { - const int Y_ofs = k * 8; - const int Cb_ofs = Y_ofs + 64 * m_expanded_blocks_per_component; - const int Cr_ofs = Y_ofs + 64 * m_expanded_blocks_per_component * 2; - for (int j = 0; j < 8; j++) - { - int y = Py[Y_ofs + j]; - int cb = Py[Cb_ofs + j]; - int cr = Py[Cr_ofs + j]; - - d[0] = clamp(y + m_crr[cr]); - d[1] = clamp(y + ((m_crg[cr] + m_cbg[cb]) >> 16)); - d[2] = clamp(y + m_cbb[cb]); - d[3] = 255; - - d += 4; - } - } - - Py += 64 * m_expanded_blocks_per_mcu; - } -} - -// Find end of image (EOI) marker, so we can return to the user the exact size of the input stream. -void jpeg_decoder::find_eoi() -{ - if (!m_progressive_flag) - { - // Attempt to read the EOI marker. - //get_bits_no_markers(m_bits_left & 7); - - // Prime the bit buffer - m_bits_left = 16; - get_bits(16); - get_bits(16); - - // The next marker _should_ be EOI - process_markers(); - } - - m_total_bytes_read -= m_in_buf_left; -} - -int jpeg_decoder::decode(const void** pScan_line, uint* pScan_line_len) -{ - if ((m_error_code) || (!m_ready_flag)) - return JPGD_FAILED; - - if (m_total_lines_left == 0) - return JPGD_DONE; - - if (m_mcu_lines_left == 0) - { - if (setjmp(m_jmp_state)) - return JPGD_FAILED; - - if (m_progressive_flag) - load_next_row(); - else - decode_next_row(); - - // Find the EOI marker if that was the last row. - if (m_total_lines_left <= m_max_mcu_y_size) - find_eoi(); - - m_mcu_lines_left = m_max_mcu_y_size; - } - - if (m_freq_domain_chroma_upsample) - { - expanded_convert(); - *pScan_line = m_pScan_line_0; - } - else - { - switch (m_scan_type) - { - case JPGD_YH2V2: - { - if ((m_mcu_lines_left & 1) == 0) - { - H2V2Convert(); - *pScan_line = m_pScan_line_0; - } - else - *pScan_line = m_pScan_line_1; - - break; - } - case JPGD_YH2V1: - { - H2V1Convert(); - *pScan_line = m_pScan_line_0; - break; - } - case JPGD_YH1V2: - { - if ((m_mcu_lines_left & 1) == 0) - { - H1V2Convert(); - *pScan_line = m_pScan_line_0; - } - else - *pScan_line = m_pScan_line_1; - - break; - } - case JPGD_YH1V1: - { - H1V1Convert(); - *pScan_line = m_pScan_line_0; - break; - } - case JPGD_GRAYSCALE: - { - gray_convert(); - *pScan_line = m_pScan_line_0; - - break; - } - } - } - - *pScan_line_len = m_real_dest_bytes_per_scan_line; - - m_mcu_lines_left--; - m_total_lines_left--; - - return JPGD_SUCCESS; -} - -// Creates the tables needed for efficient Huffman decoding. -void jpeg_decoder::make_huff_table(int index, huff_tables *pH) -{ - int p, i, l, si; - uint8 huffsize[257]; - uint huffcode[257]; - uint code; - uint subtree; - int code_size; - int lastp; - int nextfreeentry; - int currententry; - - pH->ac_table = m_huff_ac[index] != 0; - - p = 0; - - for (l = 1; l <= 16; l++) - { - for (i = 1; i <= m_huff_num[index][l]; i++) - huffsize[p++] = static_cast(l); - } - - huffsize[p] = 0; - - lastp = p; - - code = 0; - si = huffsize[0]; - p = 0; - - while (huffsize[p]) - { - while (huffsize[p] == si) - { - huffcode[p++] = code; - code++; - } - - code <<= 1; - si++; - } - - memset(pH->look_up, 0, sizeof(pH->look_up)); - memset(pH->look_up2, 0, sizeof(pH->look_up2)); - memset(pH->tree, 0, sizeof(pH->tree)); - memset(pH->code_size, 0, sizeof(pH->code_size)); - - nextfreeentry = -1; - - p = 0; - - while (p < lastp) - { - i = m_huff_val[index][p]; - code = huffcode[p]; - code_size = huffsize[p]; - - pH->code_size[i] = static_cast(code_size); - - if (code_size <= 8) - { - code <<= (8 - code_size); - - for (l = 1 << (8 - code_size); l > 0; l--) - { - JPGD_ASSERT(i < 256); - - pH->look_up[code] = i; - - bool has_extrabits = false; - int extra_bits = 0; - int num_extra_bits = i & 15; - - int bits_to_fetch = code_size; - if (num_extra_bits) - { - int total_codesize = code_size + num_extra_bits; - if (total_codesize <= 8) - { - has_extrabits = true; - extra_bits = ((1 << num_extra_bits) - 1) & (code >> (8 - total_codesize)); - JPGD_ASSERT(extra_bits <= 0x7FFF); - bits_to_fetch += num_extra_bits; - } - } - - if (!has_extrabits) - pH->look_up2[code] = i | (bits_to_fetch << 8); - else - pH->look_up2[code] = i | 0x8000 | (extra_bits << 16) | (bits_to_fetch << 8); - - code++; - } - } - else - { - subtree = (code >> (code_size - 8)) & 0xFF; - - currententry = pH->look_up[subtree]; - - if (currententry == 0) - { - pH->look_up[subtree] = currententry = nextfreeentry; - pH->look_up2[subtree] = currententry = nextfreeentry; - - nextfreeentry -= 2; - } - - code <<= (16 - (code_size - 8)); - - for (l = code_size; l > 9; l--) - { - if ((code & 0x8000) == 0) - currententry--; - - if (pH->tree[-currententry - 1] == 0) - { - pH->tree[-currententry - 1] = nextfreeentry; - - currententry = nextfreeentry; - - nextfreeentry -= 2; - } - else - currententry = pH->tree[-currententry - 1]; - - code <<= 1; - } - - if ((code & 0x8000) == 0) - currententry--; - - pH->tree[-currententry - 1] = i; - } - - p++; - } -} - -// Verifies the quantization tables needed for this scan are available. -void jpeg_decoder::check_quant_tables() -{ - for (int i = 0; i < m_comps_in_scan; i++) - if (m_quant[m_comp_quant[m_comp_list[i]]] == NULL) - stop_decoding(JPGD_UNDEFINED_QUANT_TABLE); -} - -// Verifies that all the Huffman tables needed for this scan are available. -void jpeg_decoder::check_huff_tables() -{ - for (int i = 0; i < m_comps_in_scan; i++) - { - if ((m_spectral_start == 0) && (m_huff_num[m_comp_dc_tab[m_comp_list[i]]] == NULL)) - stop_decoding(JPGD_UNDEFINED_HUFF_TABLE); - - if ((m_spectral_end > 0) && (m_huff_num[m_comp_ac_tab[m_comp_list[i]]] == NULL)) - stop_decoding(JPGD_UNDEFINED_HUFF_TABLE); - } - - for (int i = 0; i < JPGD_MAX_HUFF_TABLES; i++) - if (m_huff_num[i]) - { - if (!m_pHuff_tabs[i]) - m_pHuff_tabs[i] = (huff_tables *)alloc(sizeof(huff_tables)); - - make_huff_table(i, m_pHuff_tabs[i]); - } -} - -// Determines the component order inside each MCU. -// Also calcs how many MCU's are on each row, etc. -void jpeg_decoder::calc_mcu_block_order() -{ - int component_num, component_id; - int max_h_samp = 0, max_v_samp = 0; - - for (component_id = 0; component_id < m_comps_in_frame; component_id++) - { - if (m_comp_h_samp[component_id] > max_h_samp) - max_h_samp = m_comp_h_samp[component_id]; - - if (m_comp_v_samp[component_id] > max_v_samp) - max_v_samp = m_comp_v_samp[component_id]; - } - - for (component_id = 0; component_id < m_comps_in_frame; component_id++) - { - m_comp_h_blocks[component_id] = ((((m_image_x_size * m_comp_h_samp[component_id]) + (max_h_samp - 1)) / max_h_samp) + 7) / 8; - m_comp_v_blocks[component_id] = ((((m_image_y_size * m_comp_v_samp[component_id]) + (max_v_samp - 1)) / max_v_samp) + 7) / 8; - } - - if (m_comps_in_scan == 1) - { - m_mcus_per_row = m_comp_h_blocks[m_comp_list[0]]; - m_mcus_per_col = m_comp_v_blocks[m_comp_list[0]]; - } - else - { - m_mcus_per_row = (((m_image_x_size + 7) / 8) + (max_h_samp - 1)) / max_h_samp; - m_mcus_per_col = (((m_image_y_size + 7) / 8) + (max_v_samp - 1)) / max_v_samp; - } - - if (m_comps_in_scan == 1) - { - m_mcu_org[0] = m_comp_list[0]; - - m_blocks_per_mcu = 1; - } - else - { - m_blocks_per_mcu = 0; - - for (component_num = 0; component_num < m_comps_in_scan; component_num++) - { - int num_blocks; - - component_id = m_comp_list[component_num]; - - num_blocks = m_comp_h_samp[component_id] * m_comp_v_samp[component_id]; - - while (num_blocks--) - m_mcu_org[m_blocks_per_mcu++] = component_id; - } - } -} - -// Starts a new scan. -int jpeg_decoder::init_scan() -{ - if (!locate_sos_marker()) - return JPGD_FALSE; - - calc_mcu_block_order(); - - check_huff_tables(); - - check_quant_tables(); - - memset(m_last_dc_val, 0, m_comps_in_frame * sizeof(uint)); - - m_eob_run = 0; - - if (m_restart_interval) - { - m_restarts_left = m_restart_interval; - m_next_restart_num = 0; - } - - fix_in_buffer(); - - return JPGD_TRUE; -} - -// Starts a frame. Determines if the number of components or sampling factors -// are supported. -void jpeg_decoder::init_frame() -{ - int i; - - if (m_comps_in_frame == 1) - { - if ((m_comp_h_samp[0] != 1) || (m_comp_v_samp[0] != 1)) - stop_decoding(JPGD_UNSUPPORTED_SAMP_FACTORS); - - m_scan_type = JPGD_GRAYSCALE; - m_max_blocks_per_mcu = 1; - m_max_mcu_x_size = 8; - m_max_mcu_y_size = 8; - } - else if (m_comps_in_frame == 3) - { - if ( ((m_comp_h_samp[1] != 1) || (m_comp_v_samp[1] != 1)) || - ((m_comp_h_samp[2] != 1) || (m_comp_v_samp[2] != 1)) ) - stop_decoding(JPGD_UNSUPPORTED_SAMP_FACTORS); - - if ((m_comp_h_samp[0] == 1) && (m_comp_v_samp[0] == 1)) - { - m_scan_type = JPGD_YH1V1; - - m_max_blocks_per_mcu = 3; - m_max_mcu_x_size = 8; - m_max_mcu_y_size = 8; - } - else if ((m_comp_h_samp[0] == 2) && (m_comp_v_samp[0] == 1)) - { - m_scan_type = JPGD_YH2V1; - m_max_blocks_per_mcu = 4; - m_max_mcu_x_size = 16; - m_max_mcu_y_size = 8; - } - else if ((m_comp_h_samp[0] == 1) && (m_comp_v_samp[0] == 2)) - { - m_scan_type = JPGD_YH1V2; - m_max_blocks_per_mcu = 4; - m_max_mcu_x_size = 8; - m_max_mcu_y_size = 16; - } - else if ((m_comp_h_samp[0] == 2) && (m_comp_v_samp[0] == 2)) - { - m_scan_type = JPGD_YH2V2; - m_max_blocks_per_mcu = 6; - m_max_mcu_x_size = 16; - m_max_mcu_y_size = 16; - } - else - stop_decoding(JPGD_UNSUPPORTED_SAMP_FACTORS); - } - else - stop_decoding(JPGD_UNSUPPORTED_COLORSPACE); - - m_max_mcus_per_row = (m_image_x_size + (m_max_mcu_x_size - 1)) / m_max_mcu_x_size; - m_max_mcus_per_col = (m_image_y_size + (m_max_mcu_y_size - 1)) / m_max_mcu_y_size; - - // These values are for the *destination* pixels: after conversion. - if (m_scan_type == JPGD_GRAYSCALE) - m_dest_bytes_per_pixel = 1; - else - m_dest_bytes_per_pixel = 4; - - m_dest_bytes_per_scan_line = ((m_image_x_size + 15) & 0xFFF0) * m_dest_bytes_per_pixel; - - m_real_dest_bytes_per_scan_line = (m_image_x_size * m_dest_bytes_per_pixel); - - // Initialize two scan line buffers. - m_pScan_line_0 = (uint8 *)alloc(m_dest_bytes_per_scan_line, true); - if ((m_scan_type == JPGD_YH1V2) || (m_scan_type == JPGD_YH2V2)) - m_pScan_line_1 = (uint8 *)alloc(m_dest_bytes_per_scan_line, true); - - m_max_blocks_per_row = m_max_mcus_per_row * m_max_blocks_per_mcu; - - // Should never happen - if (m_max_blocks_per_row > JPGD_MAX_BLOCKS_PER_ROW) - stop_decoding(JPGD_ASSERTION_ERROR); - - // Allocate the coefficient buffer, enough for one MCU - m_pMCU_coefficients = (jpgd_block_t*)alloc(m_max_blocks_per_mcu * 64 * sizeof(jpgd_block_t)); - - for (i = 0; i < m_max_blocks_per_mcu; i++) - m_mcu_block_max_zag[i] = 64; - - m_expanded_blocks_per_component = m_comp_h_samp[0] * m_comp_v_samp[0]; - m_expanded_blocks_per_mcu = m_expanded_blocks_per_component * m_comps_in_frame; - m_expanded_blocks_per_row = m_max_mcus_per_row * m_expanded_blocks_per_mcu; - // Freq. domain chroma upsampling is only supported for H2V2 subsampling factor (the most common one I've seen). - m_freq_domain_chroma_upsample = false; -#if JPGD_SUPPORT_FREQ_DOMAIN_UPSAMPLING - m_freq_domain_chroma_upsample = (m_expanded_blocks_per_mcu == 4*3); -#endif - - if (m_freq_domain_chroma_upsample) - m_pSample_buf = (uint8 *)alloc(m_expanded_blocks_per_row * 64); - else - m_pSample_buf = (uint8 *)alloc(m_max_blocks_per_row * 64); - - m_total_lines_left = m_image_y_size; - - m_mcu_lines_left = 0; - - create_look_ups(); -} - -// The coeff_buf series of methods originally stored the coefficients -// into a "virtual" file which was located in EMS, XMS, or a disk file. A cache -// was used to make this process more efficient. Now, we can store the entire -// thing in RAM. -jpeg_decoder::coeff_buf* jpeg_decoder::coeff_buf_open(int block_num_x, int block_num_y, int block_len_x, int block_len_y) -{ - coeff_buf* cb = (coeff_buf*)alloc(sizeof(coeff_buf)); - - cb->block_num_x = block_num_x; - cb->block_num_y = block_num_y; - cb->block_len_x = block_len_x; - cb->block_len_y = block_len_y; - cb->block_size = (block_len_x * block_len_y) * sizeof(jpgd_block_t); - cb->pData = (uint8 *)alloc(cb->block_size * block_num_x * block_num_y, true); - return cb; -} - -inline jpgd_block_t *jpeg_decoder::coeff_buf_getp(coeff_buf *cb, int block_x, int block_y) -{ - JPGD_ASSERT((block_x < cb->block_num_x) && (block_y < cb->block_num_y)); - return (jpgd_block_t *)(cb->pData + block_x * cb->block_size + block_y * (cb->block_size * cb->block_num_x)); -} - -// The following methods decode the various types of m_blocks encountered -// in progressively encoded images. -void jpeg_decoder::decode_block_dc_first(jpeg_decoder *pD, int component_id, int block_x, int block_y) -{ - int s, r; - jpgd_block_t *p = pD->coeff_buf_getp(pD->m_dc_coeffs[component_id], block_x, block_y); - - if ((s = pD->huff_decode(pD->m_pHuff_tabs[pD->m_comp_dc_tab[component_id]])) != 0) - { - r = pD->get_bits_no_markers(s); - s = JPGD_HUFF_EXTEND(r, s); - } - - pD->m_last_dc_val[component_id] = (s += pD->m_last_dc_val[component_id]); - - p[0] = static_cast(s << pD->m_successive_low); -} - -void jpeg_decoder::decode_block_dc_refine(jpeg_decoder *pD, int component_id, int block_x, int block_y) -{ - if (pD->get_bits_no_markers(1)) - { - jpgd_block_t *p = pD->coeff_buf_getp(pD->m_dc_coeffs[component_id], block_x, block_y); - - p[0] |= (1 << pD->m_successive_low); - } -} - -void jpeg_decoder::decode_block_ac_first(jpeg_decoder *pD, int component_id, int block_x, int block_y) -{ - int k, s, r; - - if (pD->m_eob_run) - { - pD->m_eob_run--; - return; - } - - jpgd_block_t *p = pD->coeff_buf_getp(pD->m_ac_coeffs[component_id], block_x, block_y); - - for (k = pD->m_spectral_start; k <= pD->m_spectral_end; k++) - { - s = pD->huff_decode(pD->m_pHuff_tabs[pD->m_comp_ac_tab[component_id]]); - - r = s >> 4; - s &= 15; - - if (s) - { - if ((k += r) > 63) - pD->stop_decoding(JPGD_DECODE_ERROR); - - r = pD->get_bits_no_markers(s); - s = JPGD_HUFF_EXTEND(r, s); - - p[g_ZAG[k]] = static_cast(s << pD->m_successive_low); - } - else - { - if (r == 15) - { - if ((k += 15) > 63) - pD->stop_decoding(JPGD_DECODE_ERROR); - } - else - { - pD->m_eob_run = 1 << r; - - if (r) - pD->m_eob_run += pD->get_bits_no_markers(r); - - pD->m_eob_run--; - - break; - } - } - } -} - -void jpeg_decoder::decode_block_ac_refine(jpeg_decoder *pD, int component_id, int block_x, int block_y) -{ - int s, k, r; - int p1 = 1 << pD->m_successive_low; - int m1 = (-1) << pD->m_successive_low; - jpgd_block_t *p = pD->coeff_buf_getp(pD->m_ac_coeffs[component_id], block_x, block_y); - - JPGD_ASSERT(pD->m_spectral_end <= 63); - - k = pD->m_spectral_start; - - if (pD->m_eob_run == 0) - { - for ( ; k <= pD->m_spectral_end; k++) - { - s = pD->huff_decode(pD->m_pHuff_tabs[pD->m_comp_ac_tab[component_id]]); - - r = s >> 4; - s &= 15; - - if (s) - { - if (s != 1) - pD->stop_decoding(JPGD_DECODE_ERROR); - - if (pD->get_bits_no_markers(1)) - s = p1; - else - s = m1; - } - else - { - if (r != 15) - { - pD->m_eob_run = 1 << r; - - if (r) - pD->m_eob_run += pD->get_bits_no_markers(r); - - break; - } - } - - do - { - jpgd_block_t *this_coef = p + g_ZAG[k & 63]; - - if (*this_coef != 0) - { - if (pD->get_bits_no_markers(1)) - { - if ((*this_coef & p1) == 0) - { - if (*this_coef >= 0) - *this_coef = static_cast(*this_coef + p1); - else - *this_coef = static_cast(*this_coef + m1); - } - } - } - else - { - if (--r < 0) - break; - } - - k++; - - } while (k <= pD->m_spectral_end); - - if ((s) && (k < 64)) - { - p[g_ZAG[k]] = static_cast(s); - } - } - } - - if (pD->m_eob_run > 0) - { - for ( ; k <= pD->m_spectral_end; k++) - { - jpgd_block_t *this_coef = p + g_ZAG[k & 63]; // logical AND to shut up static code analysis - - if (*this_coef != 0) - { - if (pD->get_bits_no_markers(1)) - { - if ((*this_coef & p1) == 0) - { - if (*this_coef >= 0) - *this_coef = static_cast(*this_coef + p1); - else - *this_coef = static_cast(*this_coef + m1); - } - } - } - } - - pD->m_eob_run--; - } -} - -// Decode a scan in a progressively encoded image. -void jpeg_decoder::decode_scan(pDecode_block_func decode_block_func) -{ - int mcu_row, mcu_col, mcu_block; - int block_x_mcu[JPGD_MAX_COMPONENTS], m_block_y_mcu[JPGD_MAX_COMPONENTS]; - - memset(m_block_y_mcu, 0, sizeof(m_block_y_mcu)); - - for (mcu_col = 0; mcu_col < m_mcus_per_col; mcu_col++) - { - int component_num, component_id; - - memset(block_x_mcu, 0, sizeof(block_x_mcu)); - - for (mcu_row = 0; mcu_row < m_mcus_per_row; mcu_row++) - { - int block_x_mcu_ofs = 0, block_y_mcu_ofs = 0; - - if ((m_restart_interval) && (m_restarts_left == 0)) - process_restart(); - - for (mcu_block = 0; mcu_block < m_blocks_per_mcu; mcu_block++) - { - component_id = m_mcu_org[mcu_block]; - - decode_block_func(this, component_id, block_x_mcu[component_id] + block_x_mcu_ofs, m_block_y_mcu[component_id] + block_y_mcu_ofs); - - if (m_comps_in_scan == 1) - block_x_mcu[component_id]++; - else - { - if (++block_x_mcu_ofs == m_comp_h_samp[component_id]) - { - block_x_mcu_ofs = 0; - - if (++block_y_mcu_ofs == m_comp_v_samp[component_id]) - { - block_y_mcu_ofs = 0; - block_x_mcu[component_id] += m_comp_h_samp[component_id]; - } - } - } - } - - m_restarts_left--; - } - - if (m_comps_in_scan == 1) - m_block_y_mcu[m_comp_list[0]]++; - else - { - for (component_num = 0; component_num < m_comps_in_scan; component_num++) - { - component_id = m_comp_list[component_num]; - m_block_y_mcu[component_id] += m_comp_v_samp[component_id]; - } - } - } -} - -// Decode a progressively encoded image. -void jpeg_decoder::init_progressive() -{ - int i; - - if (m_comps_in_frame == 4) - stop_decoding(JPGD_UNSUPPORTED_COLORSPACE); - - // Allocate the coefficient buffers. - for (i = 0; i < m_comps_in_frame; i++) - { - m_dc_coeffs[i] = coeff_buf_open(m_max_mcus_per_row * m_comp_h_samp[i], m_max_mcus_per_col * m_comp_v_samp[i], 1, 1); - m_ac_coeffs[i] = coeff_buf_open(m_max_mcus_per_row * m_comp_h_samp[i], m_max_mcus_per_col * m_comp_v_samp[i], 8, 8); - } - - for ( ; ; ) - { - int dc_only_scan, refinement_scan; - pDecode_block_func decode_block_func; - - if (!init_scan()) - break; - - dc_only_scan = (m_spectral_start == 0); - refinement_scan = (m_successive_high != 0); - - if ((m_spectral_start > m_spectral_end) || (m_spectral_end > 63)) - stop_decoding(JPGD_BAD_SOS_SPECTRAL); - - if (dc_only_scan) - { - if (m_spectral_end) - stop_decoding(JPGD_BAD_SOS_SPECTRAL); - } - else if (m_comps_in_scan != 1) /* AC scans can only contain one component */ - stop_decoding(JPGD_BAD_SOS_SPECTRAL); - - if ((refinement_scan) && (m_successive_low != m_successive_high - 1)) - stop_decoding(JPGD_BAD_SOS_SUCCESSIVE); - - if (dc_only_scan) - { - if (refinement_scan) - decode_block_func = decode_block_dc_refine; - else - decode_block_func = decode_block_dc_first; - } - else - { - if (refinement_scan) - decode_block_func = decode_block_ac_refine; - else - decode_block_func = decode_block_ac_first; - } - - decode_scan(decode_block_func); - - m_bits_left = 16; - get_bits(16); - get_bits(16); - } - - m_comps_in_scan = m_comps_in_frame; - - for (i = 0; i < m_comps_in_frame; i++) - m_comp_list[i] = i; - - calc_mcu_block_order(); -} - -void jpeg_decoder::init_sequential() -{ - if (!init_scan()) - stop_decoding(JPGD_UNEXPECTED_MARKER); -} - -void jpeg_decoder::decode_start() -{ - init_frame(); - - if (m_progressive_flag) - init_progressive(); - else - init_sequential(); -} - -void jpeg_decoder::decode_init(jpeg_decoder_stream *pStream) -{ - init(pStream); - locate_sof_marker(); -} - -jpeg_decoder::jpeg_decoder(jpeg_decoder_stream *pStream) -{ - if (setjmp(m_jmp_state)) - return; - decode_init(pStream); -} - -int jpeg_decoder::begin_decoding() -{ - if (m_ready_flag) - return JPGD_SUCCESS; - - if (m_error_code) - return JPGD_FAILED; - - if (setjmp(m_jmp_state)) - return JPGD_FAILED; - - decode_start(); - - m_ready_flag = true; - - return JPGD_SUCCESS; -} - -jpeg_decoder::~jpeg_decoder() -{ - free_all_blocks(); -} - -jpeg_decoder_file_stream::jpeg_decoder_file_stream() -{ - m_pFile = NULL; - m_eof_flag = false; - m_error_flag = false; -} - -void jpeg_decoder_file_stream::close() -{ - if (m_pFile) - { - fclose(m_pFile); - m_pFile = NULL; - } - - m_eof_flag = false; - m_error_flag = false; -} - -jpeg_decoder_file_stream::~jpeg_decoder_file_stream() -{ - close(); -} - -bool jpeg_decoder_file_stream::open(const char *Pfilename) -{ - close(); - - m_eof_flag = false; - m_error_flag = false; + + // YCbCr H2V2 (2x2:1:1, 6 m_blocks per MCU) to RGB + void jpeg_decoder::H2V2Convert() + { + int row = m_max_mcu_y_size - m_mcu_lines_left; + uint8* d0 = m_pScan_line_0; + uint8* d1 = m_pScan_line_1; + uint8* y; + uint8* c; + + if (row < 8) + y = m_pSample_buf + row * 8; + else + y = m_pSample_buf + 64 * 2 + (row & 7) * 8; + + c = m_pSample_buf + 64 * 4 + (row >> 1) * 8; + + for (int i = m_max_mcus_per_row; i > 0; i--) + { + for (int l = 0; l < 2; l++) + { + for (int j = 0; j < 8; j += 2) + { + int cb = c[0]; + int cr = c[64]; + + int rc = m_crr[cr]; + int gc = ((m_crg[cr] + m_cbg[cb]) >> 16); + int bc = m_cbb[cb]; + + int yy = y[j]; + d0[0] = clamp(yy + rc); + d0[1] = clamp(yy + gc); + d0[2] = clamp(yy + bc); + d0[3] = 255; + + yy = y[j + 1]; + d0[4] = clamp(yy + rc); + d0[5] = clamp(yy + gc); + d0[6] = clamp(yy + bc); + d0[7] = 255; + + yy = y[j + 8]; + d1[0] = clamp(yy + rc); + d1[1] = clamp(yy + gc); + d1[2] = clamp(yy + bc); + d1[3] = 255; + + yy = y[j + 8 + 1]; + d1[4] = clamp(yy + rc); + d1[5] = clamp(yy + gc); + d1[6] = clamp(yy + bc); + d1[7] = 255; + + d0 += 8; + d1 += 8; + + c++; + } + y += 64; + } + + y += 64 * 6 - 64 * 2; + c += 64 * 6 - 8; + } + } + + uint32_t jpeg_decoder::H2V2ConvertFiltered() + { + const uint BLOCKS_PER_MCU = 6; + int y = m_image_y_size - m_total_lines_left; + int row = y & 15; + + const int half_image_y_size = (m_image_y_size >> 1) - 1; + + uint8* d0 = m_pScan_line_0; + + int c_y0 = (y - 1) >> 1; + int c_y1 = JPGD_MIN(c_y0 + 1, half_image_y_size); + + const uint8_t* p_YSamples = m_pSample_buf; + const uint8_t* p_C0Samples = m_pSample_buf; + if ((c_y0 >= 0) && (((row & 15) == 0) || ((row & 15) == 15)) && (m_total_lines_left > 1)) + { + assert(y > 0); + assert(m_sample_buf_prev_valid); + + if ((row & 15) == 15) + p_YSamples = m_pSample_buf_prev; + + p_C0Samples = m_pSample_buf_prev; + } + + const int y_sample_base_ofs = ((row & 8) ? 128 : 0) + (row & 7) * 8; + const int y0_base = (c_y0 & 7) * 8 + 256; + const int y1_base = (c_y1 & 7) * 8 + 256; + + const int half_image_x_size = (m_image_x_size >> 1) - 1; + + static const uint8_t s_muls[2][2][4] = + { + { { 1, 3, 3, 9 }, { 3, 9, 1, 3 }, }, + { { 3, 1, 9, 3 }, { 9, 3, 3, 1 } } + }; + + if (((row & 15) >= 1) && ((row & 15) <= 14)) + { + assert((row & 1) == 1); + assert(((y + 1 - 1) >> 1) == c_y0); + + assert(p_YSamples == m_pSample_buf); + assert(p_C0Samples == m_pSample_buf); + + uint8* d1 = m_pScan_line_1; + const int y_sample_base_ofs1 = (((row + 1) & 8) ? 128 : 0) + ((row + 1) & 7) * 8; + + for (int x = 0; x < m_image_x_size; x++) + { + int k = (x >> 4) * BLOCKS_PER_MCU * 64 + ((x & 8) ? 64 : 0) + (x & 7); + int y_sample0 = p_YSamples[check_sample_buf_ofs(k + y_sample_base_ofs)]; + int y_sample1 = p_YSamples[check_sample_buf_ofs(k + y_sample_base_ofs1)]; + + int c_x0 = (x - 1) >> 1; + int c_x1 = JPGD_MIN(c_x0 + 1, half_image_x_size); + c_x0 = JPGD_MAX(c_x0, 0); + + int a = (c_x0 >> 3) * BLOCKS_PER_MCU * 64 + (c_x0 & 7); + int cb00_sample = p_C0Samples[check_sample_buf_ofs(a + y0_base)]; + int cr00_sample = p_C0Samples[check_sample_buf_ofs(a + y0_base + 64)]; + + int cb01_sample = m_pSample_buf[check_sample_buf_ofs(a + y1_base)]; + int cr01_sample = m_pSample_buf[check_sample_buf_ofs(a + y1_base + 64)]; + + int b = (c_x1 >> 3) * BLOCKS_PER_MCU * 64 + (c_x1 & 7); + int cb10_sample = p_C0Samples[check_sample_buf_ofs(b + y0_base)]; + int cr10_sample = p_C0Samples[check_sample_buf_ofs(b + y0_base + 64)]; + + int cb11_sample = m_pSample_buf[check_sample_buf_ofs(b + y1_base)]; + int cr11_sample = m_pSample_buf[check_sample_buf_ofs(b + y1_base + 64)]; + + { + const uint8_t* pMuls = &s_muls[row & 1][x & 1][0]; + int cb = (cb00_sample * pMuls[0] + cb01_sample * pMuls[1] + cb10_sample * pMuls[2] + cb11_sample * pMuls[3] + 8) >> 4; + int cr = (cr00_sample * pMuls[0] + cr01_sample * pMuls[1] + cr10_sample * pMuls[2] + cr11_sample * pMuls[3] + 8) >> 4; + + int rc = m_crr[cr]; + int gc = ((m_crg[cr] + m_cbg[cb]) >> 16); + int bc = m_cbb[cb]; + + d0[0] = clamp(y_sample0 + rc); + d0[1] = clamp(y_sample0 + gc); + d0[2] = clamp(y_sample0 + bc); + d0[3] = 255; + + d0 += 4; + } + + { + const uint8_t* pMuls = &s_muls[(row + 1) & 1][x & 1][0]; + int cb = (cb00_sample * pMuls[0] + cb01_sample * pMuls[1] + cb10_sample * pMuls[2] + cb11_sample * pMuls[3] + 8) >> 4; + int cr = (cr00_sample * pMuls[0] + cr01_sample * pMuls[1] + cr10_sample * pMuls[2] + cr11_sample * pMuls[3] + 8) >> 4; + + int rc = m_crr[cr]; + int gc = ((m_crg[cr] + m_cbg[cb]) >> 16); + int bc = m_cbb[cb]; + + d1[0] = clamp(y_sample1 + rc); + d1[1] = clamp(y_sample1 + gc); + d1[2] = clamp(y_sample1 + bc); + d1[3] = 255; + + d1 += 4; + } + + if (((x & 1) == 1) && (x < m_image_x_size - 1)) + { + const int nx = x + 1; + assert(c_x0 == (nx - 1) >> 1); + + k = (nx >> 4) * BLOCKS_PER_MCU * 64 + ((nx & 8) ? 64 : 0) + (nx & 7); + y_sample0 = p_YSamples[check_sample_buf_ofs(k + y_sample_base_ofs)]; + y_sample1 = p_YSamples[check_sample_buf_ofs(k + y_sample_base_ofs1)]; + + { + const uint8_t* pMuls = &s_muls[row & 1][nx & 1][0]; + int cb = (cb00_sample * pMuls[0] + cb01_sample * pMuls[1] + cb10_sample * pMuls[2] + cb11_sample * pMuls[3] + 8) >> 4; + int cr = (cr00_sample * pMuls[0] + cr01_sample * pMuls[1] + cr10_sample * pMuls[2] + cr11_sample * pMuls[3] + 8) >> 4; + + int rc = m_crr[cr]; + int gc = ((m_crg[cr] + m_cbg[cb]) >> 16); + int bc = m_cbb[cb]; + + d0[0] = clamp(y_sample0 + rc); + d0[1] = clamp(y_sample0 + gc); + d0[2] = clamp(y_sample0 + bc); + d0[3] = 255; + + d0 += 4; + } + + { + const uint8_t* pMuls = &s_muls[(row + 1) & 1][nx & 1][0]; + int cb = (cb00_sample * pMuls[0] + cb01_sample * pMuls[1] + cb10_sample * pMuls[2] + cb11_sample * pMuls[3] + 8) >> 4; + int cr = (cr00_sample * pMuls[0] + cr01_sample * pMuls[1] + cr10_sample * pMuls[2] + cr11_sample * pMuls[3] + 8) >> 4; + + int rc = m_crr[cr]; + int gc = ((m_crg[cr] + m_cbg[cb]) >> 16); + int bc = m_cbb[cb]; + + d1[0] = clamp(y_sample1 + rc); + d1[1] = clamp(y_sample1 + gc); + d1[2] = clamp(y_sample1 + bc); + d1[3] = 255; + + d1 += 4; + } + + ++x; + } + } + + return 2; + } + else + { + for (int x = 0; x < m_image_x_size; x++) + { + int y_sample = p_YSamples[check_sample_buf_ofs((x >> 4) * BLOCKS_PER_MCU * 64 + ((x & 8) ? 64 : 0) + (x & 7) + y_sample_base_ofs)]; + + int c_x0 = (x - 1) >> 1; + int c_x1 = JPGD_MIN(c_x0 + 1, half_image_x_size); + c_x0 = JPGD_MAX(c_x0, 0); + + int a = (c_x0 >> 3) * BLOCKS_PER_MCU * 64 + (c_x0 & 7); + int cb00_sample = p_C0Samples[check_sample_buf_ofs(a + y0_base)]; + int cr00_sample = p_C0Samples[check_sample_buf_ofs(a + y0_base + 64)]; + + int cb01_sample = m_pSample_buf[check_sample_buf_ofs(a + y1_base)]; + int cr01_sample = m_pSample_buf[check_sample_buf_ofs(a + y1_base + 64)]; + + int b = (c_x1 >> 3) * BLOCKS_PER_MCU * 64 + (c_x1 & 7); + int cb10_sample = p_C0Samples[check_sample_buf_ofs(b + y0_base)]; + int cr10_sample = p_C0Samples[check_sample_buf_ofs(b + y0_base + 64)]; + + int cb11_sample = m_pSample_buf[check_sample_buf_ofs(b + y1_base)]; + int cr11_sample = m_pSample_buf[check_sample_buf_ofs(b + y1_base + 64)]; + + const uint8_t* pMuls = &s_muls[row & 1][x & 1][0]; + int cb = (cb00_sample * pMuls[0] + cb01_sample * pMuls[1] + cb10_sample * pMuls[2] + cb11_sample * pMuls[3] + 8) >> 4; + int cr = (cr00_sample * pMuls[0] + cr01_sample * pMuls[1] + cr10_sample * pMuls[2] + cr11_sample * pMuls[3] + 8) >> 4; + + int rc = m_crr[cr]; + int gc = ((m_crg[cr] + m_cbg[cb]) >> 16); + int bc = m_cbb[cb]; + + d0[0] = clamp(y_sample + rc); + d0[1] = clamp(y_sample + gc); + d0[2] = clamp(y_sample + bc); + d0[3] = 255; + + d0 += 4; + } + + return 1; + } + } + + // Y (1 block per MCU) to 8-bit grayscale + void jpeg_decoder::gray_convert() + { + int row = m_max_mcu_y_size - m_mcu_lines_left; + uint8* d = m_pScan_line_0; + uint8* s = m_pSample_buf + row * 8; + + for (int i = m_max_mcus_per_row; i > 0; i--) + { + *(uint*)d = *(uint*)s; + *(uint*)(&d[4]) = *(uint*)(&s[4]); + + s += 64; + d += 8; + } + } + + // Find end of image (EOI) marker, so we can return to the user the exact size of the input stream. + void jpeg_decoder::find_eoi() + { + if (!m_progressive_flag) + { + // Attempt to read the EOI marker. + //get_bits_no_markers(m_bits_left & 7); + + // Prime the bit buffer + m_bits_left = 16; + get_bits(16); + get_bits(16); + + // The next marker _should_ be EOI + process_markers(); + } + + m_total_bytes_read -= m_in_buf_left; + } + + int jpeg_decoder::decode_next_mcu_row() + { + if (::setjmp(m_jmp_state)) + return JPGD_FAILED; + + const bool chroma_y_filtering = ((m_flags & cFlagBoxChromaFiltering) == 0) && ((m_scan_type == JPGD_YH2V2) || (m_scan_type == JPGD_YH1V2)); + if (chroma_y_filtering) + { + std::swap(m_pSample_buf, m_pSample_buf_prev); + + m_sample_buf_prev_valid = true; + } + + if (m_progressive_flag) + load_next_row(); + else + decode_next_row(); + + // Find the EOI marker if that was the last row. + if (m_total_lines_left <= m_max_mcu_y_size) + find_eoi(); + + m_mcu_lines_left = m_max_mcu_y_size; + return 0; + } + + int jpeg_decoder::decode(const void** pScan_line, uint* pScan_line_len) + { + if ((m_error_code) || (!m_ready_flag)) + return JPGD_FAILED; + + if (m_total_lines_left == 0) + return JPGD_DONE; + + const bool chroma_y_filtering = ((m_flags & cFlagBoxChromaFiltering) == 0) && ((m_scan_type == JPGD_YH2V2) || (m_scan_type == JPGD_YH1V2)); + + bool get_another_mcu_row = false; + bool got_mcu_early = false; + if (chroma_y_filtering) + { + if (m_total_lines_left == m_image_y_size) + get_another_mcu_row = true; + else if ((m_mcu_lines_left == 1) && (m_total_lines_left > 1)) + { + get_another_mcu_row = true; + got_mcu_early = true; + } + } + else + { + get_another_mcu_row = (m_mcu_lines_left == 0); + } + + if (get_another_mcu_row) + { + int status = decode_next_mcu_row(); + if (status != 0) + return status; + } + + switch (m_scan_type) + { + case JPGD_YH2V2: + { + if ((m_flags & cFlagBoxChromaFiltering) == 0) + { + if (m_num_buffered_scanlines == 1) + { + *pScan_line = m_pScan_line_1; + } + else if (m_num_buffered_scanlines == 0) + { + m_num_buffered_scanlines = H2V2ConvertFiltered(); + *pScan_line = m_pScan_line_0; + } + + m_num_buffered_scanlines--; + } + else + { + if ((m_mcu_lines_left & 1) == 0) + { + H2V2Convert(); + *pScan_line = m_pScan_line_0; + } + else + *pScan_line = m_pScan_line_1; + } + + break; + } + case JPGD_YH2V1: + { + if ((m_flags & cFlagBoxChromaFiltering) == 0) + H2V1ConvertFiltered(); + else + H2V1Convert(); + *pScan_line = m_pScan_line_0; + break; + } + case JPGD_YH1V2: + { + if (chroma_y_filtering) + { + H1V2ConvertFiltered(); + *pScan_line = m_pScan_line_0; + } + else + { + if ((m_mcu_lines_left & 1) == 0) + { + H1V2Convert(); + *pScan_line = m_pScan_line_0; + } + else + *pScan_line = m_pScan_line_1; + } + + break; + } + case JPGD_YH1V1: + { + H1V1Convert(); + *pScan_line = m_pScan_line_0; + break; + } + case JPGD_GRAYSCALE: + { + gray_convert(); + *pScan_line = m_pScan_line_0; + + break; + } + } + + *pScan_line_len = m_real_dest_bytes_per_scan_line; + + if (!got_mcu_early) + { + m_mcu_lines_left--; + } + + m_total_lines_left--; + + return JPGD_SUCCESS; + } + + // Creates the tables needed for efficient Huffman decoding. + void jpeg_decoder::make_huff_table(int index, huff_tables* pH) + { + int p, i, l, si; + uint8 huffsize[258]; + uint huffcode[258]; + uint code; + uint subtree; + int code_size; + int lastp; + int nextfreeentry; + int currententry; + + pH->ac_table = m_huff_ac[index] != 0; + + p = 0; + + for (l = 1; l <= 16; l++) + { + for (i = 1; i <= m_huff_num[index][l]; i++) + { + if (p >= 257) + stop_decoding(JPGD_DECODE_ERROR); + huffsize[p++] = static_cast(l); + } + } + + assert(p < 258); + huffsize[p] = 0; + + lastp = p; + + code = 0; + si = huffsize[0]; + p = 0; + + while (huffsize[p]) + { + while (huffsize[p] == si) + { + if (p >= 257) + stop_decoding(JPGD_DECODE_ERROR); + huffcode[p++] = code; + code++; + } + + code <<= 1; + si++; + } + + memset(pH->look_up, 0, sizeof(pH->look_up)); + memset(pH->look_up2, 0, sizeof(pH->look_up2)); + memset(pH->tree, 0, sizeof(pH->tree)); + memset(pH->code_size, 0, sizeof(pH->code_size)); + + nextfreeentry = -1; + + p = 0; + + while (p < lastp) + { + i = m_huff_val[index][p]; + + code = huffcode[p]; + code_size = huffsize[p]; + + assert(i < JPGD_HUFF_CODE_SIZE_MAX_LENGTH); + pH->code_size[i] = static_cast(code_size); + + if (code_size <= 8) + { + code <<= (8 - code_size); + + for (l = 1 << (8 - code_size); l > 0; l--) + { + if (code >= 256) + stop_decoding(JPGD_DECODE_ERROR); + + pH->look_up[code] = i; + + bool has_extrabits = false; + int extra_bits = 0; + int num_extra_bits = i & 15; + + int bits_to_fetch = code_size; + if (num_extra_bits) + { + int total_codesize = code_size + num_extra_bits; + if (total_codesize <= 8) + { + has_extrabits = true; + extra_bits = ((1 << num_extra_bits) - 1) & (code >> (8 - total_codesize)); + + if (extra_bits > 0x7FFF) + stop_decoding(JPGD_DECODE_ERROR); + + bits_to_fetch += num_extra_bits; + } + } + + if (!has_extrabits) + pH->look_up2[code] = i | (bits_to_fetch << 8); + else + pH->look_up2[code] = i | 0x8000 | (extra_bits << 16) | (bits_to_fetch << 8); + + code++; + } + } + else + { + subtree = (code >> (code_size - 8)) & 0xFF; + + currententry = pH->look_up[subtree]; + + if (currententry == 0) + { + pH->look_up[subtree] = currententry = nextfreeentry; + pH->look_up2[subtree] = currententry = nextfreeentry; + + nextfreeentry -= 2; + } + + code <<= (16 - (code_size - 8)); + + for (l = code_size; l > 9; l--) + { + if ((code & 0x8000) == 0) + currententry--; + + unsigned int idx = -currententry - 1; + + if (idx >= JPGD_HUFF_TREE_MAX_LENGTH) + stop_decoding(JPGD_DECODE_ERROR); + + if (pH->tree[idx] == 0) + { + pH->tree[idx] = nextfreeentry; + + currententry = nextfreeentry; + + nextfreeentry -= 2; + } + else + { + currententry = pH->tree[idx]; + } + + code <<= 1; + } + + if ((code & 0x8000) == 0) + currententry--; + + if ((-currententry - 1) >= JPGD_HUFF_TREE_MAX_LENGTH) + stop_decoding(JPGD_DECODE_ERROR); + + pH->tree[-currententry - 1] = i; + } + + p++; + } + } + + // Verifies the quantization tables needed for this scan are available. + void jpeg_decoder::check_quant_tables() + { + for (int i = 0; i < m_comps_in_scan; i++) + if (m_quant[m_comp_quant[m_comp_list[i]]] == nullptr) + stop_decoding(JPGD_UNDEFINED_QUANT_TABLE); + } + + // Verifies that all the Huffman tables needed for this scan are available. + void jpeg_decoder::check_huff_tables() + { + for (int i = 0; i < m_comps_in_scan; i++) + { + if ((m_spectral_start == 0) && (m_huff_num[m_comp_dc_tab[m_comp_list[i]]] == nullptr)) + stop_decoding(JPGD_UNDEFINED_HUFF_TABLE); + + if ((m_spectral_end > 0) && (m_huff_num[m_comp_ac_tab[m_comp_list[i]]] == nullptr)) + stop_decoding(JPGD_UNDEFINED_HUFF_TABLE); + } + + for (int i = 0; i < JPGD_MAX_HUFF_TABLES; i++) + if (m_huff_num[i]) + { + if (!m_pHuff_tabs[i]) + m_pHuff_tabs[i] = (huff_tables*)alloc(sizeof(huff_tables)); + + make_huff_table(i, m_pHuff_tabs[i]); + } + } + + // Determines the component order inside each MCU. + // Also calcs how many MCU's are on each row, etc. + bool jpeg_decoder::calc_mcu_block_order() + { + int component_num, component_id; + int max_h_samp = 0, max_v_samp = 0; + + for (component_id = 0; component_id < m_comps_in_frame; component_id++) + { + if (m_comp_h_samp[component_id] > max_h_samp) + max_h_samp = m_comp_h_samp[component_id]; + + if (m_comp_v_samp[component_id] > max_v_samp) + max_v_samp = m_comp_v_samp[component_id]; + } + + for (component_id = 0; component_id < m_comps_in_frame; component_id++) + { + m_comp_h_blocks[component_id] = ((((m_image_x_size * m_comp_h_samp[component_id]) + (max_h_samp - 1)) / max_h_samp) + 7) / 8; + m_comp_v_blocks[component_id] = ((((m_image_y_size * m_comp_v_samp[component_id]) + (max_v_samp - 1)) / max_v_samp) + 7) / 8; + } + + if (m_comps_in_scan == 1) + { + m_mcus_per_row = m_comp_h_blocks[m_comp_list[0]]; + m_mcus_per_col = m_comp_v_blocks[m_comp_list[0]]; + } + else + { + m_mcus_per_row = (((m_image_x_size + 7) / 8) + (max_h_samp - 1)) / max_h_samp; + m_mcus_per_col = (((m_image_y_size + 7) / 8) + (max_v_samp - 1)) / max_v_samp; + } + + if (m_comps_in_scan == 1) + { + m_mcu_org[0] = m_comp_list[0]; + + m_blocks_per_mcu = 1; + } + else + { + m_blocks_per_mcu = 0; + + for (component_num = 0; component_num < m_comps_in_scan; component_num++) + { + int num_blocks; + + component_id = m_comp_list[component_num]; + + num_blocks = m_comp_h_samp[component_id] * m_comp_v_samp[component_id]; + + while (num_blocks--) + m_mcu_org[m_blocks_per_mcu++] = component_id; + } + } + + if (m_blocks_per_mcu > m_max_blocks_per_mcu) + return false; + + for (int mcu_block = 0; mcu_block < m_blocks_per_mcu; mcu_block++) + { + int comp_id = m_mcu_org[mcu_block]; + if (comp_id >= JPGD_MAX_QUANT_TABLES) + return false; + } + + return true; + } + + // Starts a new scan. + int jpeg_decoder::init_scan() + { + if (!locate_sos_marker()) + return JPGD_FALSE; + + if (!calc_mcu_block_order()) + return JPGD_FALSE; + + check_huff_tables(); + + check_quant_tables(); + + memset(m_last_dc_val, 0, m_comps_in_frame * sizeof(uint)); + + m_eob_run = 0; + + if (m_restart_interval) + { + m_restarts_left = m_restart_interval; + m_next_restart_num = 0; + } + + fix_in_buffer(); + + return JPGD_TRUE; + } + + // Starts a frame. Determines if the number of components or sampling factors + // are supported. + void jpeg_decoder::init_frame() + { + int i; + + if (m_comps_in_frame == 1) + { + if ((m_comp_h_samp[0] != 1) || (m_comp_v_samp[0] != 1)) + stop_decoding(JPGD_UNSUPPORTED_SAMP_FACTORS); + + m_scan_type = JPGD_GRAYSCALE; + m_max_blocks_per_mcu = 1; + m_max_mcu_x_size = 8; + m_max_mcu_y_size = 8; + } + else if (m_comps_in_frame == 3) + { + if (((m_comp_h_samp[1] != 1) || (m_comp_v_samp[1] != 1)) || + ((m_comp_h_samp[2] != 1) || (m_comp_v_samp[2] != 1))) + stop_decoding(JPGD_UNSUPPORTED_SAMP_FACTORS); + + if ((m_comp_h_samp[0] == 1) && (m_comp_v_samp[0] == 1)) + { + m_scan_type = JPGD_YH1V1; + + m_max_blocks_per_mcu = 3; + m_max_mcu_x_size = 8; + m_max_mcu_y_size = 8; + } + else if ((m_comp_h_samp[0] == 2) && (m_comp_v_samp[0] == 1)) + { + m_scan_type = JPGD_YH2V1; + m_max_blocks_per_mcu = 4; + m_max_mcu_x_size = 16; + m_max_mcu_y_size = 8; + } + else if ((m_comp_h_samp[0] == 1) && (m_comp_v_samp[0] == 2)) + { + m_scan_type = JPGD_YH1V2; + m_max_blocks_per_mcu = 4; + m_max_mcu_x_size = 8; + m_max_mcu_y_size = 16; + } + else if ((m_comp_h_samp[0] == 2) && (m_comp_v_samp[0] == 2)) + { + m_scan_type = JPGD_YH2V2; + m_max_blocks_per_mcu = 6; + m_max_mcu_x_size = 16; + m_max_mcu_y_size = 16; + } + else + stop_decoding(JPGD_UNSUPPORTED_SAMP_FACTORS); + } + else + stop_decoding(JPGD_UNSUPPORTED_COLORSPACE); + + m_max_mcus_per_row = (m_image_x_size + (m_max_mcu_x_size - 1)) / m_max_mcu_x_size; + m_max_mcus_per_col = (m_image_y_size + (m_max_mcu_y_size - 1)) / m_max_mcu_y_size; + + // These values are for the *destination* pixels: after conversion. + if (m_scan_type == JPGD_GRAYSCALE) + m_dest_bytes_per_pixel = 1; + else + m_dest_bytes_per_pixel = 4; + + m_dest_bytes_per_scan_line = ((m_image_x_size + 15) & 0xFFF0) * m_dest_bytes_per_pixel; + + m_real_dest_bytes_per_scan_line = (m_image_x_size * m_dest_bytes_per_pixel); + + // Initialize two scan line buffers. + m_pScan_line_0 = (uint8*)alloc_aligned(m_dest_bytes_per_scan_line, true); + if ((m_scan_type == JPGD_YH1V2) || (m_scan_type == JPGD_YH2V2)) + m_pScan_line_1 = (uint8*)alloc_aligned(m_dest_bytes_per_scan_line, true); + + m_max_blocks_per_row = m_max_mcus_per_row * m_max_blocks_per_mcu; + + // Should never happen + if (m_max_blocks_per_row > JPGD_MAX_BLOCKS_PER_ROW) + stop_decoding(JPGD_DECODE_ERROR); + + // Allocate the coefficient buffer, enough for one MCU + m_pMCU_coefficients = (jpgd_block_coeff_t *)alloc_aligned(m_max_blocks_per_mcu * 64 * sizeof(jpgd_block_coeff_t)); + + for (i = 0; i < m_max_blocks_per_mcu; i++) + m_mcu_block_max_zag[i] = 64; + + m_pSample_buf = (uint8*)alloc_aligned(m_max_blocks_per_row * 64); + m_pSample_buf_prev = (uint8*)alloc_aligned(m_max_blocks_per_row * 64); + + m_total_lines_left = m_image_y_size; + + m_mcu_lines_left = 0; + + create_look_ups(); + } + + // The coeff_buf series of methods originally stored the coefficients + // into a "virtual" file which was located in EMS, XMS, or a disk file. A cache + // was used to make this process more efficient. Now, we can store the entire + // thing in RAM. + jpeg_decoder::coeff_buf* jpeg_decoder::coeff_buf_open(int block_num_x, int block_num_y, int block_len_x, int block_len_y) + { + coeff_buf* cb = (coeff_buf*)alloc(sizeof(coeff_buf)); + + cb->block_num_x = block_num_x; + cb->block_num_y = block_num_y; + cb->block_len_x = block_len_x; + cb->block_len_y = block_len_y; + cb->block_size = (block_len_x * block_len_y) * sizeof(jpgd_block_coeff_t); + cb->pData = (uint8*)alloc(cb->block_size * block_num_x * block_num_y, true); + return cb; + } + + inline jpgd_block_coeff_t* jpeg_decoder::coeff_buf_getp(coeff_buf* cb, int block_x, int block_y) + { + if ((block_x >= cb->block_num_x) || (block_y >= cb->block_num_y)) + stop_decoding(JPGD_DECODE_ERROR); + + return (jpgd_block_coeff_t*)(cb->pData + block_x * cb->block_size + block_y * (cb->block_size * cb->block_num_x)); + } + + // The following methods decode the various types of m_blocks encountered + // in progressively encoded images. + void jpeg_decoder::decode_block_dc_first(jpeg_decoder* pD, int component_id, int block_x, int block_y) + { + int s, r; + jpgd_block_coeff_t* p = pD->coeff_buf_getp(pD->m_dc_coeffs[component_id], block_x, block_y); + + if ((s = pD->huff_decode(pD->m_pHuff_tabs[pD->m_comp_dc_tab[component_id]])) != 0) + { + if (s >= 16) + pD->stop_decoding(JPGD_DECODE_ERROR); + + r = pD->get_bits_no_markers(s); + s = JPGD_HUFF_EXTEND(r, s); + } + + pD->m_last_dc_val[component_id] = (s += pD->m_last_dc_val[component_id]); + + p[0] = static_cast(s << pD->m_successive_low); + } + + void jpeg_decoder::decode_block_dc_refine(jpeg_decoder* pD, int component_id, int block_x, int block_y) + { + if (pD->get_bits_no_markers(1)) + { + jpgd_block_coeff_t* p = pD->coeff_buf_getp(pD->m_dc_coeffs[component_id], block_x, block_y); + + p[0] |= (1 << pD->m_successive_low); + } + } + + void jpeg_decoder::decode_block_ac_first(jpeg_decoder* pD, int component_id, int block_x, int block_y) + { + int k, s, r; + + if (pD->m_eob_run) + { + pD->m_eob_run--; + return; + } + + jpgd_block_coeff_t* p = pD->coeff_buf_getp(pD->m_ac_coeffs[component_id], block_x, block_y); + + for (k = pD->m_spectral_start; k <= pD->m_spectral_end; k++) + { + unsigned int idx = pD->m_comp_ac_tab[component_id]; + if (idx >= JPGD_MAX_HUFF_TABLES) + pD->stop_decoding(JPGD_DECODE_ERROR); + + s = pD->huff_decode(pD->m_pHuff_tabs[idx]); + + r = s >> 4; + s &= 15; + + if (s) + { + if ((k += r) > 63) + pD->stop_decoding(JPGD_DECODE_ERROR); + + r = pD->get_bits_no_markers(s); + s = JPGD_HUFF_EXTEND(r, s); + + p[g_ZAG[k]] = static_cast(s << pD->m_successive_low); + } + else + { + if (r == 15) + { + if ((k += 15) > 63) + pD->stop_decoding(JPGD_DECODE_ERROR); + } + else + { + pD->m_eob_run = 1 << r; + + if (r) + pD->m_eob_run += pD->get_bits_no_markers(r); + + pD->m_eob_run--; + + break; + } + } + } + } + + void jpeg_decoder::decode_block_ac_refine(jpeg_decoder* pD, int component_id, int block_x, int block_y) + { + int s, k, r; + + int p1 = 1 << pD->m_successive_low; + + //int m1 = (-1) << pD->m_successive_low; + int m1 = static_cast((UINT32_MAX << pD->m_successive_low)); + + jpgd_block_coeff_t* p = pD->coeff_buf_getp(pD->m_ac_coeffs[component_id], block_x, block_y); + if (pD->m_spectral_end > 63) + pD->stop_decoding(JPGD_DECODE_ERROR); + + k = pD->m_spectral_start; + + if (pD->m_eob_run == 0) + { + for (; k <= pD->m_spectral_end; k++) + { + unsigned int idx = pD->m_comp_ac_tab[component_id]; + if (idx >= JPGD_MAX_HUFF_TABLES) + pD->stop_decoding(JPGD_DECODE_ERROR); + + s = pD->huff_decode(pD->m_pHuff_tabs[idx]); + + r = s >> 4; + s &= 15; + + if (s) + { + if (s != 1) + pD->stop_decoding(JPGD_DECODE_ERROR); + + if (pD->get_bits_no_markers(1)) + s = p1; + else + s = m1; + } + else + { + if (r != 15) + { + pD->m_eob_run = 1 << r; + + if (r) + pD->m_eob_run += pD->get_bits_no_markers(r); + + break; + } + } + + do + { + jpgd_block_coeff_t* this_coef = p + g_ZAG[k & 63]; + + if (*this_coef != 0) + { + if (pD->get_bits_no_markers(1)) + { + if ((*this_coef & p1) == 0) + { + if (*this_coef >= 0) + *this_coef = static_cast(*this_coef + p1); + else + *this_coef = static_cast(*this_coef + m1); + } + } + } + else + { + if (--r < 0) + break; + } + + k++; + + } while (k <= pD->m_spectral_end); + + if ((s) && (k < 64)) + { + p[g_ZAG[k]] = static_cast(s); + } + } + } + + if (pD->m_eob_run > 0) + { + for (; k <= pD->m_spectral_end; k++) + { + jpgd_block_coeff_t* this_coef = p + g_ZAG[k & 63]; // logical AND to shut up static code analysis + + if (*this_coef != 0) + { + if (pD->get_bits_no_markers(1)) + { + if ((*this_coef & p1) == 0) + { + if (*this_coef >= 0) + *this_coef = static_cast(*this_coef + p1); + else + *this_coef = static_cast(*this_coef + m1); + } + } + } + } + + pD->m_eob_run--; + } + } + + // Decode a scan in a progressively encoded image. + void jpeg_decoder::decode_scan(pDecode_block_func decode_block_func) + { + int mcu_row, mcu_col, mcu_block; + int block_x_mcu[JPGD_MAX_COMPONENTS], block_y_mcu[JPGD_MAX_COMPONENTS]; + + memset(block_y_mcu, 0, sizeof(block_y_mcu)); + + for (mcu_col = 0; mcu_col < m_mcus_per_col; mcu_col++) + { + int component_num, component_id; + + memset(block_x_mcu, 0, sizeof(block_x_mcu)); + + for (mcu_row = 0; mcu_row < m_mcus_per_row; mcu_row++) + { + int block_x_mcu_ofs = 0, block_y_mcu_ofs = 0; + + if ((m_restart_interval) && (m_restarts_left == 0)) + process_restart(); + + for (mcu_block = 0; mcu_block < m_blocks_per_mcu; mcu_block++) + { + component_id = m_mcu_org[mcu_block]; + + decode_block_func(this, component_id, block_x_mcu[component_id] + block_x_mcu_ofs, block_y_mcu[component_id] + block_y_mcu_ofs); + + if (m_comps_in_scan == 1) + block_x_mcu[component_id]++; + else + { + if (++block_x_mcu_ofs == m_comp_h_samp[component_id]) + { + block_x_mcu_ofs = 0; + + if (++block_y_mcu_ofs == m_comp_v_samp[component_id]) + { + block_y_mcu_ofs = 0; + block_x_mcu[component_id] += m_comp_h_samp[component_id]; + } + } + } + } + + m_restarts_left--; + } + + if (m_comps_in_scan == 1) + block_y_mcu[m_comp_list[0]]++; + else + { + for (component_num = 0; component_num < m_comps_in_scan; component_num++) + { + component_id = m_comp_list[component_num]; + block_y_mcu[component_id] += m_comp_v_samp[component_id]; + } + } + } + } + + // Decode a progressively encoded image. + void jpeg_decoder::init_progressive() + { + int i; + + if (m_comps_in_frame == 4) + stop_decoding(JPGD_UNSUPPORTED_COLORSPACE); + + // Allocate the coefficient buffers. + for (i = 0; i < m_comps_in_frame; i++) + { + m_dc_coeffs[i] = coeff_buf_open(m_max_mcus_per_row * m_comp_h_samp[i], m_max_mcus_per_col * m_comp_v_samp[i], 1, 1); + m_ac_coeffs[i] = coeff_buf_open(m_max_mcus_per_row * m_comp_h_samp[i], m_max_mcus_per_col * m_comp_v_samp[i], 8, 8); + } + + // See https://libjpeg-turbo.org/pmwiki/uploads/About/TwoIssueswiththeJPEGStandard.pdf + uint32_t total_scans = 0; + const uint32_t MAX_SCANS_TO_PROCESS = 1000; + + for (; ; ) + { + int dc_only_scan, refinement_scan; + pDecode_block_func decode_block_func; + + if (!init_scan()) + break; + + dc_only_scan = (m_spectral_start == 0); + refinement_scan = (m_successive_high != 0); + + if ((m_spectral_start > m_spectral_end) || (m_spectral_end > 63)) + stop_decoding(JPGD_BAD_SOS_SPECTRAL); + + if (dc_only_scan) + { + if (m_spectral_end) + stop_decoding(JPGD_BAD_SOS_SPECTRAL); + } + else if (m_comps_in_scan != 1) /* AC scans can only contain one component */ + stop_decoding(JPGD_BAD_SOS_SPECTRAL); + + if ((refinement_scan) && (m_successive_low != m_successive_high - 1)) + stop_decoding(JPGD_BAD_SOS_SUCCESSIVE); + + if (dc_only_scan) + { + if (refinement_scan) + decode_block_func = decode_block_dc_refine; + else + decode_block_func = decode_block_dc_first; + } + else + { + if (refinement_scan) + decode_block_func = decode_block_ac_refine; + else + decode_block_func = decode_block_ac_first; + } + + decode_scan(decode_block_func); + + m_bits_left = 16; + get_bits(16); + get_bits(16); + + total_scans++; + if (total_scans > MAX_SCANS_TO_PROCESS) + stop_decoding(JPGD_TOO_MANY_SCANS); + } + + m_comps_in_scan = m_comps_in_frame; + + for (i = 0; i < m_comps_in_frame; i++) + m_comp_list[i] = i; + + if (!calc_mcu_block_order()) + stop_decoding(JPGD_DECODE_ERROR); + } + + void jpeg_decoder::init_sequential() + { + if (!init_scan()) + stop_decoding(JPGD_UNEXPECTED_MARKER); + } + + void jpeg_decoder::decode_start() + { + init_frame(); + + if (m_progressive_flag) + init_progressive(); + else + init_sequential(); + } + + void jpeg_decoder::decode_init(jpeg_decoder_stream* pStream, uint32_t flags) + { + init(pStream, flags); + locate_sof_marker(); + } + + jpeg_decoder::jpeg_decoder(jpeg_decoder_stream* pStream, uint32_t flags) + { + if (::setjmp(m_jmp_state)) + return; + decode_init(pStream, flags); + } + + int jpeg_decoder::begin_decoding() + { + if (m_ready_flag) + return JPGD_SUCCESS; + + if (m_error_code) + return JPGD_FAILED; + + if (::setjmp(m_jmp_state)) + return JPGD_FAILED; + + decode_start(); + + m_ready_flag = true; + + return JPGD_SUCCESS; + } + + jpeg_decoder::~jpeg_decoder() + { + free_all_blocks(); + } + + jpeg_decoder_file_stream::jpeg_decoder_file_stream() + { + m_pFile = nullptr; + m_eof_flag = false; + m_error_flag = false; + } + + void jpeg_decoder_file_stream::close() + { + if (m_pFile) + { + fclose(m_pFile); + m_pFile = nullptr; + } + + m_eof_flag = false; + m_error_flag = false; + } + + jpeg_decoder_file_stream::~jpeg_decoder_file_stream() + { + close(); + } + + bool jpeg_decoder_file_stream::open(const char* Pfilename) + { + close(); + + m_eof_flag = false; + m_error_flag = false; #if defined(_MSC_VER) - m_pFile = NULL; - fopen_s(&m_pFile, Pfilename, "rb"); + m_pFile = nullptr; + fopen_s(&m_pFile, Pfilename, "rb"); #else - m_pFile = fopen(Pfilename, "rb"); + m_pFile = fopen(Pfilename, "rb"); #endif - return m_pFile != NULL; -} + return m_pFile != nullptr; + } -int jpeg_decoder_file_stream::read(uint8 *pBuf, int max_bytes_to_read, bool *pEOF_flag) -{ - if (!m_pFile) - return -1; + int jpeg_decoder_file_stream::read(uint8* pBuf, int max_bytes_to_read, bool* pEOF_flag) + { + if (!m_pFile) + return -1; - if (m_eof_flag) - { - *pEOF_flag = true; - return 0; - } + if (m_eof_flag) + { + *pEOF_flag = true; + return 0; + } - if (m_error_flag) - return -1; + if (m_error_flag) + return -1; - int bytes_read = static_cast(fread(pBuf, 1, max_bytes_to_read, m_pFile)); - if (bytes_read < max_bytes_to_read) - { - if (ferror(m_pFile)) - { - m_error_flag = true; - return -1; - } + int bytes_read = static_cast(fread(pBuf, 1, max_bytes_to_read, m_pFile)); + if (bytes_read < max_bytes_to_read) + { + if (ferror(m_pFile)) + { + m_error_flag = true; + return -1; + } - m_eof_flag = true; - *pEOF_flag = true; - } + m_eof_flag = true; + *pEOF_flag = true; + } - return bytes_read; -} + return bytes_read; + } -bool jpeg_decoder_mem_stream::open(const uint8 *pSrc_data, uint size) -{ - close(); - m_pSrc_data = pSrc_data; - m_ofs = 0; - m_size = size; - return true; -} + bool jpeg_decoder_mem_stream::open(const uint8* pSrc_data, uint size) + { + close(); + m_pSrc_data = pSrc_data; + m_ofs = 0; + m_size = size; + return true; + } -int jpeg_decoder_mem_stream::read(uint8 *pBuf, int max_bytes_to_read, bool *pEOF_flag) -{ - *pEOF_flag = false; + int jpeg_decoder_mem_stream::read(uint8* pBuf, int max_bytes_to_read, bool* pEOF_flag) + { + *pEOF_flag = false; - if (!m_pSrc_data) - return -1; + if (!m_pSrc_data) + return -1; - uint bytes_remaining = m_size - m_ofs; - if ((uint)max_bytes_to_read > bytes_remaining) - { - max_bytes_to_read = bytes_remaining; - *pEOF_flag = true; - } + uint bytes_remaining = m_size - m_ofs; + if ((uint)max_bytes_to_read > bytes_remaining) + { + max_bytes_to_read = bytes_remaining; + *pEOF_flag = true; + } - memcpy(pBuf, m_pSrc_data + m_ofs, max_bytes_to_read); - m_ofs += max_bytes_to_read; + memcpy(pBuf, m_pSrc_data + m_ofs, max_bytes_to_read); + m_ofs += max_bytes_to_read; - return max_bytes_to_read; -} + return max_bytes_to_read; + } -unsigned char *decompress_jpeg_image_from_stream(jpeg_decoder_stream *pStream, int *width, int *height, int *actual_comps, int req_comps) -{ - if (!actual_comps) - return NULL; - *actual_comps = 0; + unsigned char* decompress_jpeg_image_from_stream(jpeg_decoder_stream* pStream, int* width, int* height, int* actual_comps, int req_comps, uint32_t flags) + { + if (!actual_comps) + return nullptr; + *actual_comps = 0; - if ((!pStream) || (!width) || (!height) || (!req_comps)) - return NULL; + if ((!pStream) || (!width) || (!height) || (!req_comps)) + return nullptr; - if ((req_comps != 1) && (req_comps != 3) && (req_comps != 4)) - return NULL; + if ((req_comps != 1) && (req_comps != 3) && (req_comps != 4)) + return nullptr; - jpeg_decoder decoder(pStream); - if (decoder.get_error_code() != JPGD_SUCCESS) - return NULL; + jpeg_decoder decoder(pStream, flags); + if (decoder.get_error_code() != JPGD_SUCCESS) + return nullptr; - const int image_width = decoder.get_width(), image_height = decoder.get_height(); - *width = image_width; - *height = image_height; - *actual_comps = decoder.get_num_components(); + const int image_width = decoder.get_width(), image_height = decoder.get_height(); + *width = image_width; + *height = image_height; + *actual_comps = decoder.get_num_components(); - if (decoder.begin_decoding() != JPGD_SUCCESS) - return NULL; + if (decoder.begin_decoding() != JPGD_SUCCESS) + return nullptr; - const int dst_bpl = image_width * req_comps; + const int dst_bpl = image_width * req_comps; - uint8 *pImage_data = (uint8*)jpgd_malloc(dst_bpl * image_height); - if (!pImage_data) - return NULL; + uint8* pImage_data = (uint8*)jpgd_malloc(dst_bpl * image_height); + if (!pImage_data) + return nullptr; - for (int y = 0; y < image_height; y++) - { - const uint8* pScan_line = nullptr; - uint scan_line_len; - if (decoder.decode((const void**)&pScan_line, &scan_line_len) != JPGD_SUCCESS) - { - jpgd_free(pImage_data); - return NULL; - } + for (int y = 0; y < image_height; y++) + { + const uint8* pScan_line; + uint scan_line_len; + if (decoder.decode((const void**)&pScan_line, &scan_line_len) != JPGD_SUCCESS) + { + jpgd_free(pImage_data); + return nullptr; + } - uint8 *pDst = pImage_data + y * dst_bpl; + uint8* pDst = pImage_data + y * dst_bpl; - if (((req_comps == 1) && (decoder.get_num_components() == 1)) || ((req_comps == 4) && (decoder.get_num_components() == 3))) - memcpy(pDst, pScan_line, dst_bpl); - else if (decoder.get_num_components() == 1) - { - if (req_comps == 3) - { - for (int x = 0; x < image_width; x++) - { - uint8 luma = pScan_line[x]; - pDst[0] = luma; - pDst[1] = luma; - pDst[2] = luma; - pDst += 3; - } - } - else - { - for (int x = 0; x < image_width; x++) - { - uint8 luma = pScan_line[x]; - pDst[0] = luma; - pDst[1] = luma; - pDst[2] = luma; - pDst[3] = 255; - pDst += 4; - } - } - } - else if (decoder.get_num_components() == 3) - { - if (req_comps == 1) - { - const int YR = 19595, YG = 38470, YB = 7471; - for (int x = 0; x < image_width; x++) - { - int r = pScan_line[x*4+0]; - int g = pScan_line[x*4+1]; - int b = pScan_line[x*4+2]; - *pDst++ = static_cast((r * YR + g * YG + b * YB + 32768) >> 16); - } - } - else - { - for (int x = 0; x < image_width; x++) - { - pDst[0] = pScan_line[x*4+0]; - pDst[1] = pScan_line[x*4+1]; - pDst[2] = pScan_line[x*4+2]; - pDst += 3; - } - } - } - } + if (((req_comps == 1) && (decoder.get_num_components() == 1)) || ((req_comps == 4) && (decoder.get_num_components() == 3))) + memcpy(pDst, pScan_line, dst_bpl); + else if (decoder.get_num_components() == 1) + { + if (req_comps == 3) + { + for (int x = 0; x < image_width; x++) + { + uint8 luma = pScan_line[x]; + pDst[0] = luma; + pDst[1] = luma; + pDst[2] = luma; + pDst += 3; + } + } + else + { + for (int x = 0; x < image_width; x++) + { + uint8 luma = pScan_line[x]; + pDst[0] = luma; + pDst[1] = luma; + pDst[2] = luma; + pDst[3] = 255; + pDst += 4; + } + } + } + else if (decoder.get_num_components() == 3) + { + if (req_comps == 1) + { + const int YR = 19595, YG = 38470, YB = 7471; + for (int x = 0; x < image_width; x++) + { + int r = pScan_line[x * 4 + 0]; + int g = pScan_line[x * 4 + 1]; + int b = pScan_line[x * 4 + 2]; + *pDst++ = static_cast((r * YR + g * YG + b * YB + 32768) >> 16); + } + } + else + { + for (int x = 0; x < image_width; x++) + { + pDst[0] = pScan_line[x * 4 + 0]; + pDst[1] = pScan_line[x * 4 + 1]; + pDst[2] = pScan_line[x * 4 + 2]; + pDst += 3; + } + } + } + } - return pImage_data; -} + return pImage_data; + } -unsigned char *decompress_jpeg_image_from_memory(const unsigned char *pSrc_data, int src_data_size, int *width, int *height, int *actual_comps, int req_comps) -{ - jpgd::jpeg_decoder_mem_stream mem_stream(pSrc_data, src_data_size); - return decompress_jpeg_image_from_stream(&mem_stream, width, height, actual_comps, req_comps); -} + unsigned char* decompress_jpeg_image_from_memory(const unsigned char* pSrc_data, int src_data_size, int* width, int* height, int* actual_comps, int req_comps, uint32_t flags) + { + jpgd::jpeg_decoder_mem_stream mem_stream(pSrc_data, src_data_size); + return decompress_jpeg_image_from_stream(&mem_stream, width, height, actual_comps, req_comps, flags); + } -unsigned char *decompress_jpeg_image_from_file(const char *pSrc_filename, int *width, int *height, int *actual_comps, int req_comps) -{ - jpgd::jpeg_decoder_file_stream file_stream; - if (!file_stream.open(pSrc_filename)) - return NULL; - return decompress_jpeg_image_from_stream(&file_stream, width, height, actual_comps, req_comps); -} + unsigned char* decompress_jpeg_image_from_file(const char* pSrc_filename, int* width, int* height, int* actual_comps, int req_comps, uint32_t flags) + { + jpgd::jpeg_decoder_file_stream file_stream; + if (!file_stream.open(pSrc_filename)) + return nullptr; + return decompress_jpeg_image_from_stream(&file_stream, width, height, actual_comps, req_comps, flags); + } } // namespace jpgd diff --git a/ext/jpge/jpgd.h b/ext/jpge/jpgd.h index 150b9a0b26..39136696ba 100644 --- a/ext/jpge/jpgd.h +++ b/ext/jpge/jpgd.h @@ -1,319 +1,351 @@ // jpgd.h - C++ class for JPEG decompression. -// Public domain, Rich Geldreich +// Richard Geldreich +// See jpgd.cpp for license (Public Domain or Apache 2.0). #ifndef JPEG_DECODER_H #define JPEG_DECODER_H #include #include #include +#include +#include #ifdef _MSC_VER - #define JPGD_NORETURN __declspec(noreturn) +#define JPGD_NORETURN __declspec(noreturn) #elif defined(__GNUC__) - #define JPGD_NORETURN __attribute__ ((noreturn)) +#define JPGD_NORETURN __attribute__ ((noreturn)) #else - #define JPGD_NORETURN +#define JPGD_NORETURN #endif +#define JPGD_HUFF_TREE_MAX_LENGTH 512 +#define JPGD_HUFF_CODE_SIZE_MAX_LENGTH 256 + namespace jpgd { - typedef unsigned char uint8; - typedef signed short int16; - typedef unsigned short uint16; - typedef unsigned int uint; - typedef signed int int32; + typedef unsigned char uint8; + typedef signed short int16; + typedef unsigned short uint16; + typedef unsigned int uint; + typedef signed int int32; - // Loads a JPEG image from a memory buffer or a file. - // req_comps can be 1 (grayscale), 3 (RGB), or 4 (RGBA). - // On return, width/height will be set to the image's dimensions, and actual_comps will be set to the either 1 (grayscale) or 3 (RGB). - // Notes: For more control over where and how the source data is read, see the decompress_jpeg_image_from_stream() function below, or call the jpeg_decoder class directly. - // Requesting a 8 or 32bpp image is currently a little faster than 24bpp because the jpeg_decoder class itself currently always unpacks to either 8 or 32bpp. - unsigned char *decompress_jpeg_image_from_memory(const unsigned char *pSrc_data, int src_data_size, int *width, int *height, int *actual_comps, int req_comps); - unsigned char *decompress_jpeg_image_from_file(const char *pSrc_filename, int *width, int *height, int *actual_comps, int req_comps); + // Loads a JPEG image from a memory buffer or a file. + // req_comps can be 1 (grayscale), 3 (RGB), or 4 (RGBA). + // On return, width/height will be set to the image's dimensions, and actual_comps will be set to the either 1 (grayscale) or 3 (RGB). + // Notes: For more control over where and how the source data is read, see the decompress_jpeg_image_from_stream() function below, or call the jpeg_decoder class directly. + // Requesting a 8 or 32bpp image is currently a little faster than 24bpp because the jpeg_decoder class itself currently always unpacks to either 8 or 32bpp. + unsigned char* decompress_jpeg_image_from_memory(const unsigned char* pSrc_data, int src_data_size, int* width, int* height, int* actual_comps, int req_comps, uint32_t flags = 0); + unsigned char* decompress_jpeg_image_from_file(const char* pSrc_filename, int* width, int* height, int* actual_comps, int req_comps, uint32_t flags = 0); - // Success/failure error codes. - enum jpgd_status - { - JPGD_SUCCESS = 0, JPGD_FAILED = -1, JPGD_DONE = 1, - JPGD_BAD_DHT_COUNTS = -256, JPGD_BAD_DHT_INDEX, JPGD_BAD_DHT_MARKER, JPGD_BAD_DQT_MARKER, JPGD_BAD_DQT_TABLE, - JPGD_BAD_PRECISION, JPGD_BAD_HEIGHT, JPGD_BAD_WIDTH, JPGD_TOO_MANY_COMPONENTS, - JPGD_BAD_SOF_LENGTH, JPGD_BAD_VARIABLE_MARKER, JPGD_BAD_DRI_LENGTH, JPGD_BAD_SOS_LENGTH, - JPGD_BAD_SOS_COMP_ID, JPGD_W_EXTRA_BYTES_BEFORE_MARKER, JPGD_NO_ARITHMITIC_SUPPORT, JPGD_UNEXPECTED_MARKER, - JPGD_NOT_JPEG, JPGD_UNSUPPORTED_MARKER, JPGD_BAD_DQT_LENGTH, JPGD_TOO_MANY_BLOCKS, - JPGD_UNDEFINED_QUANT_TABLE, JPGD_UNDEFINED_HUFF_TABLE, JPGD_NOT_SINGLE_SCAN, JPGD_UNSUPPORTED_COLORSPACE, - JPGD_UNSUPPORTED_SAMP_FACTORS, JPGD_DECODE_ERROR, JPGD_BAD_RESTART_MARKER, JPGD_ASSERTION_ERROR, - JPGD_BAD_SOS_SPECTRAL, JPGD_BAD_SOS_SUCCESSIVE, JPGD_STREAM_READ, JPGD_NOTENOUGHMEM - }; - - // Input stream interface. - // Derive from this class to read input data from sources other than files or memory. Set m_eof_flag to true when no more data is available. - // The decoder is rather greedy: it will keep on calling this method until its internal input buffer is full, or until the EOF flag is set. - // It the input stream contains data after the JPEG stream's EOI (end of image) marker it will probably be pulled into the internal buffer. - // Call the get_total_bytes_read() method to determine the actual size of the JPEG stream after successful decoding. - class jpeg_decoder_stream - { - public: - jpeg_decoder_stream() { } - virtual ~jpeg_decoder_stream() { } + // Success/failure error codes. + enum jpgd_status + { + JPGD_SUCCESS = 0, JPGD_FAILED = -1, JPGD_DONE = 1, + JPGD_BAD_DHT_COUNTS = -256, JPGD_BAD_DHT_INDEX, JPGD_BAD_DHT_MARKER, JPGD_BAD_DQT_MARKER, JPGD_BAD_DQT_TABLE, + JPGD_BAD_PRECISION, JPGD_BAD_HEIGHT, JPGD_BAD_WIDTH, JPGD_TOO_MANY_COMPONENTS, + JPGD_BAD_SOF_LENGTH, JPGD_BAD_VARIABLE_MARKER, JPGD_BAD_DRI_LENGTH, JPGD_BAD_SOS_LENGTH, + JPGD_BAD_SOS_COMP_ID, JPGD_W_EXTRA_BYTES_BEFORE_MARKER, JPGD_NO_ARITHMITIC_SUPPORT, JPGD_UNEXPECTED_MARKER, + JPGD_NOT_JPEG, JPGD_UNSUPPORTED_MARKER, JPGD_BAD_DQT_LENGTH, JPGD_TOO_MANY_BLOCKS, + JPGD_UNDEFINED_QUANT_TABLE, JPGD_UNDEFINED_HUFF_TABLE, JPGD_NOT_SINGLE_SCAN, JPGD_UNSUPPORTED_COLORSPACE, + JPGD_UNSUPPORTED_SAMP_FACTORS, JPGD_DECODE_ERROR, JPGD_BAD_RESTART_MARKER, + JPGD_BAD_SOS_SPECTRAL, JPGD_BAD_SOS_SUCCESSIVE, JPGD_STREAM_READ, JPGD_NOTENOUGHMEM, JPGD_TOO_MANY_SCANS + }; - // The read() method is called when the internal input buffer is empty. - // Parameters: - // pBuf - input buffer - // max_bytes_to_read - maximum bytes that can be written to pBuf - // pEOF_flag - set this to true if at end of stream (no more bytes remaining) - // Returns -1 on error, otherwise return the number of bytes actually written to the buffer (which may be 0). - // Notes: This method will be called in a loop until you set *pEOF_flag to true or the internal buffer is full. - virtual int read(uint8 *pBuf, int max_bytes_to_read, bool *pEOF_flag) = 0; - }; + // Input stream interface. + // Derive from this class to read input data from sources other than files or memory. Set m_eof_flag to true when no more data is available. + // The decoder is rather greedy: it will keep on calling this method until its internal input buffer is full, or until the EOF flag is set. + // It the input stream contains data after the JPEG stream's EOI (end of image) marker it will probably be pulled into the internal buffer. + // Call the get_total_bytes_read() method to determine the actual size of the JPEG stream after successful decoding. + class jpeg_decoder_stream + { + public: + jpeg_decoder_stream() { } + virtual ~jpeg_decoder_stream() { } - // stdio FILE stream class. - class jpeg_decoder_file_stream : public jpeg_decoder_stream - { - jpeg_decoder_file_stream(const jpeg_decoder_file_stream &); - jpeg_decoder_file_stream &operator =(const jpeg_decoder_file_stream &); + // The read() method is called when the internal input buffer is empty. + // Parameters: + // pBuf - input buffer + // max_bytes_to_read - maximum bytes that can be written to pBuf + // pEOF_flag - set this to true if at end of stream (no more bytes remaining) + // Returns -1 on error, otherwise return the number of bytes actually written to the buffer (which may be 0). + // Notes: This method will be called in a loop until you set *pEOF_flag to true or the internal buffer is full. + virtual int read(uint8* pBuf, int max_bytes_to_read, bool* pEOF_flag) = 0; + }; - FILE *m_pFile; - bool m_eof_flag, m_error_flag; + // stdio FILE stream class. + class jpeg_decoder_file_stream : public jpeg_decoder_stream + { + jpeg_decoder_file_stream(const jpeg_decoder_file_stream&); + jpeg_decoder_file_stream& operator =(const jpeg_decoder_file_stream&); - public: - jpeg_decoder_file_stream(); - virtual ~jpeg_decoder_file_stream(); - - bool open(const char *Pfilename); - void close(); + FILE* m_pFile; + bool m_eof_flag, m_error_flag; - virtual int read(uint8 *pBuf, int max_bytes_to_read, bool *pEOF_flag); - }; + public: + jpeg_decoder_file_stream(); + virtual ~jpeg_decoder_file_stream(); - // Memory stream class. - class jpeg_decoder_mem_stream : public jpeg_decoder_stream - { - const uint8 *m_pSrc_data; - uint m_ofs, m_size; + bool open(const char* Pfilename); + void close(); - public: - jpeg_decoder_mem_stream() : m_pSrc_data(NULL), m_ofs(0), m_size(0) { } - jpeg_decoder_mem_stream(const uint8 *pSrc_data, uint size) : m_pSrc_data(pSrc_data), m_ofs(0), m_size(size) { } + virtual int read(uint8* pBuf, int max_bytes_to_read, bool* pEOF_flag); + }; - virtual ~jpeg_decoder_mem_stream() { } + // Memory stream class. + class jpeg_decoder_mem_stream : public jpeg_decoder_stream + { + const uint8* m_pSrc_data; + uint m_ofs, m_size; - bool open(const uint8 *pSrc_data, uint size); - void close() { m_pSrc_data = NULL; m_ofs = 0; m_size = 0; } - - virtual int read(uint8 *pBuf, int max_bytes_to_read, bool *pEOF_flag); - }; + public: + jpeg_decoder_mem_stream() : m_pSrc_data(NULL), m_ofs(0), m_size(0) { } + jpeg_decoder_mem_stream(const uint8* pSrc_data, uint size) : m_pSrc_data(pSrc_data), m_ofs(0), m_size(size) { } - // Loads JPEG file from a jpeg_decoder_stream. - unsigned char *decompress_jpeg_image_from_stream(jpeg_decoder_stream *pStream, int *width, int *height, int *actual_comps, int req_comps); + virtual ~jpeg_decoder_mem_stream() { } - enum - { - JPGD_IN_BUF_SIZE = 8192, JPGD_MAX_BLOCKS_PER_MCU = 10, JPGD_MAX_HUFF_TABLES = 8, JPGD_MAX_QUANT_TABLES = 4, - JPGD_MAX_COMPONENTS = 4, JPGD_MAX_COMPS_IN_SCAN = 4, JPGD_MAX_BLOCKS_PER_ROW = 8192, JPGD_MAX_HEIGHT = 16384, JPGD_MAX_WIDTH = 16384 - }; - - typedef int16 jpgd_quant_t; - typedef int16 jpgd_block_t; + bool open(const uint8* pSrc_data, uint size); + void close() { m_pSrc_data = NULL; m_ofs = 0; m_size = 0; } - class jpeg_decoder - { - public: - // Call get_error_code() after constructing to determine if the stream is valid or not. You may call the get_width(), get_height(), etc. - // methods after the constructor is called. You may then either destruct the object, or begin decoding the image by calling begin_decoding(), then decode() on each scanline. - jpeg_decoder(jpeg_decoder_stream *pStream); + virtual int read(uint8* pBuf, int max_bytes_to_read, bool* pEOF_flag); + }; - ~jpeg_decoder(); + // Loads JPEG file from a jpeg_decoder_stream. + unsigned char* decompress_jpeg_image_from_stream(jpeg_decoder_stream* pStream, int* width, int* height, int* actual_comps, int req_comps, uint32_t flags = 0); - // Call this method after constructing the object to begin decompression. - // If JPGD_SUCCESS is returned you may then call decode() on each scanline. - int begin_decoding(); + enum + { + JPGD_IN_BUF_SIZE = 8192, JPGD_MAX_BLOCKS_PER_MCU = 10, JPGD_MAX_HUFF_TABLES = 8, JPGD_MAX_QUANT_TABLES = 4, + JPGD_MAX_COMPONENTS = 4, JPGD_MAX_COMPS_IN_SCAN = 4, JPGD_MAX_BLOCKS_PER_ROW = 16384, JPGD_MAX_HEIGHT = 32768, JPGD_MAX_WIDTH = 32768 + }; - // Returns the next scan line. - // For grayscale images, pScan_line will point to a buffer containing 8-bit pixels (get_bytes_per_pixel() will return 1). - // Otherwise, it will always point to a buffer containing 32-bit RGBA pixels (A will always be 255, and get_bytes_per_pixel() will return 4). - // Returns JPGD_SUCCESS if a scan line has been returned. - // Returns JPGD_DONE if all scan lines have been returned. - // Returns JPGD_FAILED if an error occurred. Call get_error_code() for a more info. - int decode(const void** pScan_line, uint* pScan_line_len); - - inline jpgd_status get_error_code() const { return m_error_code; } + typedef int16 jpgd_quant_t; + typedef int16 jpgd_block_coeff_t; - inline int get_width() const { return m_image_x_size; } - inline int get_height() const { return m_image_y_size; } + class jpeg_decoder + { + public: + enum + { + cFlagBoxChromaFiltering = 1, + cFlagDisableSIMD = 2 + }; - inline int get_num_components() const { return m_comps_in_frame; } + // Call get_error_code() after constructing to determine if the stream is valid or not. You may call the get_width(), get_height(), etc. + // methods after the constructor is called. You may then either destruct the object, or begin decoding the image by calling begin_decoding(), then decode() on each scanline. + jpeg_decoder(jpeg_decoder_stream* pStream, uint32_t flags = 0); - inline int get_bytes_per_pixel() const { return m_dest_bytes_per_pixel; } - inline int get_bytes_per_scan_line() const { return m_image_x_size * get_bytes_per_pixel(); } + ~jpeg_decoder(); - // Returns the total number of bytes actually consumed by the decoder (which should equal the actual size of the JPEG file). - inline int get_total_bytes_read() const { return m_total_bytes_read; } - - private: - jpeg_decoder(const jpeg_decoder &); - jpeg_decoder &operator =(const jpeg_decoder &); + // Call this method after constructing the object to begin decompression. + // If JPGD_SUCCESS is returned you may then call decode() on each scanline. - typedef void (*pDecode_block_func)(jpeg_decoder *, int, int, int); + int begin_decoding(); - struct huff_tables - { - bool ac_table; - uint look_up[256]; - uint look_up2[256]; - uint8 code_size[256]; - uint tree[512]; - }; + // Returns the next scan line. + // For grayscale images, pScan_line will point to a buffer containing 8-bit pixels (get_bytes_per_pixel() will return 1). + // Otherwise, it will always point to a buffer containing 32-bit RGBA pixels (A will always be 255, and get_bytes_per_pixel() will return 4). + // Returns JPGD_SUCCESS if a scan line has been returned. + // Returns JPGD_DONE if all scan lines have been returned. + // Returns JPGD_FAILED if an error occurred. Call get_error_code() for a more info. + int decode(const void** pScan_line, uint* pScan_line_len); - struct coeff_buf - { - uint8 *pData; - int block_num_x, block_num_y; - int block_len_x, block_len_y; - int block_size; - }; + inline jpgd_status get_error_code() const { return m_error_code; } - struct mem_block - { - mem_block *m_pNext; - size_t m_used_count; - size_t m_size; - char m_data[1]; - }; + inline int get_width() const { return m_image_x_size; } + inline int get_height() const { return m_image_y_size; } - jmp_buf m_jmp_state; - mem_block *m_pMem_blocks; - int m_image_x_size; - int m_image_y_size; - jpeg_decoder_stream *m_pStream; - int m_progressive_flag; - uint8 m_huff_ac[JPGD_MAX_HUFF_TABLES]; - uint8* m_huff_num[JPGD_MAX_HUFF_TABLES]; // pointer to number of Huffman codes per bit size - uint8* m_huff_val[JPGD_MAX_HUFF_TABLES]; // pointer to Huffman codes per bit size - jpgd_quant_t* m_quant[JPGD_MAX_QUANT_TABLES]; // pointer to quantization tables - int m_scan_type; // Gray, Yh1v1, Yh1v2, Yh2v1, Yh2v2 (CMYK111, CMYK4114 no longer supported) - int m_comps_in_frame; // # of components in frame - int m_comp_h_samp[JPGD_MAX_COMPONENTS]; // component's horizontal sampling factor - int m_comp_v_samp[JPGD_MAX_COMPONENTS]; // component's vertical sampling factor - int m_comp_quant[JPGD_MAX_COMPONENTS]; // component's quantization table selector - int m_comp_ident[JPGD_MAX_COMPONENTS]; // component's ID - int m_comp_h_blocks[JPGD_MAX_COMPONENTS]; - int m_comp_v_blocks[JPGD_MAX_COMPONENTS]; - int m_comps_in_scan; // # of components in scan - int m_comp_list[JPGD_MAX_COMPS_IN_SCAN]; // components in this scan - int m_comp_dc_tab[JPGD_MAX_COMPONENTS]; // component's DC Huffman coding table selector - int m_comp_ac_tab[JPGD_MAX_COMPONENTS]; // component's AC Huffman coding table selector - int m_spectral_start; // spectral selection start - int m_spectral_end; // spectral selection end - int m_successive_low; // successive approximation low - int m_successive_high; // successive approximation high - int m_max_mcu_x_size; // MCU's max. X size in pixels - int m_max_mcu_y_size; // MCU's max. Y size in pixels - int m_blocks_per_mcu; - int m_max_blocks_per_row; - int m_mcus_per_row, m_mcus_per_col; - int m_mcu_org[JPGD_MAX_BLOCKS_PER_MCU]; - int m_total_lines_left; // total # lines left in image - int m_mcu_lines_left; // total # lines left in this MCU - int m_real_dest_bytes_per_scan_line; - int m_dest_bytes_per_scan_line; // rounded up - int m_dest_bytes_per_pixel; // 4 (RGB) or 1 (Y) - huff_tables* m_pHuff_tabs[JPGD_MAX_HUFF_TABLES]; - coeff_buf* m_dc_coeffs[JPGD_MAX_COMPONENTS]; - coeff_buf* m_ac_coeffs[JPGD_MAX_COMPONENTS]; - int m_eob_run; - int m_block_y_mcu[JPGD_MAX_COMPONENTS]; - uint8* m_pIn_buf_ofs; - int m_in_buf_left; - int m_tem_flag; - bool m_eof_flag; - uint8 m_in_buf_pad_start[128]; - uint8 m_in_buf[JPGD_IN_BUF_SIZE + 128]; - uint8 m_in_buf_pad_end[128]; - int m_bits_left; - uint m_bit_buf; - int m_restart_interval; - int m_restarts_left; - int m_next_restart_num; - int m_max_mcus_per_row; - int m_max_blocks_per_mcu; - int m_expanded_blocks_per_mcu; - int m_expanded_blocks_per_row; - int m_expanded_blocks_per_component; - bool m_freq_domain_chroma_upsample; - int m_max_mcus_per_col; - uint m_last_dc_val[JPGD_MAX_COMPONENTS]; - jpgd_block_t* m_pMCU_coefficients; - int m_mcu_block_max_zag[JPGD_MAX_BLOCKS_PER_MCU]; - uint8* m_pSample_buf; - int m_crr[256]; - int m_cbb[256]; - int m_crg[256]; - int m_cbg[256]; - uint8* m_pScan_line_0; - uint8* m_pScan_line_1; - jpgd_status m_error_code; - bool m_ready_flag; - int m_total_bytes_read; + inline int get_num_components() const { return m_comps_in_frame; } + + inline int get_bytes_per_pixel() const { return m_dest_bytes_per_pixel; } + inline int get_bytes_per_scan_line() const { return m_image_x_size * get_bytes_per_pixel(); } + + // Returns the total number of bytes actually consumed by the decoder (which should equal the actual size of the JPEG file). + inline int get_total_bytes_read() const { return m_total_bytes_read; } + + private: + jpeg_decoder(const jpeg_decoder&); + jpeg_decoder& operator =(const jpeg_decoder&); + + typedef void (*pDecode_block_func)(jpeg_decoder*, int, int, int); + + struct huff_tables + { + bool ac_table; + uint look_up[256]; + uint look_up2[256]; + uint8 code_size[JPGD_HUFF_CODE_SIZE_MAX_LENGTH]; + uint tree[JPGD_HUFF_TREE_MAX_LENGTH]; + }; + + struct coeff_buf + { + uint8* pData; + int block_num_x, block_num_y; + int block_len_x, block_len_y; + int block_size; + }; + + struct mem_block + { + mem_block* m_pNext; + size_t m_used_count; + size_t m_size; + char m_data[1]; + }; + + jmp_buf m_jmp_state; + uint32_t m_flags; + mem_block* m_pMem_blocks; + int m_image_x_size; + int m_image_y_size; + jpeg_decoder_stream* m_pStream; + + int m_progressive_flag; + + uint8 m_huff_ac[JPGD_MAX_HUFF_TABLES]; + uint8* m_huff_num[JPGD_MAX_HUFF_TABLES]; // pointer to number of Huffman codes per bit size + uint8* m_huff_val[JPGD_MAX_HUFF_TABLES]; // pointer to Huffman codes per bit size + jpgd_quant_t* m_quant[JPGD_MAX_QUANT_TABLES]; // pointer to quantization tables + int m_scan_type; // Gray, Yh1v1, Yh1v2, Yh2v1, Yh2v2 (CMYK111, CMYK4114 no longer supported) + int m_comps_in_frame; // # of components in frame + int m_comp_h_samp[JPGD_MAX_COMPONENTS]; // component's horizontal sampling factor + int m_comp_v_samp[JPGD_MAX_COMPONENTS]; // component's vertical sampling factor + int m_comp_quant[JPGD_MAX_COMPONENTS]; // component's quantization table selector + int m_comp_ident[JPGD_MAX_COMPONENTS]; // component's ID + int m_comp_h_blocks[JPGD_MAX_COMPONENTS]; + int m_comp_v_blocks[JPGD_MAX_COMPONENTS]; + int m_comps_in_scan; // # of components in scan + int m_comp_list[JPGD_MAX_COMPS_IN_SCAN]; // components in this scan + int m_comp_dc_tab[JPGD_MAX_COMPONENTS]; // component's DC Huffman coding table selector + int m_comp_ac_tab[JPGD_MAX_COMPONENTS]; // component's AC Huffman coding table selector + int m_spectral_start; // spectral selection start + int m_spectral_end; // spectral selection end + int m_successive_low; // successive approximation low + int m_successive_high; // successive approximation high + int m_max_mcu_x_size; // MCU's max. X size in pixels + int m_max_mcu_y_size; // MCU's max. Y size in pixels + int m_blocks_per_mcu; + int m_max_blocks_per_row; + int m_mcus_per_row, m_mcus_per_col; + int m_mcu_org[JPGD_MAX_BLOCKS_PER_MCU]; + int m_total_lines_left; // total # lines left in image + int m_mcu_lines_left; // total # lines left in this MCU + int m_num_buffered_scanlines; + int m_real_dest_bytes_per_scan_line; + int m_dest_bytes_per_scan_line; // rounded up + int m_dest_bytes_per_pixel; // 4 (RGB) or 1 (Y) + huff_tables* m_pHuff_tabs[JPGD_MAX_HUFF_TABLES]; + coeff_buf* m_dc_coeffs[JPGD_MAX_COMPONENTS]; + coeff_buf* m_ac_coeffs[JPGD_MAX_COMPONENTS]; + int m_eob_run; + int m_block_y_mcu[JPGD_MAX_COMPONENTS]; + uint8* m_pIn_buf_ofs; + int m_in_buf_left; + int m_tem_flag; + + uint8 m_in_buf_pad_start[64]; + uint8 m_in_buf[JPGD_IN_BUF_SIZE + 128]; + uint8 m_in_buf_pad_end[64]; + + int m_bits_left; + uint m_bit_buf; + int m_restart_interval; + int m_restarts_left; + int m_next_restart_num; + int m_max_mcus_per_row; + int m_max_blocks_per_mcu; + + int m_max_mcus_per_col; + uint m_last_dc_val[JPGD_MAX_COMPONENTS]; + jpgd_block_coeff_t* m_pMCU_coefficients; + int m_mcu_block_max_zag[JPGD_MAX_BLOCKS_PER_MCU]; + uint8* m_pSample_buf; + uint8* m_pSample_buf_prev; + int m_crr[256]; + int m_cbb[256]; + int m_crg[256]; + int m_cbg[256]; + uint8* m_pScan_line_0; + uint8* m_pScan_line_1; + jpgd_status m_error_code; + int m_total_bytes_read; + + bool m_ready_flag; + bool m_eof_flag; + bool m_sample_buf_prev_valid; + bool m_has_sse2; + + inline int check_sample_buf_ofs(int ofs) const { assert(ofs >= 0); assert(ofs < m_max_blocks_per_row * 64); return ofs; } + void free_all_blocks(); + JPGD_NORETURN void stop_decoding(jpgd_status status); + void* alloc(size_t n, bool zero = false); + void* alloc_aligned(size_t nSize, uint32_t align = 16, bool zero = false); + void word_clear(void* p, uint16 c, uint n); + void prep_in_buffer(); + void read_dht_marker(); + void read_dqt_marker(); + void read_sof_marker(); + void skip_variable_marker(); + void read_dri_marker(); + void read_sos_marker(); + int next_marker(); + int process_markers(); + void locate_soi_marker(); + void locate_sof_marker(); + int locate_sos_marker(); + void init(jpeg_decoder_stream* pStream, uint32_t flags); + void create_look_ups(); + void fix_in_buffer(); + void transform_mcu(int mcu_row); + coeff_buf* coeff_buf_open(int block_num_x, int block_num_y, int block_len_x, int block_len_y); + inline jpgd_block_coeff_t* coeff_buf_getp(coeff_buf* cb, int block_x, int block_y); + void load_next_row(); + void decode_next_row(); + void make_huff_table(int index, huff_tables* pH); + void check_quant_tables(); + void check_huff_tables(); + bool calc_mcu_block_order(); + int init_scan(); + void init_frame(); + void process_restart(); + void decode_scan(pDecode_block_func decode_block_func); + void init_progressive(); + void init_sequential(); + void decode_start(); + void decode_init(jpeg_decoder_stream* pStream, uint32_t flags); + void H2V2Convert(); + uint32_t H2V2ConvertFiltered(); + void H2V1Convert(); + void H2V1ConvertFiltered(); + void H1V2Convert(); + void H1V2ConvertFiltered(); + void H1V1Convert(); + void gray_convert(); + void find_eoi(); + inline uint get_char(); + inline uint get_char(bool* pPadding_flag); + inline void stuff_char(uint8 q); + inline uint8 get_octet(); + inline uint get_bits(int num_bits); + inline uint get_bits_no_markers(int numbits); + inline int huff_decode(huff_tables* pH); + inline int huff_decode(huff_tables* pH, int& extrabits); + + // Clamps a value between 0-255. + static inline uint8 clamp(int i) + { + if (static_cast(i) > 255) + i = (((~i) >> 31) & 0xFF); + return static_cast(i); + } + int decode_next_mcu_row(); + + static void decode_block_dc_first(jpeg_decoder* pD, int component_id, int block_x, int block_y); + static void decode_block_dc_refine(jpeg_decoder* pD, int component_id, int block_x, int block_y); + static void decode_block_ac_first(jpeg_decoder* pD, int component_id, int block_x, int block_y); + static void decode_block_ac_refine(jpeg_decoder* pD, int component_id, int block_x, int block_y); + }; - void free_all_blocks(); - JPGD_NORETURN void stop_decoding(jpgd_status status); - void *alloc(size_t n, bool zero = false); - void word_clear(void *p, uint16 c, uint n); - void prep_in_buffer(); - void read_dht_marker(); - void read_dqt_marker(); - void read_sof_marker(); - void skip_variable_marker(); - void read_dri_marker(); - void read_sos_marker(); - int next_marker(); - int process_markers(); - void locate_soi_marker(); - void locate_sof_marker(); - int locate_sos_marker(); - void init(jpeg_decoder_stream * pStream); - void create_look_ups(); - void fix_in_buffer(); - void transform_mcu(int mcu_row); - void transform_mcu_expand(int mcu_row); - coeff_buf* coeff_buf_open(int block_num_x, int block_num_y, int block_len_x, int block_len_y); - inline jpgd_block_t *coeff_buf_getp(coeff_buf *cb, int block_x, int block_y); - void load_next_row(); - void decode_next_row(); - void make_huff_table(int index, huff_tables *pH); - void check_quant_tables(); - void check_huff_tables(); - void calc_mcu_block_order(); - int init_scan(); - void init_frame(); - void process_restart(); - void decode_scan(pDecode_block_func decode_block_func); - void init_progressive(); - void init_sequential(); - void decode_start(); - void decode_init(jpeg_decoder_stream * pStream); - void H2V2Convert(); - void H2V1Convert(); - void H1V2Convert(); - void H1V1Convert(); - void gray_convert(); - void expanded_convert(); - void find_eoi(); - inline uint get_char(); - inline uint get_char(bool *pPadding_flag); - inline void stuff_char(uint8 q); - inline uint8 get_octet(); - inline uint get_bits(int num_bits); - inline uint get_bits_no_markers(int numbits); - inline int huff_decode(huff_tables *pH); - inline int huff_decode(huff_tables *pH, int& extrabits); - static inline uint8 clamp(int i); - static void decode_block_dc_first(jpeg_decoder *pD, int component_id, int block_x, int block_y); - static void decode_block_dc_refine(jpeg_decoder *pD, int component_id, int block_x, int block_y); - static void decode_block_ac_first(jpeg_decoder *pD, int component_id, int block_x, int block_y); - static void decode_block_ac_refine(jpeg_decoder *pD, int component_id, int block_x, int block_y); - }; - } // namespace jpgd #endif // JPEG_DECODER_H diff --git a/ext/jpge/jpgd_idct.h b/ext/jpge/jpgd_idct.h new file mode 100644 index 0000000000..876425a959 --- /dev/null +++ b/ext/jpge/jpgd_idct.h @@ -0,0 +1,462 @@ +// Copyright 2009 Intel Corporation +// All Rights Reserved +// +// Permission is granted to use, copy, distribute and prepare derivative works of this +// software for any purpose and without fee, provided, that the above copyright notice +// and this statement appear in all copies. Intel makes no representations about the +// suitability of this software for any purpose. THIS SOFTWARE IS PROVIDED "AS IS." +// INTEL SPECIFICALLY DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, AND ALL LIABILITY, +// INCLUDING CONSEQUENTIAL AND OTHER INDIRECT DAMAGES, FOR THE USE OF THIS SOFTWARE, +// INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PROPRIETARY RIGHTS, AND INCLUDING THE +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. Intel does not +// assume any responsibility for any errors which may appear in this software nor any +// responsibility to update it. +// +// From: +// https://software.intel.com/sites/default/files/m/d/4/1/d/8/UsingIntelAVXToImplementIDCT-r1_5.pdf +// https://software.intel.com/file/29048 +// +// Requires SSE +// +#ifdef _MSC_VER +#include +#endif +#include + +#ifdef _MSC_VER + #define JPGD_SIMD_ALIGN(type, name) __declspec(align(16)) type name +#else + #define JPGD_SIMD_ALIGN(type, name) type name __attribute__((aligned(16))) +#endif + +#define BITS_INV_ACC 4 +#define SHIFT_INV_ROW 16 - BITS_INV_ACC +#define SHIFT_INV_COL 1 + BITS_INV_ACC +const short IRND_INV_ROW = 1024 * (6 - BITS_INV_ACC); //1 << (SHIFT_INV_ROW-1) +const short IRND_INV_COL = 16 * (BITS_INV_ACC - 3); // 1 << (SHIFT_INV_COL-1) +const short IRND_INV_CORR = IRND_INV_COL - 1; // correction -1.0 and round + +JPGD_SIMD_ALIGN(short, shortM128_one_corr[8]) = {1, 1, 1, 1, 1, 1, 1, 1}; +JPGD_SIMD_ALIGN(short, shortM128_round_inv_row[8]) = {IRND_INV_ROW, 0, IRND_INV_ROW, 0, IRND_INV_ROW, 0, IRND_INV_ROW, 0}; +JPGD_SIMD_ALIGN(short, shortM128_round_inv_col[8]) = {IRND_INV_COL, IRND_INV_COL, IRND_INV_COL, IRND_INV_COL, IRND_INV_COL, IRND_INV_COL, IRND_INV_COL, IRND_INV_COL}; +JPGD_SIMD_ALIGN(short, shortM128_round_inv_corr[8])= {IRND_INV_CORR, IRND_INV_CORR, IRND_INV_CORR, IRND_INV_CORR, IRND_INV_CORR, IRND_INV_CORR, IRND_INV_CORR, IRND_INV_CORR}; +JPGD_SIMD_ALIGN(short, shortM128_tg_1_16[8]) = {13036, 13036, 13036, 13036, 13036, 13036, 13036, 13036}; // tg * (2<<16) + 0.5 +JPGD_SIMD_ALIGN(short, shortM128_tg_2_16[8]) = {27146, 27146, 27146, 27146, 27146, 27146, 27146, 27146}; // tg * (2<<16) + 0.5 +JPGD_SIMD_ALIGN(short, shortM128_tg_3_16[8]) = {-21746, -21746, -21746, -21746, -21746, -21746, -21746, -21746}; // tg * (2<<16) + 0.5 +JPGD_SIMD_ALIGN(short, shortM128_cos_4_16[8]) = {-19195, -19195, -19195, -19195, -19195, -19195, -19195, -19195};// cos * (2<<16) + 0.5 + +//----------------------------------------------------------------------------- +// Table for rows 0,4 - constants are multiplied on cos_4_16 +// w15 w14 w11 w10 w07 w06 w03 w02 +// w29 w28 w25 w24 w21 w20 w17 w16 +// w31 w30 w27 w26 w23 w22 w19 w18 +//movq -> w05 w04 w01 w00 +JPGD_SIMD_ALIGN(short, shortM128_tab_i_04[]) = { + 16384, 21407, 16384, 8867, + 16384, -8867, 16384, -21407, // w13 w12 w09 w08 + 16384, 8867, -16384, -21407, // w07 w06 w03 w02 + -16384, 21407, 16384, -8867, // w15 w14 w11 w10 + 22725, 19266, 19266, -4520, // w21 w20 w17 w16 + 12873, -22725, 4520, -12873, // w29 w28 w25 w24 + 12873, 4520, -22725, -12873, // w23 w22 w19 w18 + 4520, 19266, 19266, -22725}; // w31 w30 w27 w26 + + // Table for rows 1,7 - constants are multiplied on cos_1_16 +//movq -> w05 w04 w01 w00 +JPGD_SIMD_ALIGN(short, shortM128_tab_i_17[]) = { + 22725, 29692, 22725, 12299, + 22725, -12299, 22725, -29692, // w13 w12 w09 w08 + 22725, 12299, -22725, -29692, // w07 w06 w03 w02 + -22725, 29692, 22725, -12299, // w15 w14 w11 w10 + 31521, 26722, 26722, -6270, // w21 w20 w17 w16 + 17855, -31521, 6270, -17855, // w29 w28 w25 w24 + 17855, 6270, -31521, -17855, // w23 w22 w19 w18 + 6270, 26722, 26722, -31521}; // w31 w30 w27 w26 + +// Table for rows 2,6 - constants are multiplied on cos_2_16 +//movq -> w05 w04 w01 w00 +JPGD_SIMD_ALIGN(short, shortM128_tab_i_26[]) = { + 21407, 27969, 21407, 11585, + 21407, -11585, 21407, -27969, // w13 w12 w09 w08 + 21407, 11585, -21407, -27969, // w07 w06 w03 w02 + -21407, 27969, 21407, -11585, // w15 w14 w11 w10 + 29692, 25172, 25172, -5906, // w21 w20 w17 w16 + 16819, -29692, 5906, -16819, // w29 w28 w25 w24 + 16819, 5906, -29692, -16819, // w23 w22 w19 w18 + 5906, 25172, 25172, -29692}; // w31 w30 w27 w26 +// Table for rows 3,5 - constants are multiplied on cos_3_16 +//movq -> w05 w04 w01 w00 +JPGD_SIMD_ALIGN(short, shortM128_tab_i_35[]) = { + 19266, 25172, 19266, 10426, + 19266, -10426, 19266, -25172, // w13 w12 w09 w08 + 19266, 10426, -19266, -25172, // w07 w06 w03 w02 + -19266, 25172, 19266, -10426, // w15 w14 w11 w10 + 26722, 22654, 22654, -5315, // w21 w20 w17 w16 + 15137, -26722, 5315, -15137, // w29 w28 w25 w24 + 15137, 5315, -26722, -15137, // w23 w22 w19 w18 + 5315, 22654, 22654, -26722}; // w31 w30 w27 w26 + +JPGD_SIMD_ALIGN(short, shortM128_128[8]) = { 128, 128, 128, 128, 128, 128, 128, 128 }; + +void idctSSEShortU8(const short *pInput, uint8_t * pOutputUB) +{ + __m128i r_xmm0, r_xmm4; + __m128i r_xmm1, r_xmm2, r_xmm3, r_xmm5, r_xmm6, r_xmm7; + __m128i row0, row1, row2, row3, row4, row5, row6, row7; + short * pTab_i_04 = shortM128_tab_i_04; + short * pTab_i_26 = shortM128_tab_i_26; + + //Get pointers for this input and output + pTab_i_04 = shortM128_tab_i_04; + pTab_i_26 = shortM128_tab_i_26; + + //Row 1 and Row 3 + r_xmm0 = _mm_load_si128((__m128i *) pInput); + r_xmm4 = _mm_load_si128((__m128i *) (&pInput[2*8])); + + // *** Work on the data in xmm0 + //low shuffle mask = 0xd8 = 11 01 10 00 + //get short 2 and short 0 into ls 32-bits + r_xmm0 = _mm_shufflelo_epi16(r_xmm0, 0xd8); + + // copy short 2 and short 0 to all locations + r_xmm1 = _mm_shuffle_epi32(r_xmm0, 0); + + // add to those copies + r_xmm1 = _mm_madd_epi16(r_xmm1, *((__m128i *) pTab_i_04)); + + // shuffle mask = 0x55 = 01 01 01 01 + // copy short 3 and short 1 to all locations + r_xmm3 = _mm_shuffle_epi32(r_xmm0, 0x55); + + // high shuffle mask = 0xd8 = 11 01 10 00 + // get short 6 and short 4 into bit positions 64-95 + // get short 7 and short 5 into bit positions 96-127 + r_xmm0 = _mm_shufflehi_epi16(r_xmm0, 0xd8); + + // add to short 3 and short 1 + r_xmm3 = _mm_madd_epi16(r_xmm3, *((__m128i *) &pTab_i_04[16])); + + // shuffle mask = 0xaa = 10 10 10 10 + // copy short 6 and short 4 to all locations + r_xmm2 = _mm_shuffle_epi32(r_xmm0, 0xaa); + + // shuffle mask = 0xaa = 11 11 11 11 + // copy short 7 and short 5 to all locations + r_xmm0 = _mm_shuffle_epi32(r_xmm0, 0xff); + + // add to short 6 and short 4 + r_xmm2 = _mm_madd_epi16(r_xmm2, *((__m128i *) &pTab_i_04[8])); + + // *** Work on the data in xmm4 + // high shuffle mask = 0xd8 11 01 10 00 + // get short 6 and short 4 into bit positions 64-95 + // get short 7 and short 5 into bit positions 96-127 + r_xmm4 = _mm_shufflehi_epi16(r_xmm4, 0xd8); + + // (xmm0 short 2 and short 0 plus pSi) + some constants + r_xmm1 = _mm_add_epi32(r_xmm1, *((__m128i *) shortM128_round_inv_row)); + r_xmm4 = _mm_shufflelo_epi16(r_xmm4, 0xd8); + r_xmm0 = _mm_madd_epi16(r_xmm0, *((__m128i *) &pTab_i_04[24])); + r_xmm5 = _mm_shuffle_epi32(r_xmm4, 0); + r_xmm6 = _mm_shuffle_epi32(r_xmm4, 0xaa); + r_xmm5 = _mm_madd_epi16(r_xmm5, *((__m128i *) &shortM128_tab_i_26[0])); + r_xmm1 = _mm_add_epi32(r_xmm1, r_xmm2); + r_xmm2 = r_xmm1; + r_xmm7 = _mm_shuffle_epi32(r_xmm4, 0x55); + r_xmm6 = _mm_madd_epi16(r_xmm6, *((__m128i *) &shortM128_tab_i_26[8])); + r_xmm0 = _mm_add_epi32(r_xmm0, r_xmm3); + r_xmm4 = _mm_shuffle_epi32(r_xmm4, 0xff); + r_xmm2 = _mm_sub_epi32(r_xmm2, r_xmm0); + r_xmm7 = _mm_madd_epi16(r_xmm7, *((__m128i *) &shortM128_tab_i_26[16])); + r_xmm0 = _mm_add_epi32(r_xmm0, r_xmm1); + r_xmm2 = _mm_srai_epi32(r_xmm2, 12); + r_xmm5 = _mm_add_epi32(r_xmm5, *((__m128i *) shortM128_round_inv_row)); + r_xmm4 = _mm_madd_epi16(r_xmm4, *((__m128i *) &shortM128_tab_i_26[24])); + r_xmm5 = _mm_add_epi32(r_xmm5, r_xmm6); + r_xmm6 = r_xmm5; + r_xmm0 = _mm_srai_epi32(r_xmm0, 12); + r_xmm2 = _mm_shuffle_epi32(r_xmm2, 0x1b); + row0 = _mm_packs_epi32(r_xmm0, r_xmm2); + r_xmm4 = _mm_add_epi32(r_xmm4, r_xmm7); + r_xmm6 = _mm_sub_epi32(r_xmm6, r_xmm4); + r_xmm4 = _mm_add_epi32(r_xmm4, r_xmm5); + r_xmm6 = _mm_srai_epi32(r_xmm6, 12); + r_xmm4 = _mm_srai_epi32(r_xmm4, 12); + r_xmm6 = _mm_shuffle_epi32(r_xmm6, 0x1b); + row2 = _mm_packs_epi32(r_xmm4, r_xmm6); + + //Row 5 and row 7 + r_xmm0 = _mm_load_si128((__m128i *) (&pInput[4*8])); + r_xmm4 = _mm_load_si128((__m128i *) (&pInput[6*8])); + + r_xmm0 = _mm_shufflelo_epi16(r_xmm0, 0xd8); + r_xmm1 = _mm_shuffle_epi32(r_xmm0, 0); + r_xmm1 = _mm_madd_epi16(r_xmm1, *((__m128i *) pTab_i_04)); + r_xmm3 = _mm_shuffle_epi32(r_xmm0, 0x55); + r_xmm0 = _mm_shufflehi_epi16(r_xmm0, 0xd8); + r_xmm3 = _mm_madd_epi16(r_xmm3, *((__m128i *) &pTab_i_04[16])); + r_xmm2 = _mm_shuffle_epi32(r_xmm0, 0xaa); + r_xmm0 = _mm_shuffle_epi32(r_xmm0, 0xff); + r_xmm2 = _mm_madd_epi16(r_xmm2, *((__m128i *) &pTab_i_04[8])); + r_xmm4 = _mm_shufflehi_epi16(r_xmm4, 0xd8); + r_xmm1 = _mm_add_epi32(r_xmm1, *((__m128i *) shortM128_round_inv_row)); + r_xmm4 = _mm_shufflelo_epi16(r_xmm4, 0xd8); + r_xmm0 = _mm_madd_epi16(r_xmm0, *((__m128i *) &pTab_i_04[24])); + r_xmm5 = _mm_shuffle_epi32(r_xmm4, 0); + r_xmm6 = _mm_shuffle_epi32(r_xmm4, 0xaa); + r_xmm5 = _mm_madd_epi16(r_xmm5, *((__m128i *) &shortM128_tab_i_26[0])); + r_xmm1 = _mm_add_epi32(r_xmm1, r_xmm2); + r_xmm2 = r_xmm1; + r_xmm7 = _mm_shuffle_epi32(r_xmm4, 0x55); + r_xmm6 = _mm_madd_epi16(r_xmm6, *((__m128i *) &shortM128_tab_i_26[8])); + r_xmm0 = _mm_add_epi32(r_xmm0, r_xmm3); + r_xmm4 = _mm_shuffle_epi32(r_xmm4, 0xff); + r_xmm2 = _mm_sub_epi32(r_xmm2, r_xmm0); + r_xmm7 = _mm_madd_epi16(r_xmm7, *((__m128i *) &shortM128_tab_i_26[16])); + r_xmm0 = _mm_add_epi32(r_xmm0, r_xmm1); + r_xmm2 = _mm_srai_epi32(r_xmm2, 12); + r_xmm5 = _mm_add_epi32(r_xmm5, *((__m128i *) shortM128_round_inv_row)); + r_xmm4 = _mm_madd_epi16(r_xmm4, *((__m128i *) &shortM128_tab_i_26[24])); + r_xmm5 = _mm_add_epi32(r_xmm5, r_xmm6); + r_xmm6 = r_xmm5; + r_xmm0 = _mm_srai_epi32(r_xmm0, 12); + r_xmm2 = _mm_shuffle_epi32(r_xmm2, 0x1b); + row4 = _mm_packs_epi32(r_xmm0, r_xmm2); + r_xmm4 = _mm_add_epi32(r_xmm4, r_xmm7); + r_xmm6 = _mm_sub_epi32(r_xmm6, r_xmm4); + r_xmm4 = _mm_add_epi32(r_xmm4, r_xmm5); + r_xmm6 = _mm_srai_epi32(r_xmm6, 12); + r_xmm4 = _mm_srai_epi32(r_xmm4, 12); + r_xmm6 = _mm_shuffle_epi32(r_xmm6, 0x1b); + row6 = _mm_packs_epi32(r_xmm4, r_xmm6); + + //Row 4 and row 2 + pTab_i_04 = shortM128_tab_i_35; + pTab_i_26 = shortM128_tab_i_17; + r_xmm0 = _mm_load_si128((__m128i *) (&pInput[3*8])); + r_xmm4 = _mm_load_si128((__m128i *) (&pInput[1*8])); + + r_xmm0 = _mm_shufflelo_epi16(r_xmm0, 0xd8); + r_xmm1 = _mm_shuffle_epi32(r_xmm0, 0); + r_xmm1 = _mm_madd_epi16(r_xmm1, *((__m128i *) pTab_i_04)); + r_xmm3 = _mm_shuffle_epi32(r_xmm0, 0x55); + r_xmm0 = _mm_shufflehi_epi16(r_xmm0, 0xd8); + r_xmm3 = _mm_madd_epi16(r_xmm3, *((__m128i *) &pTab_i_04[16])); + r_xmm2 = _mm_shuffle_epi32(r_xmm0, 0xaa); + r_xmm0 = _mm_shuffle_epi32(r_xmm0, 0xff); + r_xmm2 = _mm_madd_epi16(r_xmm2, *((__m128i *) &pTab_i_04[8])); + r_xmm4 = _mm_shufflehi_epi16(r_xmm4, 0xd8); + r_xmm1 = _mm_add_epi32(r_xmm1, *((__m128i *) shortM128_round_inv_row)); + r_xmm4 = _mm_shufflelo_epi16(r_xmm4, 0xd8); + r_xmm0 = _mm_madd_epi16(r_xmm0, *((__m128i *) &pTab_i_04[24])); + r_xmm5 = _mm_shuffle_epi32(r_xmm4, 0); + r_xmm6 = _mm_shuffle_epi32(r_xmm4, 0xaa); + r_xmm5 = _mm_madd_epi16(r_xmm5, *((__m128i *) &pTab_i_26[0])); + r_xmm1 = _mm_add_epi32(r_xmm1, r_xmm2); + r_xmm2 = r_xmm1; + r_xmm7 = _mm_shuffle_epi32(r_xmm4, 0x55); + r_xmm6 = _mm_madd_epi16(r_xmm6, *((__m128i *) &pTab_i_26[8])); + r_xmm0 = _mm_add_epi32(r_xmm0, r_xmm3); + r_xmm4 = _mm_shuffle_epi32(r_xmm4, 0xff); + r_xmm2 = _mm_sub_epi32(r_xmm2, r_xmm0); + r_xmm7 = _mm_madd_epi16(r_xmm7, *((__m128i *) &pTab_i_26[16])); + r_xmm0 = _mm_add_epi32(r_xmm0, r_xmm1); + r_xmm2 = _mm_srai_epi32(r_xmm2, 12); + r_xmm5 = _mm_add_epi32(r_xmm5, *((__m128i *) shortM128_round_inv_row)); + r_xmm4 = _mm_madd_epi16(r_xmm4, *((__m128i *) &pTab_i_26[24])); + r_xmm5 = _mm_add_epi32(r_xmm5, r_xmm6); + r_xmm6 = r_xmm5; + r_xmm0 = _mm_srai_epi32(r_xmm0, 12); + r_xmm2 = _mm_shuffle_epi32(r_xmm2, 0x1b); + row3 = _mm_packs_epi32(r_xmm0, r_xmm2); + r_xmm4 = _mm_add_epi32(r_xmm4, r_xmm7); + r_xmm6 = _mm_sub_epi32(r_xmm6, r_xmm4); + r_xmm4 = _mm_add_epi32(r_xmm4, r_xmm5); + r_xmm6 = _mm_srai_epi32(r_xmm6, 12); + r_xmm4 = _mm_srai_epi32(r_xmm4, 12); + r_xmm6 = _mm_shuffle_epi32(r_xmm6, 0x1b); + row1 = _mm_packs_epi32(r_xmm4, r_xmm6); + + //Row 6 and row 8 + r_xmm0 = _mm_load_si128((__m128i *) (&pInput[5*8])); + r_xmm4 = _mm_load_si128((__m128i *) (&pInput[7*8])); + + r_xmm0 = _mm_shufflelo_epi16(r_xmm0, 0xd8); + r_xmm1 = _mm_shuffle_epi32(r_xmm0, 0); + r_xmm1 = _mm_madd_epi16(r_xmm1, *((__m128i *) pTab_i_04)); + r_xmm3 = _mm_shuffle_epi32(r_xmm0, 0x55); + r_xmm0 = _mm_shufflehi_epi16(r_xmm0, 0xd8); + r_xmm3 = _mm_madd_epi16(r_xmm3, *((__m128i *) &pTab_i_04[16])); + r_xmm2 = _mm_shuffle_epi32(r_xmm0, 0xaa); + r_xmm0 = _mm_shuffle_epi32(r_xmm0, 0xff); + r_xmm2 = _mm_madd_epi16(r_xmm2, *((__m128i *) &pTab_i_04[8])); + r_xmm4 = _mm_shufflehi_epi16(r_xmm4, 0xd8); + r_xmm1 = _mm_add_epi32(r_xmm1, *((__m128i *) shortM128_round_inv_row)); + r_xmm4 = _mm_shufflelo_epi16(r_xmm4, 0xd8); + r_xmm0 = _mm_madd_epi16(r_xmm0, *((__m128i *) &pTab_i_04[24])); + r_xmm5 = _mm_shuffle_epi32(r_xmm4, 0); + r_xmm6 = _mm_shuffle_epi32(r_xmm4, 0xaa); + r_xmm5 = _mm_madd_epi16(r_xmm5, *((__m128i *) &pTab_i_26[0])); + r_xmm1 = _mm_add_epi32(r_xmm1, r_xmm2); + r_xmm2 = r_xmm1; + r_xmm7 = _mm_shuffle_epi32(r_xmm4, 0x55); + r_xmm6 = _mm_madd_epi16(r_xmm6, *((__m128i *) &pTab_i_26[8])); + r_xmm0 = _mm_add_epi32(r_xmm0, r_xmm3); + r_xmm4 = _mm_shuffle_epi32(r_xmm4, 0xff); + r_xmm2 = _mm_sub_epi32(r_xmm2, r_xmm0); + r_xmm7 = _mm_madd_epi16(r_xmm7, *((__m128i *) &pTab_i_26[16])); + r_xmm0 = _mm_add_epi32(r_xmm0, r_xmm1); + r_xmm2 = _mm_srai_epi32(r_xmm2, 12); + r_xmm5 = _mm_add_epi32(r_xmm5, *((__m128i *) shortM128_round_inv_row)); + r_xmm4 = _mm_madd_epi16(r_xmm4, *((__m128i *) &pTab_i_26[24])); + r_xmm5 = _mm_add_epi32(r_xmm5, r_xmm6); + r_xmm6 = r_xmm5; + r_xmm0 = _mm_srai_epi32(r_xmm0, 12); + r_xmm2 = _mm_shuffle_epi32(r_xmm2, 0x1b); + row5 = _mm_packs_epi32(r_xmm0, r_xmm2); + r_xmm4 = _mm_add_epi32(r_xmm4, r_xmm7); + r_xmm6 = _mm_sub_epi32(r_xmm6, r_xmm4); + r_xmm4 = _mm_add_epi32(r_xmm4, r_xmm5); + r_xmm6 = _mm_srai_epi32(r_xmm6, 12); + r_xmm4 = _mm_srai_epi32(r_xmm4, 12); + r_xmm6 = _mm_shuffle_epi32(r_xmm6, 0x1b); + row7 = _mm_packs_epi32(r_xmm4, r_xmm6); + + r_xmm1 = _mm_load_si128((__m128i *) shortM128_tg_3_16); + r_xmm2 = row5; + r_xmm3 = row3; + r_xmm0 = _mm_mulhi_epi16(row5, r_xmm1); + + r_xmm1 = _mm_mulhi_epi16(r_xmm1, r_xmm3); + r_xmm5 = _mm_load_si128((__m128i *) shortM128_tg_1_16); + r_xmm6 = row7; + r_xmm4 = _mm_mulhi_epi16(row7, r_xmm5); + + r_xmm0 = _mm_adds_epi16(r_xmm0, r_xmm2); + r_xmm5 = _mm_mulhi_epi16(r_xmm5, row1); + r_xmm1 = _mm_adds_epi16(r_xmm1, r_xmm3); + r_xmm7 = row6; + + r_xmm0 = _mm_adds_epi16(r_xmm0, r_xmm3); + r_xmm3 = _mm_load_si128((__m128i *) shortM128_tg_2_16); + r_xmm2 = _mm_subs_epi16(r_xmm2, r_xmm1); + r_xmm7 = _mm_mulhi_epi16(r_xmm7, r_xmm3); + r_xmm1 = r_xmm0; + r_xmm3 = _mm_mulhi_epi16(r_xmm3, row2); + r_xmm5 = _mm_subs_epi16(r_xmm5, r_xmm6); + r_xmm4 = _mm_adds_epi16(r_xmm4, row1); + r_xmm0 = _mm_adds_epi16(r_xmm0, r_xmm4); + r_xmm0 = _mm_adds_epi16(r_xmm0, *((__m128i *) shortM128_one_corr)); + r_xmm4 = _mm_subs_epi16(r_xmm4, r_xmm1); + r_xmm6 = r_xmm5; + r_xmm5 = _mm_subs_epi16(r_xmm5, r_xmm2); + r_xmm5 = _mm_adds_epi16(r_xmm5, *((__m128i *) shortM128_one_corr)); + r_xmm6 = _mm_adds_epi16(r_xmm6, r_xmm2); + + //Intermediate results, needed later + __m128i temp3, temp7; + temp7 = r_xmm0; + + r_xmm1 = r_xmm4; + r_xmm0 = _mm_load_si128((__m128i *) shortM128_cos_4_16); + r_xmm4 = _mm_adds_epi16(r_xmm4, r_xmm5); + r_xmm2 = _mm_load_si128((__m128i *) shortM128_cos_4_16); + r_xmm2 = _mm_mulhi_epi16(r_xmm2, r_xmm4); + + //Intermediate results, needed later + temp3 = r_xmm6; + + r_xmm1 = _mm_subs_epi16(r_xmm1, r_xmm5); + r_xmm7 = _mm_adds_epi16(r_xmm7, row2); + r_xmm3 = _mm_subs_epi16(r_xmm3, row6); + r_xmm6 = row0; + r_xmm0 = _mm_mulhi_epi16(r_xmm0, r_xmm1); + r_xmm5 = row4; + r_xmm5 = _mm_adds_epi16(r_xmm5, r_xmm6); + r_xmm6 = _mm_subs_epi16(r_xmm6, row4); + r_xmm4 = _mm_adds_epi16(r_xmm4, r_xmm2); + + r_xmm4 = _mm_or_si128(r_xmm4, *((__m128i *) shortM128_one_corr)); + r_xmm0 = _mm_adds_epi16(r_xmm0, r_xmm1); + r_xmm0 = _mm_or_si128(r_xmm0, *((__m128i *) shortM128_one_corr)); + + r_xmm2 = r_xmm5; + r_xmm5 = _mm_adds_epi16(r_xmm5, r_xmm7); + r_xmm1 = r_xmm6; + r_xmm5 = _mm_adds_epi16(r_xmm5, *((__m128i *) shortM128_round_inv_col)); + r_xmm2 = _mm_subs_epi16(r_xmm2, r_xmm7); + r_xmm7 = temp7; + r_xmm6 = _mm_adds_epi16(r_xmm6, r_xmm3); + r_xmm6 = _mm_adds_epi16(r_xmm6, *((__m128i *) shortM128_round_inv_col)); + r_xmm7 = _mm_adds_epi16(r_xmm7, r_xmm5); + r_xmm7 = _mm_srai_epi16(r_xmm7, SHIFT_INV_COL); + r_xmm1 = _mm_subs_epi16(r_xmm1, r_xmm3); + r_xmm1 = _mm_adds_epi16(r_xmm1, *((__m128i *) shortM128_round_inv_corr)); + r_xmm3 = r_xmm6; + r_xmm2 = _mm_adds_epi16(r_xmm2, *((__m128i *) shortM128_round_inv_corr)); + r_xmm6 = _mm_adds_epi16(r_xmm6, r_xmm4); + + //Store results for row 0 + //_mm_store_si128((__m128i *) pOutput, r_xmm7); + __m128i r0 = r_xmm7; + + r_xmm6 = _mm_srai_epi16(r_xmm6, SHIFT_INV_COL); + r_xmm7 = r_xmm1; + r_xmm1 = _mm_adds_epi16(r_xmm1, r_xmm0); + + //Store results for row 1 + //_mm_store_si128((__m128i *) (&pOutput[1*8]), r_xmm6); + __m128i r1 = r_xmm6; + + r_xmm1 = _mm_srai_epi16(r_xmm1, SHIFT_INV_COL); + r_xmm6 = temp3; + r_xmm7 = _mm_subs_epi16(r_xmm7, r_xmm0); + r_xmm7 = _mm_srai_epi16(r_xmm7, SHIFT_INV_COL); + + //Store results for row 2 + //_mm_store_si128((__m128i *) (&pOutput[2*8]), r_xmm1); + __m128i r2 = r_xmm1; + + r_xmm5 = _mm_subs_epi16(r_xmm5, temp7); + r_xmm5 = _mm_srai_epi16(r_xmm5, SHIFT_INV_COL); + + //Store results for row 7 + //_mm_store_si128((__m128i *) (&pOutput[7*8]), r_xmm5); + __m128i r7 = r_xmm5; + + r_xmm3 = _mm_subs_epi16(r_xmm3, r_xmm4); + r_xmm6 = _mm_adds_epi16(r_xmm6, r_xmm2); + r_xmm2 = _mm_subs_epi16(r_xmm2, temp3); + r_xmm6 = _mm_srai_epi16(r_xmm6, SHIFT_INV_COL); + r_xmm2 = _mm_srai_epi16(r_xmm2, SHIFT_INV_COL); + + //Store results for row 3 + //_mm_store_si128((__m128i *) (&pOutput[3*8]), r_xmm6); + __m128i r3 = r_xmm6; + + r_xmm3 = _mm_srai_epi16(r_xmm3, SHIFT_INV_COL); + + //Store results for rows 4, 5, and 6 + //_mm_store_si128((__m128i *) (&pOutput[4*8]), r_xmm2); + //_mm_store_si128((__m128i *) (&pOutput[5*8]), r_xmm7); + //_mm_store_si128((__m128i *) (&pOutput[6*8]), r_xmm3); + + __m128i r4 = r_xmm2; + __m128i r5 = r_xmm7; + __m128i r6 = r_xmm3; + + r0 = _mm_add_epi16(*(const __m128i *)shortM128_128, r0); + r1 = _mm_add_epi16(*(const __m128i *)shortM128_128, r1); + r2 = _mm_add_epi16(*(const __m128i *)shortM128_128, r2); + r3 = _mm_add_epi16(*(const __m128i *)shortM128_128, r3); + r4 = _mm_add_epi16(*(const __m128i *)shortM128_128, r4); + r5 = _mm_add_epi16(*(const __m128i *)shortM128_128, r5); + r6 = _mm_add_epi16(*(const __m128i *)shortM128_128, r6); + r7 = _mm_add_epi16(*(const __m128i *)shortM128_128, r7); + + ((__m128i *)pOutputUB)[0] = _mm_packus_epi16(r0, r1); + ((__m128i *)pOutputUB)[1] = _mm_packus_epi16(r2, r3); + ((__m128i *)pOutputUB)[2] = _mm_packus_epi16(r4, r5); + ((__m128i *)pOutputUB)[3] = _mm_packus_epi16(r6, r7); +} diff --git a/ext/jpge/jpge.cpp b/ext/jpge/jpge.cpp index 1800e500e9..d9cc1866d3 100644 --- a/ext/jpge/jpge.cpp +++ b/ext/jpge/jpge.cpp @@ -1,5 +1,24 @@ -// jpge.cpp - C++ class for JPEG compression. -// Public domain, Rich Geldreich +// jpge.cpp - C++ class for JPEG compression. Richard Geldreich +// Supports grayscale, H1V1, H2V1, and H2V2 chroma subsampling factors, one or two pass Huffman table optimization, libjpeg-style quality 1-100 quality factors. +// Also supports using luma quantization tables for chroma. +// +// Released under two licenses. You are free to choose which license you want: +// License 1: +// Public Domain +// +// License 2: +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// // v1.01, Dec. 18, 2010 - Initial release // v1.02, Apr. 6, 2011 - Removed 2x2 ordered dither in H2V1 chroma subsampling method load_block_16_8_8(). (The rounding factor was 2, when it should have been 1. Either way, it wasn't helping.) // v1.03, Apr. 16, 2011 - Added support for optimized Huffman code tables, optimized dynamic memory allocation down to only 1 alloc. @@ -7,101 +26,110 @@ // v1.04, May. 19, 2012: Forgot to set m_pFile ptr to NULL in cfile_stream::close(). Thanks to Owen Kaluza for reporting this bug. // Code tweaks to fix VS2008 static code analysis warnings (all looked harmless). // Code review revealed method load_block_16_8_8() (used for the non-default H2V1 sampling mode to downsample chroma) somehow didn't get the rounding factor fix from v1.02. +// v1.05, March 25, 2020: Added Apache 2.0 alternate license #include "jpge.h" #include #include -// Higher level wrappers/examples (optional). -#include #define JPGE_MAX(a,b) (((a)>(b))?(a):(b)) #define JPGE_MIN(a,b) (((a)<(b))?(a):(b)) namespace jpge { -static inline void *jpge_malloc(size_t nSize) { return malloc(nSize); } -static inline void jpge_free(void *p) { free(p); } + static inline void* jpge_malloc(size_t nSize) { return malloc(nSize); } + static inline void jpge_free(void* p) { free(p); } -// Various JPEG enums and tables. -enum { M_SOF0 = 0xC0, M_DHT = 0xC4, M_SOI = 0xD8, M_EOI = 0xD9, M_SOS = 0xDA, M_DQT = 0xDB, M_APP0 = 0xE0 }; -enum { DC_LUM_CODES = 12, AC_LUM_CODES = 256, DC_CHROMA_CODES = 12, AC_CHROMA_CODES = 256, MAX_HUFF_SYMBOLS = 257, MAX_HUFF_CODESIZE = 32 }; + // Various JPEG enums and tables. + enum { M_SOF0 = 0xC0, M_DHT = 0xC4, M_SOI = 0xD8, M_EOI = 0xD9, M_SOS = 0xDA, M_DQT = 0xDB, M_APP0 = 0xE0 }; + enum { DC_LUM_CODES = 12, AC_LUM_CODES = 256, DC_CHROMA_CODES = 12, AC_CHROMA_CODES = 256, MAX_HUFF_SYMBOLS = 257, MAX_HUFF_CODESIZE = 32 }; -static uint8 s_zag[64] = { 0,1,8,16,9,2,3,10,17,24,32,25,18,11,4,5,12,19,26,33,40,48,41,34,27,20,13,6,7,14,21,28,35,42,49,56,57,50,43,36,29,22,15,23,30,37,44,51,58,59,52,45,38,31,39,46,53,60,61,54,47,55,62,63 }; -static int16 s_std_lum_quant[64] = { 16,11,12,14,12,10,16,14,13,14,18,17,16,19,24,40,26,24,22,22,24,49,35,37,29,40,58,51,61,60,57,51,56,55,64,72,92,78,64,68,87,69,55,56,80,109,81,87,95,98,103,104,103,62,77,113,121,112,100,120,92,101,103,99 }; -static int16 s_std_croma_quant[64] = { 17,18,18,24,21,24,47,26,26,47,99,66,56,66,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99 }; -static uint8 s_dc_lum_bits[17] = { 0,0,1,5,1,1,1,1,1,1,0,0,0,0,0,0,0 }; -static uint8 s_dc_lum_val[DC_LUM_CODES] = { 0,1,2,3,4,5,6,7,8,9,10,11 }; -static uint8 s_ac_lum_bits[17] = { 0,0,2,1,3,3,2,4,3,5,5,4,4,0,0,1,0x7d }; -static uint8 s_ac_lum_val[AC_LUM_CODES] = -{ - 0x01,0x02,0x03,0x00,0x04,0x11,0x05,0x12,0x21,0x31,0x41,0x06,0x13,0x51,0x61,0x07,0x22,0x71,0x14,0x32,0x81,0x91,0xa1,0x08,0x23,0x42,0xb1,0xc1,0x15,0x52,0xd1,0xf0, - 0x24,0x33,0x62,0x72,0x82,0x09,0x0a,0x16,0x17,0x18,0x19,0x1a,0x25,0x26,0x27,0x28,0x29,0x2a,0x34,0x35,0x36,0x37,0x38,0x39,0x3a,0x43,0x44,0x45,0x46,0x47,0x48,0x49, - 0x4a,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5a,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6a,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7a,0x83,0x84,0x85,0x86,0x87,0x88,0x89, - 0x8a,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9a,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,0xa8,0xa9,0xaa,0xb2,0xb3,0xb4,0xb5,0xb6,0xb7,0xb8,0xb9,0xba,0xc2,0xc3,0xc4,0xc5, - 0xc6,0xc7,0xc8,0xc9,0xca,0xd2,0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda,0xe1,0xe2,0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,0xf1,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8, - 0xf9,0xfa -}; -static uint8 s_dc_chroma_bits[17] = { 0,0,3,1,1,1,1,1,1,1,1,1,0,0,0,0,0 }; -static uint8 s_dc_chroma_val[DC_CHROMA_CODES] = { 0,1,2,3,4,5,6,7,8,9,10,11 }; -static uint8 s_ac_chroma_bits[17] = { 0,0,2,1,2,4,4,3,4,7,5,4,4,0,1,2,0x77 }; -static uint8 s_ac_chroma_val[AC_CHROMA_CODES] = -{ - 0x00,0x01,0x02,0x03,0x11,0x04,0x05,0x21,0x31,0x06,0x12,0x41,0x51,0x07,0x61,0x71,0x13,0x22,0x32,0x81,0x08,0x14,0x42,0x91,0xa1,0xb1,0xc1,0x09,0x23,0x33,0x52,0xf0, - 0x15,0x62,0x72,0xd1,0x0a,0x16,0x24,0x34,0xe1,0x25,0xf1,0x17,0x18,0x19,0x1a,0x26,0x27,0x28,0x29,0x2a,0x35,0x36,0x37,0x38,0x39,0x3a,0x43,0x44,0x45,0x46,0x47,0x48, - 0x49,0x4a,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5a,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6a,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7a,0x82,0x83,0x84,0x85,0x86,0x87, - 0x88,0x89,0x8a,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9a,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,0xa8,0xa9,0xaa,0xb2,0xb3,0xb4,0xb5,0xb6,0xb7,0xb8,0xb9,0xba,0xc2,0xc3, - 0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xd2,0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda,0xe2,0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8, - 0xf9,0xfa -}; + static uint8 s_zag[64] = { 0,1,8,16,9,2,3,10,17,24,32,25,18,11,4,5,12,19,26,33,40,48,41,34,27,20,13,6,7,14,21,28,35,42,49,56,57,50,43,36,29,22,15,23,30,37,44,51,58,59,52,45,38,31,39,46,53,60,61,54,47,55,62,63 }; + static int16 s_std_lum_quant[64] = { 16,11,12,14,12,10,16,14,13,14,18,17,16,19,24,40,26,24,22,22,24,49,35,37,29,40,58,51,61,60,57,51,56,55,64,72,92,78,64,68,87,69,55,56,80,109,81,87,95,98,103,104,103,62,77,113,121,112,100,120,92,101,103,99 }; + static int16 s_std_croma_quant[64] = { 17,18,18,24,21,24,47,26,26,47,99,66,56,66,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99 }; -// Low-level helper functions. -template inline void clear_obj(T &obj) { memset(&obj, 0, sizeof(obj)); } + // Table from http://www.imagemagick.org/discourse-server/viewtopic.php?f=22&t=20333&p=98008#p98008 + // This is mozjpeg's default table, in zag order. + static int16 s_alt_quant[64] = { 16,16,16,16,17,16,18,20,20,18,25,27,24,27,25,37,34,31,31,34,37,56,40,43,40,43,40,56,85,53,62,53,53,62,53,85,75,91,74,69,74,91,75,135,106,94,94,106,135,156,131,124,131,156,189,169,169,189,238,226,238,311,311,418 }; -const int YR = 19595, YG = 38470, YB = 7471, CB_R = -11059, CB_G = -21709, CB_B = 32768, CR_R = 32768, CR_G = -27439, CR_B = -5329; -static inline uint8 clamp(int i) { if (static_cast(i) > 255U) { if (i < 0) i = 0; else if (i > 255) i = 255; } return static_cast(i); } + static uint8 s_dc_lum_bits[17] = { 0,0,1,5,1,1,1,1,1,1,0,0,0,0,0,0,0 }; + static uint8 s_dc_lum_val[DC_LUM_CODES] = { 0,1,2,3,4,5,6,7,8,9,10,11 }; + static uint8 s_ac_lum_bits[17] = { 0,0,2,1,3,3,2,4,3,5,5,4,4,0,0,1,0x7d }; + static uint8 s_ac_lum_val[AC_LUM_CODES] = + { + 0x01,0x02,0x03,0x00,0x04,0x11,0x05,0x12,0x21,0x31,0x41,0x06,0x13,0x51,0x61,0x07,0x22,0x71,0x14,0x32,0x81,0x91,0xa1,0x08,0x23,0x42,0xb1,0xc1,0x15,0x52,0xd1,0xf0, + 0x24,0x33,0x62,0x72,0x82,0x09,0x0a,0x16,0x17,0x18,0x19,0x1a,0x25,0x26,0x27,0x28,0x29,0x2a,0x34,0x35,0x36,0x37,0x38,0x39,0x3a,0x43,0x44,0x45,0x46,0x47,0x48,0x49, + 0x4a,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5a,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6a,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7a,0x83,0x84,0x85,0x86,0x87,0x88,0x89, + 0x8a,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9a,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,0xa8,0xa9,0xaa,0xb2,0xb3,0xb4,0xb5,0xb6,0xb7,0xb8,0xb9,0xba,0xc2,0xc3,0xc4,0xc5, + 0xc6,0xc7,0xc8,0xc9,0xca,0xd2,0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda,0xe1,0xe2,0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,0xf1,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8, + 0xf9,0xfa + }; + static uint8 s_dc_chroma_bits[17] = { 0,0,3,1,1,1,1,1,1,1,1,1,0,0,0,0,0 }; + static uint8 s_dc_chroma_val[DC_CHROMA_CODES] = { 0,1,2,3,4,5,6,7,8,9,10,11 }; + static uint8 s_ac_chroma_bits[17] = { 0,0,2,1,2,4,4,3,4,7,5,4,4,0,1,2,0x77 }; + static uint8 s_ac_chroma_val[AC_CHROMA_CODES] = + { + 0x00,0x01,0x02,0x03,0x11,0x04,0x05,0x21,0x31,0x06,0x12,0x41,0x51,0x07,0x61,0x71,0x13,0x22,0x32,0x81,0x08,0x14,0x42,0x91,0xa1,0xb1,0xc1,0x09,0x23,0x33,0x52,0xf0, + 0x15,0x62,0x72,0xd1,0x0a,0x16,0x24,0x34,0xe1,0x25,0xf1,0x17,0x18,0x19,0x1a,0x26,0x27,0x28,0x29,0x2a,0x35,0x36,0x37,0x38,0x39,0x3a,0x43,0x44,0x45,0x46,0x47,0x48, + 0x49,0x4a,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5a,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6a,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7a,0x82,0x83,0x84,0x85,0x86,0x87, + 0x88,0x89,0x8a,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9a,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,0xa8,0xa9,0xaa,0xb2,0xb3,0xb4,0xb5,0xb6,0xb7,0xb8,0xb9,0xba,0xc2,0xc3, + 0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xd2,0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda,0xe2,0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8, + 0xf9,0xfa + }; -static void RGB_to_YCC(uint8* pDst, const uint8 *pSrc, int num_pixels) -{ - for ( ; num_pixels; pDst += 3, pSrc += 3, num_pixels--) - { - const int r = pSrc[0], g = pSrc[1], b = pSrc[2]; - pDst[0] = static_cast((r * YR + g * YG + b * YB + 32768) >> 16); - pDst[1] = clamp(128 + ((r * CB_R + g * CB_G + b * CB_B + 32768) >> 16)); - pDst[2] = clamp(128 + ((r * CR_R + g * CR_G + b * CR_B + 32768) >> 16)); - } -} + // Low-level helper functions. + template inline void clear_obj(T& obj) { memset(&obj, 0, sizeof(obj)); } -static void RGB_to_Y(uint8* pDst, const uint8 *pSrc, int num_pixels) -{ - for ( ; num_pixels; pDst++, pSrc += 3, num_pixels--) - pDst[0] = static_cast((pSrc[0] * YR + pSrc[1] * YG + pSrc[2] * YB + 32768) >> 16); -} + const int YR = 19595, YG = 38470, YB = 7471, CB_R = -11059, CB_G = -21709, CB_B = 32768, CR_R = 32768, CR_G = -27439, CR_B = -5329; + static inline uint8 clamp(int i) { if (static_cast(i) > 255U) { if (i < 0) i = 0; else if (i > 255) i = 255; } return static_cast(i); } -static void RGBA_to_YCC(uint8* pDst, const uint8 *pSrc, int num_pixels) -{ - for ( ; num_pixels; pDst += 3, pSrc += 4, num_pixels--) - { - const int r = pSrc[0], g = pSrc[1], b = pSrc[2]; - pDst[0] = static_cast((r * YR + g * YG + b * YB + 32768) >> 16); - pDst[1] = clamp(128 + ((r * CB_R + g * CB_G + b * CB_B + 32768) >> 16)); - pDst[2] = clamp(128 + ((r * CR_R + g * CR_G + b * CR_B + 32768) >> 16)); - } -} + static inline int left_shifti(int val, uint32 bits) + { + return static_cast(static_cast(val) << bits); + } -static void RGBA_to_Y(uint8* pDst, const uint8 *pSrc, int num_pixels) -{ - for ( ; num_pixels; pDst++, pSrc += 4, num_pixels--) - pDst[0] = static_cast((pSrc[0] * YR + pSrc[1] * YG + pSrc[2] * YB + 32768) >> 16); -} + static void RGB_to_YCC(uint8* pDst, const uint8* pSrc, int num_pixels) + { + for (; num_pixels; pDst += 3, pSrc += 3, num_pixels--) + { + const int r = pSrc[0], g = pSrc[1], b = pSrc[2]; + pDst[0] = static_cast((r * YR + g * YG + b * YB + 32768) >> 16); + pDst[1] = clamp(128 + ((r * CB_R + g * CB_G + b * CB_B + 32768) >> 16)); + pDst[2] = clamp(128 + ((r * CR_R + g * CR_G + b * CR_B + 32768) >> 16)); + } + } -static void Y_to_YCC(uint8* pDst, const uint8* pSrc, int num_pixels) -{ - for( ; num_pixels; pDst += 3, pSrc++, num_pixels--) { pDst[0] = pSrc[0]; pDst[1] = 128; pDst[2] = 128; } -} + static void RGB_to_Y(uint8* pDst, const uint8* pSrc, int num_pixels) + { + for (; num_pixels; pDst++, pSrc += 3, num_pixels--) + pDst[0] = static_cast((pSrc[0] * YR + pSrc[1] * YG + pSrc[2] * YB + 32768) >> 16); + } -// Forward DCT - DCT derived from jfdctint. -enum { CONST_BITS = 13, ROW_BITS = 2 }; + static void RGBA_to_YCC(uint8* pDst, const uint8* pSrc, int num_pixels) + { + for (; num_pixels; pDst += 3, pSrc += 4, num_pixels--) + { + const int r = pSrc[0], g = pSrc[1], b = pSrc[2]; + pDst[0] = static_cast((r * YR + g * YG + b * YB + 32768) >> 16); + pDst[1] = clamp(128 + ((r * CB_R + g * CB_G + b * CB_B + 32768) >> 16)); + pDst[2] = clamp(128 + ((r * CR_R + g * CR_G + b * CR_B + 32768) >> 16)); + } + } + + static void RGBA_to_Y(uint8* pDst, const uint8* pSrc, int num_pixels) + { + for (; num_pixels; pDst++, pSrc += 4, num_pixels--) + pDst[0] = static_cast((pSrc[0] * YR + pSrc[1] * YG + pSrc[2] * YB + 32768) >> 16); + } + + static void Y_to_YCC(uint8* pDst, const uint8* pSrc, int num_pixels) + { + for (; num_pixels; pDst += 3, pSrc++, num_pixels--) { pDst[0] = pSrc[0]; pDst[1] = 128; pDst[2] = 128; } + } + + // Forward DCT - DCT derived from jfdctint. + enum { CONST_BITS = 13, ROW_BITS = 2 }; #define DCT_DESCALE(x, n) (((x) + (((int32)1) << ((n) - 1))) >> (n)) #define DCT_MUL(var, c) (static_cast(var) * static_cast(c)) #define DCT1D(s0, s1, s2, s3, s4, s5, s6, s7) \ @@ -120,840 +148,928 @@ enum { CONST_BITS = 13, ROW_BITS = 2 }; u3 += z5; u4 += z5; \ s0 = t10 + t11; s1 = t7 + u1 + u4; s3 = t6 + u2 + u3; s4 = t10 - t11; s5 = t5 + u2 + u4; s7 = t4 + u1 + u3; -static void DCT2D(int32 *p) -{ - int32 c, *q = p; - for (c = 7; c >= 0; c--, q += 8) - { - int32 s0 = q[0], s1 = q[1], s2 = q[2], s3 = q[3], s4 = q[4], s5 = q[5], s6 = q[6], s7 = q[7]; - DCT1D(s0, s1, s2, s3, s4, s5, s6, s7); - q[0] = s0 << ROW_BITS; q[1] = DCT_DESCALE(s1, CONST_BITS-ROW_BITS); q[2] = DCT_DESCALE(s2, CONST_BITS-ROW_BITS); q[3] = DCT_DESCALE(s3, CONST_BITS-ROW_BITS); - q[4] = s4 << ROW_BITS; q[5] = DCT_DESCALE(s5, CONST_BITS-ROW_BITS); q[6] = DCT_DESCALE(s6, CONST_BITS-ROW_BITS); q[7] = DCT_DESCALE(s7, CONST_BITS-ROW_BITS); - } - for (q = p, c = 7; c >= 0; c--, q++) - { - int32 s0 = q[0*8], s1 = q[1*8], s2 = q[2*8], s3 = q[3*8], s4 = q[4*8], s5 = q[5*8], s6 = q[6*8], s7 = q[7*8]; - DCT1D(s0, s1, s2, s3, s4, s5, s6, s7); - q[0*8] = DCT_DESCALE(s0, ROW_BITS+3); q[1*8] = DCT_DESCALE(s1, CONST_BITS+ROW_BITS+3); q[2*8] = DCT_DESCALE(s2, CONST_BITS+ROW_BITS+3); q[3*8] = DCT_DESCALE(s3, CONST_BITS+ROW_BITS+3); - q[4*8] = DCT_DESCALE(s4, ROW_BITS+3); q[5*8] = DCT_DESCALE(s5, CONST_BITS+ROW_BITS+3); q[6*8] = DCT_DESCALE(s6, CONST_BITS+ROW_BITS+3); q[7*8] = DCT_DESCALE(s7, CONST_BITS+ROW_BITS+3); - } -} - -struct sym_freq { uint m_key, m_sym_index; }; - -// Radix sorts sym_freq[] array by 32-bit key m_key. Returns ptr to sorted values. -static inline sym_freq* radix_sort_syms(uint num_syms, sym_freq* pSyms0, sym_freq* pSyms1) -{ - const uint cMaxPasses = 4; - uint32 hist[256 * cMaxPasses]; clear_obj(hist); - for (uint i = 0; i < num_syms; i++) { uint freq = pSyms0[i].m_key; hist[freq & 0xFF]++; hist[256 + ((freq >> 8) & 0xFF)]++; hist[256*2 + ((freq >> 16) & 0xFF)]++; hist[256*3 + ((freq >> 24) & 0xFF)]++; } - sym_freq* pCur_syms = pSyms0, *pNew_syms = pSyms1; - uint total_passes = cMaxPasses; while ((total_passes > 1) && (num_syms == hist[(total_passes - 1) * 256])) total_passes--; - for (uint pass_shift = 0, pass = 0; pass < total_passes; pass++, pass_shift += 8) - { - const uint32* pHist = &hist[pass << 8]; - uint offsets[256], cur_ofs = 0; - for (uint i = 0; i < 256; i++) { offsets[i] = cur_ofs; cur_ofs += pHist[i]; } - for (uint i = 0; i < num_syms; i++) - pNew_syms[offsets[(pCur_syms[i].m_key >> pass_shift) & 0xFF]++] = pCur_syms[i]; - sym_freq* t = pCur_syms; pCur_syms = pNew_syms; pNew_syms = t; - } - return pCur_syms; -} - -// calculate_minimum_redundancy() originally written by: Alistair Moffat, alistair@cs.mu.oz.au, Jyrki Katajainen, jyrki@diku.dk, November 1996. -static void calculate_minimum_redundancy(sym_freq *A, int n) -{ - int root, leaf, next, avbl, used, dpth; - if (n==0) return; else if (n==1) { A[0].m_key = 1; return; } - A[0].m_key += A[1].m_key; root = 0; leaf = 2; - for (next=1; next < n-1; next++) - { - if (leaf>=n || A[root].m_key=n || (root=0; next--) A[next].m_key = A[A[next].m_key].m_key+1; - avbl = 1; used = dpth = 0; root = n-2; next = n-1; - while (avbl>0) - { - while (root>=0 && (int)A[root].m_key==dpth) { used++; root--; } - while (avbl>used) { A[next--].m_key = dpth; avbl--; } - avbl = 2*used; dpth++; used = 0; - } -} - -// Limits canonical Huffman code table's max code size to max_code_size. -static void huffman_enforce_max_code_size(int *pNum_codes, int code_list_len, int max_code_size) -{ - if (code_list_len <= 1) return; - - for (int i = max_code_size + 1; i <= MAX_HUFF_CODESIZE; i++) pNum_codes[max_code_size] += pNum_codes[i]; - - uint32 total = 0; - for (int i = max_code_size; i > 0; i--) - total += (((uint32)pNum_codes[i]) << (max_code_size - i)); - - while (total != (1UL << max_code_size)) - { - pNum_codes[max_code_size]--; - for (int i = max_code_size - 1; i > 0; i--) - { - if (pNum_codes[i]) { pNum_codes[i]--; pNum_codes[i + 1] += 2; break; } - } - total--; - } -} - -// Generates an optimized offman table. -void jpeg_encoder::optimize_huffman_table(int table_num, int table_len) -{ - sym_freq syms0[MAX_HUFF_SYMBOLS], syms1[MAX_HUFF_SYMBOLS]; - syms0[0].m_key = 1; syms0[0].m_sym_index = 0; // dummy symbol, assures that no valid code contains all 1's - int num_used_syms = 1; - const uint32 *pSym_count = &m_huff_count[table_num][0]; - for (int i = 0; i < table_len; i++) - if (pSym_count[i]) { syms0[num_used_syms].m_key = pSym_count[i]; syms0[num_used_syms++].m_sym_index = i + 1; } - sym_freq* pSyms = radix_sort_syms(num_used_syms, syms0, syms1); - calculate_minimum_redundancy(pSyms, num_used_syms); - - // Count the # of symbols of each code size. - int num_codes[1 + MAX_HUFF_CODESIZE]; clear_obj(num_codes); - for (int i = 0; i < num_used_syms; i++) - num_codes[pSyms[i].m_key]++; - - const uint JPGE_CODE_SIZE_LIMIT = 16; // the maximum possible size of a JPEG Huffman code (valid range is [9,16] - 9 vs. 8 because of the dummy symbol) - huffman_enforce_max_code_size(num_codes, num_used_syms, JPGE_CODE_SIZE_LIMIT); - - // Compute m_huff_bits array, which contains the # of symbols per code size. - clear_obj(m_huff_bits[table_num]); - for (int i = 1; i <= (int)JPGE_CODE_SIZE_LIMIT; i++) - m_huff_bits[table_num][i] = static_cast(num_codes[i]); - - // Remove the dummy symbol added above, which must be in largest bucket. - for (int i = JPGE_CODE_SIZE_LIMIT; i >= 1; i--) - { - if (m_huff_bits[table_num][i]) { m_huff_bits[table_num][i]--; break; } - } - - // Compute the m_huff_val array, which contains the symbol indices sorted by code size (smallest to largest). - for (int i = num_used_syms - 1; i >= 1; i--) - m_huff_val[table_num][num_used_syms - 1 - i] = static_cast(pSyms[i].m_sym_index - 1); -} - -// JPEG marker generation. -void jpeg_encoder::emit_byte(uint8 i) -{ - m_all_stream_writes_succeeded = m_all_stream_writes_succeeded && m_pStream->put_obj(i); -} - -void jpeg_encoder::emit_word(uint i) -{ - emit_byte(uint8(i >> 8)); emit_byte(uint8(i & 0xFF)); -} - -void jpeg_encoder::emit_marker(int marker) -{ - emit_byte(uint8(0xFF)); emit_byte(uint8(marker)); -} - -// Emit JFIF marker -void jpeg_encoder::emit_jfif_app0() -{ - emit_marker(M_APP0); - emit_word(2 + 4 + 1 + 2 + 1 + 2 + 2 + 1 + 1); - emit_byte(0x4A); emit_byte(0x46); emit_byte(0x49); emit_byte(0x46); /* Identifier: ASCII "JFIF" */ - emit_byte(0); - emit_byte(1); /* Major version */ - emit_byte(1); /* Minor version */ - emit_byte(0); /* Density unit */ - emit_word(1); - emit_word(1); - emit_byte(0); /* No thumbnail image */ - emit_byte(0); -} - -// Emit quantization tables -void jpeg_encoder::emit_dqt() -{ - for (int i = 0; i < ((m_num_components == 3) ? 2 : 1); i++) - { - emit_marker(M_DQT); - emit_word(64 + 1 + 2); - emit_byte(static_cast(i)); - for (int j = 0; j < 64; j++) - emit_byte(static_cast(m_quantization_tables[i][j])); - } -} - -// Emit start of frame marker -void jpeg_encoder::emit_sof() -{ - emit_marker(M_SOF0); /* baseline */ - emit_word(3 * m_num_components + 2 + 5 + 1); - emit_byte(8); /* precision */ - emit_word(m_image_y); - emit_word(m_image_x); - emit_byte(m_num_components); - for (int i = 0; i < m_num_components; i++) - { - emit_byte(static_cast(i + 1)); /* component ID */ - emit_byte((m_comp_h_samp[i] << 4) + m_comp_v_samp[i]); /* h and v sampling */ - emit_byte(i > 0); /* quant. table num */ - } -} - -// Emit Huffman table. -void jpeg_encoder::emit_dht(uint8 *bits, uint8 *val, int index, bool ac_flag) -{ - emit_marker(M_DHT); - - int length = 0; - for (int i = 1; i <= 16; i++) - length += bits[i]; - - emit_word(length + 2 + 1 + 16); - emit_byte(static_cast(index + (ac_flag << 4))); - - for (int i = 1; i <= 16; i++) - emit_byte(bits[i]); - - for (int i = 0; i < length; i++) - emit_byte(val[i]); -} - -// Emit all Huffman tables. -void jpeg_encoder::emit_dhts() -{ - emit_dht(m_huff_bits[0+0], m_huff_val[0+0], 0, false); - emit_dht(m_huff_bits[2+0], m_huff_val[2+0], 0, true); - if (m_num_components == 3) - { - emit_dht(m_huff_bits[0+1], m_huff_val[0+1], 1, false); - emit_dht(m_huff_bits[2+1], m_huff_val[2+1], 1, true); - } -} - -// emit start of scan -void jpeg_encoder::emit_sos() -{ - emit_marker(M_SOS); - emit_word(2 * m_num_components + 2 + 1 + 3); - emit_byte(m_num_components); - for (int i = 0; i < m_num_components; i++) - { - emit_byte(static_cast(i + 1)); - if (i == 0) - emit_byte((0 << 4) + 0); - else - emit_byte((1 << 4) + 1); - } - emit_byte(0); /* spectral selection */ - emit_byte(63); - emit_byte(0); -} - -// Emit all markers at beginning of image file. -void jpeg_encoder::emit_markers() -{ - emit_marker(M_SOI); - emit_jfif_app0(); - emit_dqt(); - emit_sof(); - emit_dhts(); - emit_sos(); -} - -// Compute the actual canonical Huffman codes/code sizes given the JPEG huff bits and val arrays. -void jpeg_encoder::compute_huffman_table(uint *codes, uint8 *code_sizes, uint8 *bits, uint8 *val) -{ - int i, l, last_p, si; - uint8 huff_size[257]; - uint huff_code[257]; - uint code; - - int p = 0; - for (l = 1; l <= 16; l++) - for (i = 1; i <= bits[l]; i++) - huff_size[p++] = (char)l; - - huff_size[p] = 0; last_p = p; // write sentinel - - code = 0; si = huff_size[0]; p = 0; - - while (huff_size[p]) - { - while (huff_size[p] == si) - huff_code[p++] = code++; - code <<= 1; - si++; - } - - memset(codes, 0, sizeof(codes[0])*256); - memset(code_sizes, 0, sizeof(code_sizes[0])*256); - for (p = 0; p < last_p; p++) - { - codes[val[p]] = huff_code[p]; - code_sizes[val[p]] = huff_size[p]; - } -} - -// Quantization table generation. -void jpeg_encoder::compute_quant_table(int32 *pDst, int16 *pSrc) -{ - int32 q; - if (m_params.m_quality < 50) - q = 5000 / m_params.m_quality; - else - q = 200 - m_params.m_quality * 2; - for (int i = 0; i < 64; i++) - { - int32 j = *pSrc++; j = (j * q + 50L) / 100L; - *pDst++ = JPGE_MIN(JPGE_MAX(j, 1), 255); - } -} - -// Higher-level methods. -void jpeg_encoder::first_pass_init() -{ - m_bit_buffer = 0; m_bits_in = 0; - memset(m_last_dc_val, 0, 3 * sizeof(m_last_dc_val[0])); - m_mcu_y_ofs = 0; - m_pass_num = 1; -} - -bool jpeg_encoder::second_pass_init() -{ - compute_huffman_table(&m_huff_codes[0+0][0], &m_huff_code_sizes[0+0][0], m_huff_bits[0+0], m_huff_val[0+0]); - compute_huffman_table(&m_huff_codes[2+0][0], &m_huff_code_sizes[2+0][0], m_huff_bits[2+0], m_huff_val[2+0]); - if (m_num_components > 1) - { - compute_huffman_table(&m_huff_codes[0+1][0], &m_huff_code_sizes[0+1][0], m_huff_bits[0+1], m_huff_val[0+1]); - compute_huffman_table(&m_huff_codes[2+1][0], &m_huff_code_sizes[2+1][0], m_huff_bits[2+1], m_huff_val[2+1]); - } - first_pass_init(); - emit_markers(); - m_pass_num = 2; - return true; -} - -bool jpeg_encoder::jpg_open(int p_x_res, int p_y_res, int src_channels) -{ - m_num_components = 3; - switch (m_params.m_subsampling) - { - case Y_ONLY: - { - m_num_components = 1; - m_comp_h_samp[0] = 1; m_comp_v_samp[0] = 1; - m_mcu_x = 8; m_mcu_y = 8; - break; - } - case H1V1: - { - m_comp_h_samp[0] = 1; m_comp_v_samp[0] = 1; - m_comp_h_samp[1] = 1; m_comp_v_samp[1] = 1; - m_comp_h_samp[2] = 1; m_comp_v_samp[2] = 1; - m_mcu_x = 8; m_mcu_y = 8; - break; - } - case H2V1: - { - m_comp_h_samp[0] = 2; m_comp_v_samp[0] = 1; - m_comp_h_samp[1] = 1; m_comp_v_samp[1] = 1; - m_comp_h_samp[2] = 1; m_comp_v_samp[2] = 1; - m_mcu_x = 16; m_mcu_y = 8; - break; - } - case H2V2: - { - m_comp_h_samp[0] = 2; m_comp_v_samp[0] = 2; - m_comp_h_samp[1] = 1; m_comp_v_samp[1] = 1; - m_comp_h_samp[2] = 1; m_comp_v_samp[2] = 1; - m_mcu_x = 16; m_mcu_y = 16; - } - } - - m_image_x = p_x_res; m_image_y = p_y_res; - m_image_bpp = src_channels; - m_image_bpl = m_image_x * src_channels; - m_image_x_mcu = (m_image_x + m_mcu_x - 1) & (~(m_mcu_x - 1)); - m_image_y_mcu = (m_image_y + m_mcu_y - 1) & (~(m_mcu_y - 1)); - m_image_bpl_xlt = m_image_x * m_num_components; - m_image_bpl_mcu = m_image_x_mcu * m_num_components; - m_mcus_per_row = m_image_x_mcu / m_mcu_x; - - if ((m_mcu_lines[0] = static_cast(jpge_malloc(m_image_bpl_mcu * m_mcu_y))) == NULL) return false; - for (int i = 1; i < m_mcu_y; i++) - m_mcu_lines[i] = m_mcu_lines[i-1] + m_image_bpl_mcu; - - compute_quant_table(m_quantization_tables[0], s_std_lum_quant); - compute_quant_table(m_quantization_tables[1], m_params.m_no_chroma_discrim_flag ? s_std_lum_quant : s_std_croma_quant); - - m_out_buf_left = JPGE_OUT_BUF_SIZE; - m_pOut_buf = m_out_buf; - - if (m_params.m_two_pass_flag) - { - clear_obj(m_huff_count); - first_pass_init(); - } - else - { - memcpy(m_huff_bits[0+0], s_dc_lum_bits, 17); memcpy(m_huff_val [0+0], s_dc_lum_val, DC_LUM_CODES); - memcpy(m_huff_bits[2+0], s_ac_lum_bits, 17); memcpy(m_huff_val [2+0], s_ac_lum_val, AC_LUM_CODES); - memcpy(m_huff_bits[0+1], s_dc_chroma_bits, 17); memcpy(m_huff_val [0+1], s_dc_chroma_val, DC_CHROMA_CODES); - memcpy(m_huff_bits[2+1], s_ac_chroma_bits, 17); memcpy(m_huff_val [2+1], s_ac_chroma_val, AC_CHROMA_CODES); - if (!second_pass_init()) return false; // in effect, skip over the first pass - } - return m_all_stream_writes_succeeded; -} - -void jpeg_encoder::load_block_8_8_grey(int x) -{ - uint8 *pSrc; - sample_array_t *pDst = m_sample_array; - x <<= 3; - for (int i = 0; i < 8; i++, pDst += 8) - { - pSrc = m_mcu_lines[i] + x; - pDst[0] = pSrc[0] - 128; pDst[1] = pSrc[1] - 128; pDst[2] = pSrc[2] - 128; pDst[3] = pSrc[3] - 128; - pDst[4] = pSrc[4] - 128; pDst[5] = pSrc[5] - 128; pDst[6] = pSrc[6] - 128; pDst[7] = pSrc[7] - 128; - } -} - -void jpeg_encoder::load_block_8_8(int x, int y, int c) -{ - uint8 *pSrc; - sample_array_t *pDst = m_sample_array; - x = (x * (8 * 3)) + c; - y <<= 3; - for (int i = 0; i < 8; i++, pDst += 8) - { - pSrc = m_mcu_lines[y + i] + x; - pDst[0] = pSrc[0 * 3] - 128; pDst[1] = pSrc[1 * 3] - 128; pDst[2] = pSrc[2 * 3] - 128; pDst[3] = pSrc[3 * 3] - 128; - pDst[4] = pSrc[4 * 3] - 128; pDst[5] = pSrc[5 * 3] - 128; pDst[6] = pSrc[6 * 3] - 128; pDst[7] = pSrc[7 * 3] - 128; - } -} - -void jpeg_encoder::load_block_16_8(int x, int c) -{ - uint8 *pSrc1, *pSrc2; - sample_array_t *pDst = m_sample_array; - x = (x * (16 * 3)) + c; - int a = 0, b = 2; - for (int i = 0; i < 16; i += 2, pDst += 8) - { - pSrc1 = m_mcu_lines[i + 0] + x; - pSrc2 = m_mcu_lines[i + 1] + x; - pDst[0] = ((pSrc1[ 0 * 3] + pSrc1[ 1 * 3] + pSrc2[ 0 * 3] + pSrc2[ 1 * 3] + a) >> 2) - 128; pDst[1] = ((pSrc1[ 2 * 3] + pSrc1[ 3 * 3] + pSrc2[ 2 * 3] + pSrc2[ 3 * 3] + b) >> 2) - 128; - pDst[2] = ((pSrc1[ 4 * 3] + pSrc1[ 5 * 3] + pSrc2[ 4 * 3] + pSrc2[ 5 * 3] + a) >> 2) - 128; pDst[3] = ((pSrc1[ 6 * 3] + pSrc1[ 7 * 3] + pSrc2[ 6 * 3] + pSrc2[ 7 * 3] + b) >> 2) - 128; - pDst[4] = ((pSrc1[ 8 * 3] + pSrc1[ 9 * 3] + pSrc2[ 8 * 3] + pSrc2[ 9 * 3] + a) >> 2) - 128; pDst[5] = ((pSrc1[10 * 3] + pSrc1[11 * 3] + pSrc2[10 * 3] + pSrc2[11 * 3] + b) >> 2) - 128; - pDst[6] = ((pSrc1[12 * 3] + pSrc1[13 * 3] + pSrc2[12 * 3] + pSrc2[13 * 3] + a) >> 2) - 128; pDst[7] = ((pSrc1[14 * 3] + pSrc1[15 * 3] + pSrc2[14 * 3] + pSrc2[15 * 3] + b) >> 2) - 128; - int temp = a; a = b; b = temp; - } -} - -void jpeg_encoder::load_block_16_8_8(int x, int c) -{ - uint8 *pSrc1; - sample_array_t *pDst = m_sample_array; - x = (x * (16 * 3)) + c; - for (int i = 0; i < 8; i++, pDst += 8) - { - pSrc1 = m_mcu_lines[i + 0] + x; - pDst[0] = ((pSrc1[ 0 * 3] + pSrc1[ 1 * 3]) >> 1) - 128; pDst[1] = ((pSrc1[ 2 * 3] + pSrc1[ 3 * 3]) >> 1) - 128; - pDst[2] = ((pSrc1[ 4 * 3] + pSrc1[ 5 * 3]) >> 1) - 128; pDst[3] = ((pSrc1[ 6 * 3] + pSrc1[ 7 * 3]) >> 1) - 128; - pDst[4] = ((pSrc1[ 8 * 3] + pSrc1[ 9 * 3]) >> 1) - 128; pDst[5] = ((pSrc1[10 * 3] + pSrc1[11 * 3]) >> 1) - 128; - pDst[6] = ((pSrc1[12 * 3] + pSrc1[13 * 3]) >> 1) - 128; pDst[7] = ((pSrc1[14 * 3] + pSrc1[15 * 3]) >> 1) - 128; - } -} - -void jpeg_encoder::load_quantized_coefficients(int component_num) -{ - int32 *q = m_quantization_tables[component_num > 0]; - int16 *pDst = m_coefficient_array; - for (int i = 0; i < 64; i++) - { - sample_array_t j = m_sample_array[s_zag[i]]; - if (j < 0) - { - if ((j = -j + (*q >> 1)) < *q) - *pDst++ = 0; - else - *pDst++ = static_cast(-(j / *q)); - } - else - { - if ((j = j + (*q >> 1)) < *q) - *pDst++ = 0; - else - *pDst++ = static_cast((j / *q)); - } - q++; - } -} - -void jpeg_encoder::flush_output_buffer() -{ - if (m_out_buf_left != JPGE_OUT_BUF_SIZE) - m_all_stream_writes_succeeded = m_all_stream_writes_succeeded && m_pStream->put_buf(m_out_buf, JPGE_OUT_BUF_SIZE - m_out_buf_left); - m_pOut_buf = m_out_buf; - m_out_buf_left = JPGE_OUT_BUF_SIZE; -} - -void jpeg_encoder::put_bits(uint bits, uint len) -{ - m_bit_buffer |= ((uint32)bits << (24 - (m_bits_in += len))); - while (m_bits_in >= 8) - { - uint8 c; - #define JPGE_PUT_BYTE(c) { *m_pOut_buf++ = (c); if (--m_out_buf_left == 0) flush_output_buffer(); } - JPGE_PUT_BYTE(c = (uint8)((m_bit_buffer >> 16) & 0xFF)); - if (c == 0xFF) JPGE_PUT_BYTE(0); - m_bit_buffer <<= 8; - m_bits_in -= 8; - } -} - -void jpeg_encoder::code_coefficients_pass_one(int component_num) -{ - if (component_num >= 3) return; // just to shut up static analysis - int i, run_len, nbits, temp1; - int16 *src = m_coefficient_array; - uint32 *dc_count = component_num ? m_huff_count[0 + 1] : m_huff_count[0 + 0], *ac_count = component_num ? m_huff_count[2 + 1] : m_huff_count[2 + 0]; - - temp1 = src[0] - m_last_dc_val[component_num]; - m_last_dc_val[component_num] = src[0]; - if (temp1 < 0) temp1 = -temp1; - - nbits = 0; - while (temp1) - { - nbits++; temp1 >>= 1; - } - - dc_count[nbits]++; - for (run_len = 0, i = 1; i < 64; i++) - { - if ((temp1 = m_coefficient_array[i]) == 0) - run_len++; - else - { - while (run_len >= 16) - { - ac_count[0xF0]++; - run_len -= 16; - } - if (temp1 < 0) temp1 = -temp1; - nbits = 1; - while (temp1 >>= 1) nbits++; - ac_count[(run_len << 4) + nbits]++; - run_len = 0; - } - } - if (run_len) ac_count[0]++; -} - -void jpeg_encoder::code_coefficients_pass_two(int component_num) -{ - int i, j, run_len, nbits, temp1, temp2; - int16 *pSrc = m_coefficient_array; - uint *codes[2]; - uint8 *code_sizes[2]; - - if (component_num == 0) - { - codes[0] = m_huff_codes[0 + 0]; codes[1] = m_huff_codes[2 + 0]; - code_sizes[0] = m_huff_code_sizes[0 + 0]; code_sizes[1] = m_huff_code_sizes[2 + 0]; - } - else - { - codes[0] = m_huff_codes[0 + 1]; codes[1] = m_huff_codes[2 + 1]; - code_sizes[0] = m_huff_code_sizes[0 + 1]; code_sizes[1] = m_huff_code_sizes[2 + 1]; - } - - temp1 = temp2 = pSrc[0] - m_last_dc_val[component_num]; - m_last_dc_val[component_num] = pSrc[0]; - - if (temp1 < 0) - { - temp1 = -temp1; temp2--; - } - - nbits = 0; - while (temp1) - { - nbits++; temp1 >>= 1; - } - - put_bits(codes[0][nbits], code_sizes[0][nbits]); - if (nbits) put_bits(temp2 & ((1 << nbits) - 1), nbits); - - for (run_len = 0, i = 1; i < 64; i++) - { - if ((temp1 = m_coefficient_array[i]) == 0) - run_len++; - else - { - while (run_len >= 16) - { - put_bits(codes[1][0xF0], code_sizes[1][0xF0]); - run_len -= 16; - } - if ((temp2 = temp1) < 0) - { - temp1 = -temp1; - temp2--; - } - nbits = 1; - while (temp1 >>= 1) - nbits++; - j = (run_len << 4) + nbits; - put_bits(codes[1][j], code_sizes[1][j]); - put_bits(temp2 & ((1 << nbits) - 1), nbits); - run_len = 0; - } - } - if (run_len) - put_bits(codes[1][0], code_sizes[1][0]); -} - -void jpeg_encoder::code_block(int component_num) -{ - DCT2D(m_sample_array); - load_quantized_coefficients(component_num); - if (m_pass_num == 1) - code_coefficients_pass_one(component_num); - else - code_coefficients_pass_two(component_num); -} - -void jpeg_encoder::process_mcu_row() -{ - if (m_num_components == 1) - { - for (int i = 0; i < m_mcus_per_row; i++) - { - load_block_8_8_grey(i); code_block(0); - } - } - else if ((m_comp_h_samp[0] == 1) && (m_comp_v_samp[0] == 1)) - { - for (int i = 0; i < m_mcus_per_row; i++) - { - load_block_8_8(i, 0, 0); code_block(0); load_block_8_8(i, 0, 1); code_block(1); load_block_8_8(i, 0, 2); code_block(2); - } - } - else if ((m_comp_h_samp[0] == 2) && (m_comp_v_samp[0] == 1)) - { - for (int i = 0; i < m_mcus_per_row; i++) - { - load_block_8_8(i * 2 + 0, 0, 0); code_block(0); load_block_8_8(i * 2 + 1, 0, 0); code_block(0); - load_block_16_8_8(i, 1); code_block(1); load_block_16_8_8(i, 2); code_block(2); - } - } - else if ((m_comp_h_samp[0] == 2) && (m_comp_v_samp[0] == 2)) - { - for (int i = 0; i < m_mcus_per_row; i++) - { - load_block_8_8(i * 2 + 0, 0, 0); code_block(0); load_block_8_8(i * 2 + 1, 0, 0); code_block(0); - load_block_8_8(i * 2 + 0, 1, 0); code_block(0); load_block_8_8(i * 2 + 1, 1, 0); code_block(0); - load_block_16_8(i, 1); code_block(1); load_block_16_8(i, 2); code_block(2); - } - } -} - -bool jpeg_encoder::terminate_pass_one() -{ - optimize_huffman_table(0+0, DC_LUM_CODES); optimize_huffman_table(2+0, AC_LUM_CODES); - if (m_num_components > 1) - { - optimize_huffman_table(0+1, DC_CHROMA_CODES); optimize_huffman_table(2+1, AC_CHROMA_CODES); - } - return second_pass_init(); -} - -bool jpeg_encoder::terminate_pass_two() -{ - put_bits(0x7F, 7); - flush_output_buffer(); - emit_marker(M_EOI); - m_pass_num++; // purposely bump up m_pass_num, for debugging - return true; -} - -bool jpeg_encoder::process_end_of_image() -{ - if (m_mcu_y_ofs) - { - if (m_mcu_y_ofs < 16) // check here just to shut up static analysis - { - for (int i = m_mcu_y_ofs; i < m_mcu_y; i++) - memcpy(m_mcu_lines[i], m_mcu_lines[m_mcu_y_ofs - 1], m_image_bpl_mcu); - } - - process_mcu_row(); - } - - if (m_pass_num == 1) - return terminate_pass_one(); - else - return terminate_pass_two(); -} - -void jpeg_encoder::load_mcu(const void *pSrc) -{ - const uint8* Psrc = reinterpret_cast(pSrc); - - uint8* pDst = m_mcu_lines[m_mcu_y_ofs]; // OK to write up to m_image_bpl_xlt bytes to pDst - - if (m_num_components == 1) - { - if (m_image_bpp == 4) - RGBA_to_Y(pDst, Psrc, m_image_x); - else if (m_image_bpp == 3) - RGB_to_Y(pDst, Psrc, m_image_x); - else - memcpy(pDst, Psrc, m_image_x); - } - else - { - if (m_image_bpp == 4) - RGBA_to_YCC(pDst, Psrc, m_image_x); - else if (m_image_bpp == 3) - RGB_to_YCC(pDst, Psrc, m_image_x); - else - Y_to_YCC(pDst, Psrc, m_image_x); - } - - // Possibly duplicate pixels at end of scanline if not a multiple of 8 or 16 - if (m_num_components == 1) - memset(m_mcu_lines[m_mcu_y_ofs] + m_image_bpl_xlt, pDst[m_image_bpl_xlt - 1], m_image_x_mcu - m_image_x); - else - { - const uint8 y = pDst[m_image_bpl_xlt - 3 + 0], cb = pDst[m_image_bpl_xlt - 3 + 1], cr = pDst[m_image_bpl_xlt - 3 + 2]; - uint8 *q = m_mcu_lines[m_mcu_y_ofs] + m_image_bpl_xlt; - for (int i = m_image_x; i < m_image_x_mcu; i++) - { - *q++ = y; *q++ = cb; *q++ = cr; - } - } - - if (++m_mcu_y_ofs == m_mcu_y) - { - process_mcu_row(); - m_mcu_y_ofs = 0; - } -} - -void jpeg_encoder::clear() -{ - m_mcu_lines[0] = NULL; - m_pass_num = 0; - m_all_stream_writes_succeeded = true; -} - -jpeg_encoder::jpeg_encoder() -{ - clear(); -} - -jpeg_encoder::~jpeg_encoder() -{ - deinit(); -} - -bool jpeg_encoder::init(output_stream *pStream, int width, int height, int src_channels, const params &comp_params) -{ - deinit(); - if (((!pStream) || (width < 1) || (height < 1)) || ((src_channels != 1) && (src_channels != 3) && (src_channels != 4)) || (!comp_params.check())) return false; - m_pStream = pStream; - m_params = comp_params; - return jpg_open(width, height, src_channels); -} - -void jpeg_encoder::deinit() -{ - jpge_free(m_mcu_lines[0]); - clear(); -} - -bool jpeg_encoder::process_scanline(const void* pScanline) -{ - if ((m_pass_num < 1) || (m_pass_num > 2)) return false; - if (m_all_stream_writes_succeeded) - { - if (!pScanline) - { - if (!process_end_of_image()) return false; - } - else - { - load_mcu(pScanline); - } - } - return m_all_stream_writes_succeeded; -} - -class memory_stream : public output_stream -{ - memory_stream(const memory_stream &); - memory_stream &operator= (const memory_stream &); - - uint8 *m_pBuf; - uint m_buf_size, m_buf_ofs; - -public: - memory_stream(void *pBuf, uint buf_size) : m_pBuf(static_cast(pBuf)), m_buf_size(buf_size), m_buf_ofs(0) { } - - virtual ~memory_stream() { } - - virtual bool put_buf(const void* pBuf, int len) - { - uint buf_remaining = m_buf_size - m_buf_ofs; - if ((uint)len > buf_remaining) - return false; - memcpy(m_pBuf + m_buf_ofs, pBuf, len); - m_buf_ofs += len; - return true; - } - - uint get_size() const - { - return m_buf_ofs; - } -}; - -bool compress_image_to_jpeg_file_in_memory(void *pDstBuf, int &buf_size, int width, int height, int num_channels, const uint8 *pImage_data, const params &comp_params) -{ - if ((!pDstBuf) || (!buf_size)) - return false; - - memory_stream dst_stream(pDstBuf, buf_size); - - buf_size = 0; - - jpge::jpeg_encoder dst_image; - if (!dst_image.init(&dst_stream, width, height, num_channels, comp_params)) - return false; - - for (uint pass_index = 0; pass_index < dst_image.get_total_passes(); pass_index++) - { - for (int i = 0; i < height; i++) - { - const uint8* pScanline = pImage_data + i * width * num_channels; - if (!dst_image.process_scanline(pScanline)) - return false; - } - if (!dst_image.process_scanline(NULL)) - return false; - } - - dst_image.deinit(); - - buf_size = dst_stream.get_size(); - return true; -} + static void DCT2D(int32* p) + { + int32 c, * q = p; + for (c = 7; c >= 0; c--, q += 8) + { + int32 s0 = q[0], s1 = q[1], s2 = q[2], s3 = q[3], s4 = q[4], s5 = q[5], s6 = q[6], s7 = q[7]; + DCT1D(s0, s1, s2, s3, s4, s5, s6, s7); + q[0] = left_shifti(s0, ROW_BITS); q[1] = DCT_DESCALE(s1, CONST_BITS - ROW_BITS); q[2] = DCT_DESCALE(s2, CONST_BITS - ROW_BITS); q[3] = DCT_DESCALE(s3, CONST_BITS - ROW_BITS); + q[4] = left_shifti(s4, ROW_BITS); q[5] = DCT_DESCALE(s5, CONST_BITS - ROW_BITS); q[6] = DCT_DESCALE(s6, CONST_BITS - ROW_BITS); q[7] = DCT_DESCALE(s7, CONST_BITS - ROW_BITS); + } + for (q = p, c = 7; c >= 0; c--, q++) + { + int32 s0 = q[0 * 8], s1 = q[1 * 8], s2 = q[2 * 8], s3 = q[3 * 8], s4 = q[4 * 8], s5 = q[5 * 8], s6 = q[6 * 8], s7 = q[7 * 8]; + DCT1D(s0, s1, s2, s3, s4, s5, s6, s7); + q[0 * 8] = DCT_DESCALE(s0, ROW_BITS + 3); q[1 * 8] = DCT_DESCALE(s1, CONST_BITS + ROW_BITS + 3); q[2 * 8] = DCT_DESCALE(s2, CONST_BITS + ROW_BITS + 3); q[3 * 8] = DCT_DESCALE(s3, CONST_BITS + ROW_BITS + 3); + q[4 * 8] = DCT_DESCALE(s4, ROW_BITS + 3); q[5 * 8] = DCT_DESCALE(s5, CONST_BITS + ROW_BITS + 3); q[6 * 8] = DCT_DESCALE(s6, CONST_BITS + ROW_BITS + 3); q[7 * 8] = DCT_DESCALE(s7, CONST_BITS + ROW_BITS + 3); + } + } + + struct sym_freq { uint m_key, m_sym_index; }; + + // Radix sorts sym_freq[] array by 32-bit key m_key. Returns ptr to sorted values. + static inline sym_freq* radix_sort_syms(uint num_syms, sym_freq* pSyms0, sym_freq* pSyms1) + { + const uint cMaxPasses = 4; + uint32 hist[256 * cMaxPasses]; clear_obj(hist); + for (uint i = 0; i < num_syms; i++) { uint freq = pSyms0[i].m_key; hist[freq & 0xFF]++; hist[256 + ((freq >> 8) & 0xFF)]++; hist[256 * 2 + ((freq >> 16) & 0xFF)]++; hist[256 * 3 + ((freq >> 24) & 0xFF)]++; } + sym_freq* pCur_syms = pSyms0, * pNew_syms = pSyms1; + uint total_passes = cMaxPasses; while ((total_passes > 1) && (num_syms == hist[(total_passes - 1) * 256])) total_passes--; + for (uint pass_shift = 0, pass = 0; pass < total_passes; pass++, pass_shift += 8) + { + const uint32* pHist = &hist[pass << 8]; + uint offsets[256], cur_ofs = 0; + for (uint i = 0; i < 256; i++) { offsets[i] = cur_ofs; cur_ofs += pHist[i]; } + for (uint i = 0; i < num_syms; i++) + pNew_syms[offsets[(pCur_syms[i].m_key >> pass_shift) & 0xFF]++] = pCur_syms[i]; + sym_freq* t = pCur_syms; pCur_syms = pNew_syms; pNew_syms = t; + } + return pCur_syms; + } + + // calculate_minimum_redundancy() originally written by: Alistair Moffat, alistair@cs.mu.oz.au, Jyrki Katajainen, jyrki@diku.dk, November 1996. + static void calculate_minimum_redundancy(sym_freq* A, int n) + { + int root, leaf, next, avbl, used, dpth; + if (n == 0) return; else if (n == 1) { A[0].m_key = 1; return; } + A[0].m_key += A[1].m_key; root = 0; leaf = 2; + for (next = 1; next < n - 1; next++) + { + if (leaf >= n || A[root].m_key < A[leaf].m_key) { A[next].m_key = A[root].m_key; A[root++].m_key = next; } + else A[next].m_key = A[leaf++].m_key; + if (leaf >= n || (root < next && A[root].m_key < A[leaf].m_key)) { A[next].m_key += A[root].m_key; A[root++].m_key = next; } + else A[next].m_key += A[leaf++].m_key; + } + A[n - 2].m_key = 0; + for (next = n - 3; next >= 0; next--) A[next].m_key = A[A[next].m_key].m_key + 1; + avbl = 1; used = dpth = 0; root = n - 2; next = n - 1; + while (avbl > 0) + { + while (root >= 0 && (int)A[root].m_key == dpth) { used++; root--; } + while (avbl > used) { A[next--].m_key = dpth; avbl--; } + avbl = 2 * used; dpth++; used = 0; + } + } + + // Limits canonical Huffman code table's max code size to max_code_size. + static void huffman_enforce_max_code_size(int* pNum_codes, int code_list_len, int max_code_size) + { + if (code_list_len <= 1) return; + + for (int i = max_code_size + 1; i <= MAX_HUFF_CODESIZE; i++) pNum_codes[max_code_size] += pNum_codes[i]; + + uint32 total = 0; + for (int i = max_code_size; i > 0; i--) + total += (((uint32)pNum_codes[i]) << (max_code_size - i)); + + while (total != (1UL << max_code_size)) + { + pNum_codes[max_code_size]--; + for (int i = max_code_size - 1; i > 0; i--) + { + if (pNum_codes[i]) { pNum_codes[i]--; pNum_codes[i + 1] += 2; break; } + } + total--; + } + } + + // Generates an optimized offman table. + void jpeg_encoder::optimize_huffman_table(int table_num, int table_len) + { + sym_freq syms0[MAX_HUFF_SYMBOLS], syms1[MAX_HUFF_SYMBOLS]; + syms0[0].m_key = 1; syms0[0].m_sym_index = 0; // dummy symbol, assures that no valid code contains all 1's + int num_used_syms = 1; + const uint32* pSym_count = &m_huff_count[table_num][0]; + for (int i = 0; i < table_len; i++) + if (pSym_count[i]) { syms0[num_used_syms].m_key = pSym_count[i]; syms0[num_used_syms++].m_sym_index = i + 1; } + sym_freq* pSyms = radix_sort_syms(num_used_syms, syms0, syms1); + calculate_minimum_redundancy(pSyms, num_used_syms); + + // Count the # of symbols of each code size. + int num_codes[1 + MAX_HUFF_CODESIZE]; clear_obj(num_codes); + for (int i = 0; i < num_used_syms; i++) + num_codes[pSyms[i].m_key]++; + + const uint JPGE_CODE_SIZE_LIMIT = 16; // the maximum possible size of a JPEG Huffman code (valid range is [9,16] - 9 vs. 8 because of the dummy symbol) + huffman_enforce_max_code_size(num_codes, num_used_syms, JPGE_CODE_SIZE_LIMIT); + + // Compute m_huff_bits array, which contains the # of symbols per code size. + clear_obj(m_huff_bits[table_num]); + for (int i = 1; i <= (int)JPGE_CODE_SIZE_LIMIT; i++) + m_huff_bits[table_num][i] = static_cast(num_codes[i]); + + // Remove the dummy symbol added above, which must be in largest bucket. + for (int i = JPGE_CODE_SIZE_LIMIT; i >= 1; i--) + { + if (m_huff_bits[table_num][i]) { m_huff_bits[table_num][i]--; break; } + } + + // Compute the m_huff_val array, which contains the symbol indices sorted by code size (smallest to largest). + for (int i = num_used_syms - 1; i >= 1; i--) + m_huff_val[table_num][num_used_syms - 1 - i] = static_cast(pSyms[i].m_sym_index - 1); + } + + // JPEG marker generation. + void jpeg_encoder::emit_byte(uint8 i) + { + m_all_stream_writes_succeeded = m_all_stream_writes_succeeded && m_pStream->put_obj(i); + } + + void jpeg_encoder::emit_word(uint i) + { + emit_byte(uint8(i >> 8)); emit_byte(uint8(i & 0xFF)); + } + + void jpeg_encoder::emit_marker(int marker) + { + emit_byte(uint8(0xFF)); emit_byte(uint8(marker)); + } + + // Emit JFIF marker + void jpeg_encoder::emit_jfif_app0() + { + emit_marker(M_APP0); + emit_word(2 + 4 + 1 + 2 + 1 + 2 + 2 + 1 + 1); + emit_byte(0x4A); emit_byte(0x46); emit_byte(0x49); emit_byte(0x46); /* Identifier: ASCII "JFIF" */ + emit_byte(0); + emit_byte(1); /* Major version */ + emit_byte(1); /* Minor version */ + emit_byte(0); /* Density unit */ + emit_word(1); + emit_word(1); + emit_byte(0); /* No thumbnail image */ + emit_byte(0); + } + + // Emit quantization tables + void jpeg_encoder::emit_dqt() + { + for (int i = 0; i < ((m_num_components == 3) ? 2 : 1); i++) + { + emit_marker(M_DQT); + emit_word(64 + 1 + 2); + emit_byte(static_cast(i)); + for (int j = 0; j < 64; j++) + emit_byte(static_cast(m_quantization_tables[i][j])); + } + } + + // Emit start of frame marker + void jpeg_encoder::emit_sof() + { + emit_marker(M_SOF0); /* baseline */ + emit_word(3 * m_num_components + 2 + 5 + 1); + emit_byte(8); /* precision */ + emit_word(m_image_y); + emit_word(m_image_x); + emit_byte(m_num_components); + for (int i = 0; i < m_num_components; i++) + { + emit_byte(static_cast(i + 1)); /* component ID */ + emit_byte((m_comp_h_samp[i] << 4) + m_comp_v_samp[i]); /* h and v sampling */ + emit_byte(i > 0); /* quant. table num */ + } + } + + // Emit Huffman table. + void jpeg_encoder::emit_dht(uint8* bits, uint8* val, int index, bool ac_flag) + { + emit_marker(M_DHT); + + int length = 0; + for (int i = 1; i <= 16; i++) + length += bits[i]; + + emit_word(length + 2 + 1 + 16); + emit_byte(static_cast(index + (ac_flag << 4))); + + for (int i = 1; i <= 16; i++) + emit_byte(bits[i]); + + for (int i = 0; i < length; i++) + emit_byte(val[i]); + } + + // Emit all Huffman tables. + void jpeg_encoder::emit_dhts() + { + emit_dht(m_huff_bits[0 + 0], m_huff_val[0 + 0], 0, false); + emit_dht(m_huff_bits[2 + 0], m_huff_val[2 + 0], 0, true); + if (m_num_components == 3) + { + emit_dht(m_huff_bits[0 + 1], m_huff_val[0 + 1], 1, false); + emit_dht(m_huff_bits[2 + 1], m_huff_val[2 + 1], 1, true); + } + } + + // emit start of scan + void jpeg_encoder::emit_sos() + { + emit_marker(M_SOS); + emit_word(2 * m_num_components + 2 + 1 + 3); + emit_byte(m_num_components); + for (int i = 0; i < m_num_components; i++) + { + emit_byte(static_cast(i + 1)); + if (i == 0) + emit_byte((0 << 4) + 0); + else + emit_byte((1 << 4) + 1); + } + emit_byte(0); /* spectral selection */ + emit_byte(63); + emit_byte(0); + } + + // Emit all markers at beginning of image file. + void jpeg_encoder::emit_markers() + { + emit_marker(M_SOI); + emit_jfif_app0(); + emit_dqt(); + emit_sof(); + emit_dhts(); + emit_sos(); + } + + // Compute the actual canonical Huffman codes/code sizes given the JPEG huff bits and val arrays. + void jpeg_encoder::compute_huffman_table(uint* codes, uint8* code_sizes, uint8* bits, uint8* val) + { + int i, l, last_p, si; + uint8 huff_size[257]; + uint huff_code[257]; + uint code; + + int p = 0; + for (l = 1; l <= 16; l++) + for (i = 1; i <= bits[l]; i++) + huff_size[p++] = (char)l; + + huff_size[p] = 0; last_p = p; // write sentinel + + code = 0; si = huff_size[0]; p = 0; + + while (huff_size[p]) + { + while (huff_size[p] == si) + huff_code[p++] = code++; + code <<= 1; + si++; + } + + memset(codes, 0, sizeof(codes[0]) * 256); + memset(code_sizes, 0, sizeof(code_sizes[0]) * 256); + for (p = 0; p < last_p; p++) + { + codes[val[p]] = huff_code[p]; + code_sizes[val[p]] = huff_size[p]; + } + } + + // Quantization table generation. + void jpeg_encoder::compute_quant_table(int32* pDst, int16* pSrc) + { + int32 q; + if (m_params.m_quality < 50) + q = 5000 / m_params.m_quality; + else + q = 200 - m_params.m_quality * 2; + for (int i = 0; i < 64; i++) + { + int32 j = *pSrc++; j = (j * q + 50L) / 100L; + *pDst++ = JPGE_MIN(JPGE_MAX(j, 1), 255); + } + } + + // Higher-level methods. + void jpeg_encoder::first_pass_init() + { + m_bit_buffer = 0; m_bits_in = 0; + memset(m_last_dc_val, 0, 3 * sizeof(m_last_dc_val[0])); + m_mcu_y_ofs = 0; + m_pass_num = 1; + } + + bool jpeg_encoder::second_pass_init() + { + compute_huffman_table(&m_huff_codes[0 + 0][0], &m_huff_code_sizes[0 + 0][0], m_huff_bits[0 + 0], m_huff_val[0 + 0]); + compute_huffman_table(&m_huff_codes[2 + 0][0], &m_huff_code_sizes[2 + 0][0], m_huff_bits[2 + 0], m_huff_val[2 + 0]); + if (m_num_components > 1) + { + compute_huffman_table(&m_huff_codes[0 + 1][0], &m_huff_code_sizes[0 + 1][0], m_huff_bits[0 + 1], m_huff_val[0 + 1]); + compute_huffman_table(&m_huff_codes[2 + 1][0], &m_huff_code_sizes[2 + 1][0], m_huff_bits[2 + 1], m_huff_val[2 + 1]); + } + first_pass_init(); + emit_markers(); + m_pass_num = 2; + return true; + } + + bool jpeg_encoder::jpg_open(int p_x_res, int p_y_res, int src_channels) + { + m_num_components = 3; + switch (m_params.m_subsampling) + { + case Y_ONLY: + { + m_num_components = 1; + m_comp_h_samp[0] = 1; m_comp_v_samp[0] = 1; + m_mcu_x = 8; m_mcu_y = 8; + break; + } + case H1V1: + { + m_comp_h_samp[0] = 1; m_comp_v_samp[0] = 1; + m_comp_h_samp[1] = 1; m_comp_v_samp[1] = 1; + m_comp_h_samp[2] = 1; m_comp_v_samp[2] = 1; + m_mcu_x = 8; m_mcu_y = 8; + break; + } + case H2V1: + { + m_comp_h_samp[0] = 2; m_comp_v_samp[0] = 1; + m_comp_h_samp[1] = 1; m_comp_v_samp[1] = 1; + m_comp_h_samp[2] = 1; m_comp_v_samp[2] = 1; + m_mcu_x = 16; m_mcu_y = 8; + break; + } + case H2V2: + { + m_comp_h_samp[0] = 2; m_comp_v_samp[0] = 2; + m_comp_h_samp[1] = 1; m_comp_v_samp[1] = 1; + m_comp_h_samp[2] = 1; m_comp_v_samp[2] = 1; + m_mcu_x = 16; m_mcu_y = 16; + } + } + + m_image_x = p_x_res; m_image_y = p_y_res; + m_image_bpp = src_channels; + m_image_bpl = m_image_x * src_channels; + m_image_x_mcu = (m_image_x + m_mcu_x - 1) & (~(m_mcu_x - 1)); + m_image_y_mcu = (m_image_y + m_mcu_y - 1) & (~(m_mcu_y - 1)); + m_image_bpl_xlt = m_image_x * m_num_components; + m_image_bpl_mcu = m_image_x_mcu * m_num_components; + m_mcus_per_row = m_image_x_mcu / m_mcu_x; + + if ((m_mcu_lines[0] = static_cast(jpge_malloc(m_image_bpl_mcu * m_mcu_y))) == NULL) return false; + for (int i = 1; i < m_mcu_y; i++) + m_mcu_lines[i] = m_mcu_lines[i - 1] + m_image_bpl_mcu; + + if (m_params.m_use_std_tables) + { + compute_quant_table(m_quantization_tables[0], s_std_lum_quant); + compute_quant_table(m_quantization_tables[1], m_params.m_no_chroma_discrim_flag ? s_std_lum_quant : s_std_croma_quant); + } + else + { + compute_quant_table(m_quantization_tables[0], s_alt_quant); + memcpy(m_quantization_tables[1], m_quantization_tables[0], sizeof(m_quantization_tables[1])); + } + + m_out_buf_left = JPGE_OUT_BUF_SIZE; + m_pOut_buf = m_out_buf; + + if (m_params.m_two_pass_flag) + { + clear_obj(m_huff_count); + first_pass_init(); + } + else + { + memcpy(m_huff_bits[0 + 0], s_dc_lum_bits, 17); memcpy(m_huff_val[0 + 0], s_dc_lum_val, DC_LUM_CODES); + memcpy(m_huff_bits[2 + 0], s_ac_lum_bits, 17); memcpy(m_huff_val[2 + 0], s_ac_lum_val, AC_LUM_CODES); + memcpy(m_huff_bits[0 + 1], s_dc_chroma_bits, 17); memcpy(m_huff_val[0 + 1], s_dc_chroma_val, DC_CHROMA_CODES); + memcpy(m_huff_bits[2 + 1], s_ac_chroma_bits, 17); memcpy(m_huff_val[2 + 1], s_ac_chroma_val, AC_CHROMA_CODES); + if (!second_pass_init()) return false; // in effect, skip over the first pass + } + return m_all_stream_writes_succeeded; + } + + void jpeg_encoder::load_block_8_8_grey(int x) + { + uint8* pSrc; + sample_array_t* pDst = m_sample_array; + x <<= 3; + for (int i = 0; i < 8; i++, pDst += 8) + { + pSrc = m_mcu_lines[i] + x; + pDst[0] = pSrc[0] - 128; pDst[1] = pSrc[1] - 128; pDst[2] = pSrc[2] - 128; pDst[3] = pSrc[3] - 128; + pDst[4] = pSrc[4] - 128; pDst[5] = pSrc[5] - 128; pDst[6] = pSrc[6] - 128; pDst[7] = pSrc[7] - 128; + } + } + + void jpeg_encoder::load_block_8_8(int x, int y, int c) + { + uint8* pSrc; + sample_array_t* pDst = m_sample_array; + x = (x * (8 * 3)) + c; + y <<= 3; + for (int i = 0; i < 8; i++, pDst += 8) + { + pSrc = m_mcu_lines[y + i] + x; + pDst[0] = pSrc[0 * 3] - 128; pDst[1] = pSrc[1 * 3] - 128; pDst[2] = pSrc[2 * 3] - 128; pDst[3] = pSrc[3 * 3] - 128; + pDst[4] = pSrc[4 * 3] - 128; pDst[5] = pSrc[5 * 3] - 128; pDst[6] = pSrc[6 * 3] - 128; pDst[7] = pSrc[7 * 3] - 128; + } + } + + void jpeg_encoder::load_block_16_8(int x, int c) + { + uint8* pSrc1, * pSrc2; + sample_array_t* pDst = m_sample_array; + x = (x * (16 * 3)) + c; + for (int i = 0; i < 16; i += 2, pDst += 8) + { + pSrc1 = m_mcu_lines[i + 0] + x; + pSrc2 = m_mcu_lines[i + 1] + x; + pDst[0] = ((pSrc1[0 * 3] + pSrc1[1 * 3] + pSrc2[0 * 3] + pSrc2[1 * 3] + 2) >> 2) - 128; pDst[1] = ((pSrc1[2 * 3] + pSrc1[3 * 3] + pSrc2[2 * 3] + pSrc2[3 * 3] + 2) >> 2) - 128; + pDst[2] = ((pSrc1[4 * 3] + pSrc1[5 * 3] + pSrc2[4 * 3] + pSrc2[5 * 3] + 2) >> 2) - 128; pDst[3] = ((pSrc1[6 * 3] + pSrc1[7 * 3] + pSrc2[6 * 3] + pSrc2[7 * 3] + 2) >> 2) - 128; + pDst[4] = ((pSrc1[8 * 3] + pSrc1[9 * 3] + pSrc2[8 * 3] + pSrc2[9 * 3] + 2) >> 2) - 128; pDst[5] = ((pSrc1[10 * 3] + pSrc1[11 * 3] + pSrc2[10 * 3] + pSrc2[11 * 3] + 2) >> 2) - 128; + pDst[6] = ((pSrc1[12 * 3] + pSrc1[13 * 3] + pSrc2[12 * 3] + pSrc2[13 * 3] + 2) >> 2) - 128; pDst[7] = ((pSrc1[14 * 3] + pSrc1[15 * 3] + pSrc2[14 * 3] + pSrc2[15 * 3] + 2) >> 2) - 128; + } + } + + void jpeg_encoder::load_block_16_8_8(int x, int c) + { + uint8* pSrc1; + sample_array_t* pDst = m_sample_array; + x = (x * (16 * 3)) + c; + for (int i = 0; i < 8; i++, pDst += 8) + { + pSrc1 = m_mcu_lines[i + 0] + x; + pDst[0] = ((pSrc1[0 * 3] + pSrc1[1 * 3] + 1) >> 1) - 128; pDst[1] = ((pSrc1[2 * 3] + pSrc1[3 * 3] + 1) >> 1) - 128; + pDst[2] = ((pSrc1[4 * 3] + pSrc1[5 * 3] + 1) >> 1) - 128; pDst[3] = ((pSrc1[6 * 3] + pSrc1[7 * 3] + 1) >> 1) - 128; + pDst[4] = ((pSrc1[8 * 3] + pSrc1[9 * 3] + 1) >> 1) - 128; pDst[5] = ((pSrc1[10 * 3] + pSrc1[11 * 3] + 1) >> 1) - 128; + pDst[6] = ((pSrc1[12 * 3] + pSrc1[13 * 3] + 1) >> 1) - 128; pDst[7] = ((pSrc1[14 * 3] + pSrc1[15 * 3] + 1) >> 1) - 128; + } + } + + void jpeg_encoder::load_quantized_coefficients(int component_num) + { + int32* q = m_quantization_tables[component_num > 0]; + int16* pDst = m_coefficient_array; + for (int i = 0; i < 64; i++) + { + sample_array_t j = m_sample_array[s_zag[i]]; + if (j < 0) + { + if ((j = -j + (*q >> 1)) < *q) + *pDst++ = 0; + else + *pDst++ = static_cast(-(j / *q)); + } + else + { + if ((j = j + (*q >> 1)) < *q) + *pDst++ = 0; + else + *pDst++ = static_cast((j / *q)); + } + q++; + } + } + + void jpeg_encoder::flush_output_buffer() + { + if (m_out_buf_left != JPGE_OUT_BUF_SIZE) + m_all_stream_writes_succeeded = m_all_stream_writes_succeeded && m_pStream->put_buf(m_out_buf, JPGE_OUT_BUF_SIZE - m_out_buf_left); + m_pOut_buf = m_out_buf; + m_out_buf_left = JPGE_OUT_BUF_SIZE; + } + + void jpeg_encoder::put_bits(uint bits, uint len) + { + m_bit_buffer |= ((uint32)bits << (24 - (m_bits_in += len))); + while (m_bits_in >= 8) + { + uint8 c; +#define JPGE_PUT_BYTE(c) { *m_pOut_buf++ = (c); if (--m_out_buf_left == 0) flush_output_buffer(); } + JPGE_PUT_BYTE(c = (uint8)((m_bit_buffer >> 16) & 0xFF)); + if (c == 0xFF) JPGE_PUT_BYTE(0); + m_bit_buffer <<= 8; + m_bits_in -= 8; + } + } + + void jpeg_encoder::code_coefficients_pass_one(int component_num) + { + if (component_num >= 3) return; // just to shut up static analysis + int i, run_len, nbits, temp1; + int16* src = m_coefficient_array; + uint32* dc_count = component_num ? m_huff_count[0 + 1] : m_huff_count[0 + 0], * ac_count = component_num ? m_huff_count[2 + 1] : m_huff_count[2 + 0]; + + temp1 = src[0] - m_last_dc_val[component_num]; + m_last_dc_val[component_num] = src[0]; + if (temp1 < 0) temp1 = -temp1; + + nbits = 0; + while (temp1) + { + nbits++; temp1 >>= 1; + } + + dc_count[nbits]++; + for (run_len = 0, i = 1; i < 64; i++) + { + if ((temp1 = m_coefficient_array[i]) == 0) + run_len++; + else + { + while (run_len >= 16) + { + ac_count[0xF0]++; + run_len -= 16; + } + if (temp1 < 0) temp1 = -temp1; + nbits = 1; + while (temp1 >>= 1) nbits++; + ac_count[(run_len << 4) + nbits]++; + run_len = 0; + } + } + if (run_len) ac_count[0]++; + } + + void jpeg_encoder::code_coefficients_pass_two(int component_num) + { + int i, j, run_len, nbits, temp1, temp2; + int16* pSrc = m_coefficient_array; + uint* codes[2]; + uint8* code_sizes[2]; + + if (component_num == 0) + { + codes[0] = m_huff_codes[0 + 0]; codes[1] = m_huff_codes[2 + 0]; + code_sizes[0] = m_huff_code_sizes[0 + 0]; code_sizes[1] = m_huff_code_sizes[2 + 0]; + } + else + { + codes[0] = m_huff_codes[0 + 1]; codes[1] = m_huff_codes[2 + 1]; + code_sizes[0] = m_huff_code_sizes[0 + 1]; code_sizes[1] = m_huff_code_sizes[2 + 1]; + } + + temp1 = temp2 = pSrc[0] - m_last_dc_val[component_num]; + m_last_dc_val[component_num] = pSrc[0]; + + if (temp1 < 0) + { + temp1 = -temp1; temp2--; + } + + nbits = 0; + while (temp1) + { + nbits++; temp1 >>= 1; + } + + put_bits(codes[0][nbits], code_sizes[0][nbits]); + if (nbits) put_bits(temp2 & ((1 << nbits) - 1), nbits); + + for (run_len = 0, i = 1; i < 64; i++) + { + if ((temp1 = m_coefficient_array[i]) == 0) + run_len++; + else + { + while (run_len >= 16) + { + put_bits(codes[1][0xF0], code_sizes[1][0xF0]); + run_len -= 16; + } + if ((temp2 = temp1) < 0) + { + temp1 = -temp1; + temp2--; + } + nbits = 1; + while (temp1 >>= 1) + nbits++; + j = (run_len << 4) + nbits; + put_bits(codes[1][j], code_sizes[1][j]); + put_bits(temp2 & ((1 << nbits) - 1), nbits); + run_len = 0; + } + } + if (run_len) + put_bits(codes[1][0], code_sizes[1][0]); + } + + void jpeg_encoder::code_block(int component_num) + { + DCT2D(m_sample_array); + load_quantized_coefficients(component_num); + if (m_pass_num == 1) + code_coefficients_pass_one(component_num); + else + code_coefficients_pass_two(component_num); + } + + void jpeg_encoder::process_mcu_row() + { + if (m_num_components == 1) + { + for (int i = 0; i < m_mcus_per_row; i++) + { + load_block_8_8_grey(i); code_block(0); + } + } + else if ((m_comp_h_samp[0] == 1) && (m_comp_v_samp[0] == 1)) + { + for (int i = 0; i < m_mcus_per_row; i++) + { + load_block_8_8(i, 0, 0); code_block(0); load_block_8_8(i, 0, 1); code_block(1); load_block_8_8(i, 0, 2); code_block(2); + } + } + else if ((m_comp_h_samp[0] == 2) && (m_comp_v_samp[0] == 1)) + { + for (int i = 0; i < m_mcus_per_row; i++) + { + load_block_8_8(i * 2 + 0, 0, 0); code_block(0); load_block_8_8(i * 2 + 1, 0, 0); code_block(0); + load_block_16_8_8(i, 1); code_block(1); load_block_16_8_8(i, 2); code_block(2); + } + } + else if ((m_comp_h_samp[0] == 2) && (m_comp_v_samp[0] == 2)) + { + for (int i = 0; i < m_mcus_per_row; i++) + { + load_block_8_8(i * 2 + 0, 0, 0); code_block(0); load_block_8_8(i * 2 + 1, 0, 0); code_block(0); + load_block_8_8(i * 2 + 0, 1, 0); code_block(0); load_block_8_8(i * 2 + 1, 1, 0); code_block(0); + load_block_16_8(i, 1); code_block(1); load_block_16_8(i, 2); code_block(2); + } + } + } + + bool jpeg_encoder::terminate_pass_one() + { + optimize_huffman_table(0 + 0, DC_LUM_CODES); optimize_huffman_table(2 + 0, AC_LUM_CODES); + if (m_num_components > 1) + { + optimize_huffman_table(0 + 1, DC_CHROMA_CODES); optimize_huffman_table(2 + 1, AC_CHROMA_CODES); + } + return second_pass_init(); + } + + bool jpeg_encoder::terminate_pass_two() + { + put_bits(0x7F, 7); + flush_output_buffer(); + emit_marker(M_EOI); + m_pass_num++; // purposely bump up m_pass_num, for debugging + return true; + } + + bool jpeg_encoder::process_end_of_image() + { + if (m_mcu_y_ofs) + { + if (m_mcu_y_ofs < 16) // check here just to shut up static analysis + { + for (int i = m_mcu_y_ofs; i < m_mcu_y; i++) + memcpy(m_mcu_lines[i], m_mcu_lines[m_mcu_y_ofs - 1], m_image_bpl_mcu); + } + + process_mcu_row(); + } + + if (m_pass_num == 1) + return terminate_pass_one(); + else + return terminate_pass_two(); + } + + void jpeg_encoder::load_mcu(const void* pSrc) + { + const uint8* Psrc = reinterpret_cast(pSrc); + + uint8* pDst = m_mcu_lines[m_mcu_y_ofs]; // OK to write up to m_image_bpl_xlt bytes to pDst + + if (m_num_components == 1) + { + if (m_image_bpp == 4) + RGBA_to_Y(pDst, Psrc, m_image_x); + else if (m_image_bpp == 3) + RGB_to_Y(pDst, Psrc, m_image_x); + else + memcpy(pDst, Psrc, m_image_x); + } + else + { + if (m_image_bpp == 4) + RGBA_to_YCC(pDst, Psrc, m_image_x); + else if (m_image_bpp == 3) + RGB_to_YCC(pDst, Psrc, m_image_x); + else + Y_to_YCC(pDst, Psrc, m_image_x); + } + + // Possibly duplicate pixels at end of scanline if not a multiple of 8 or 16 + if (m_num_components == 1) + memset(m_mcu_lines[m_mcu_y_ofs] + m_image_bpl_xlt, pDst[m_image_bpl_xlt - 1], m_image_x_mcu - m_image_x); + else + { + const uint8 y = pDst[m_image_bpl_xlt - 3 + 0], cb = pDst[m_image_bpl_xlt - 3 + 1], cr = pDst[m_image_bpl_xlt - 3 + 2]; + uint8* q = m_mcu_lines[m_mcu_y_ofs] + m_image_bpl_xlt; + for (int i = m_image_x; i < m_image_x_mcu; i++) + { + *q++ = y; *q++ = cb; *q++ = cr; + } + } + + if (++m_mcu_y_ofs == m_mcu_y) + { + process_mcu_row(); + m_mcu_y_ofs = 0; + } + } + + void jpeg_encoder::clear() + { + m_mcu_lines[0] = NULL; + m_pass_num = 0; + m_all_stream_writes_succeeded = true; + } + + jpeg_encoder::jpeg_encoder() + { + clear(); + } + + jpeg_encoder::~jpeg_encoder() + { + deinit(); + } + + bool jpeg_encoder::init(output_stream* pStream, int width, int height, int src_channels, const params& comp_params) + { + deinit(); + if (((!pStream) || (width < 1) || (height < 1)) || ((src_channels != 1) && (src_channels != 3) && (src_channels != 4)) || (!comp_params.check())) return false; + m_pStream = pStream; + m_params = comp_params; + return jpg_open(width, height, src_channels); + } + + void jpeg_encoder::deinit() + { + jpge_free(m_mcu_lines[0]); + clear(); + } + + bool jpeg_encoder::process_scanline(const void* pScanline) + { + if ((m_pass_num < 1) || (m_pass_num > 2)) return false; + if (m_all_stream_writes_succeeded) + { + if (!pScanline) + { + if (!process_end_of_image()) return false; + } + else + { + load_mcu(pScanline); + } + } + return m_all_stream_writes_succeeded; + } + + // Higher level wrappers/examples (optional). +#include + + class cfile_stream : public output_stream + { + cfile_stream(const cfile_stream&); + cfile_stream& operator= (const cfile_stream&); + + FILE* m_pFile; + bool m_bStatus; + + public: + cfile_stream() : m_pFile(NULL), m_bStatus(false) { } + + virtual ~cfile_stream() + { + close(); + } + + bool open(const char* pFilename) + { + close(); + m_pFile = fopen(pFilename, "wb"); + m_bStatus = (m_pFile != NULL); + return m_bStatus; + } + + bool close() + { + if (m_pFile) + { + if (fclose(m_pFile) == EOF) + { + m_bStatus = false; + } + m_pFile = NULL; + } + return m_bStatus; + } + + virtual bool put_buf(const void* pBuf, int len) + { + m_bStatus = m_bStatus && (fwrite(pBuf, len, 1, m_pFile) == 1); + return m_bStatus; + } + + uint get_size() const + { + return m_pFile ? ftell(m_pFile) : 0; + } + }; + + // Writes JPEG image to file. + bool compress_image_to_jpeg_file(const char* pFilename, int width, int height, int num_channels, const uint8* pImage_data, const params& comp_params) + { + cfile_stream dst_stream; + if (!dst_stream.open(pFilename)) + return false; + + jpge::jpeg_encoder dst_image; + if (!dst_image.init(&dst_stream, width, height, num_channels, comp_params)) + return false; + + for (uint pass_index = 0; pass_index < dst_image.get_total_passes(); pass_index++) + { + for (int i = 0; i < height; i++) + { + const uint8* pBuf = pImage_data + i * width * num_channels; + if (!dst_image.process_scanline(pBuf)) + return false; + } + if (!dst_image.process_scanline(NULL)) + return false; + } + + dst_image.deinit(); + + return dst_stream.close(); + } + + class memory_stream : public output_stream + { + memory_stream(const memory_stream&); + memory_stream& operator= (const memory_stream&); + + uint8* m_pBuf; + uint m_buf_size, m_buf_ofs; + + public: + memory_stream(void* pBuf, uint buf_size) : m_pBuf(static_cast(pBuf)), m_buf_size(buf_size), m_buf_ofs(0) { } + + virtual ~memory_stream() { } + + virtual bool put_buf(const void* pBuf, int len) + { + uint buf_remaining = m_buf_size - m_buf_ofs; + if ((uint)len > buf_remaining) + return false; + memcpy(m_pBuf + m_buf_ofs, pBuf, len); + m_buf_ofs += len; + return true; + } + + uint get_size() const + { + return m_buf_ofs; + } + }; + + bool compress_image_to_jpeg_file_in_memory(void* pDstBuf, int& buf_size, int width, int height, int num_channels, const uint8* pImage_data, const params& comp_params) + { + if ((!pDstBuf) || (!buf_size)) + return false; + + memory_stream dst_stream(pDstBuf, buf_size); + + buf_size = 0; + + jpge::jpeg_encoder dst_image; + if (!dst_image.init(&dst_stream, width, height, num_channels, comp_params)) + return false; + + for (uint pass_index = 0; pass_index < dst_image.get_total_passes(); pass_index++) + { + for (int i = 0; i < height; i++) + { + const uint8* pScanline = pImage_data + i * width * num_channels; + if (!dst_image.process_scanline(pScanline)) + return false; + } + if (!dst_image.process_scanline(NULL)) + return false; + } + + dst_image.deinit(); + + buf_size = dst_stream.get_size(); + return true; + } } // namespace jpge diff --git a/ext/jpge/jpge.h b/ext/jpge/jpge.h index 262bbfbb38..b98a4a6413 100644 --- a/ext/jpge/jpge.h +++ b/ext/jpge/jpge.h @@ -1,168 +1,172 @@ // jpge.h - C++ class for JPEG compression. -// Public domain, Rich Geldreich +// Public Domain or Apache 2.0, Richard Geldreich // Alex Evans: Added RGBA support, linear memory allocator. #ifndef JPEG_ENCODER_H #define JPEG_ENCODER_H namespace jpge { - typedef unsigned char uint8; - typedef signed short int16; - typedef signed int int32; - typedef unsigned short uint16; - typedef unsigned int uint32; - typedef unsigned int uint; - - // JPEG chroma subsampling factors. Y_ONLY (grayscale images) and H2V2 (color images) are the most common. - enum subsampling_t { Y_ONLY = 0, H1V1 = 1, H2V1 = 2, H2V2 = 3 }; + typedef unsigned char uint8; + typedef signed short int16; + typedef signed int int32; + typedef unsigned short uint16; + typedef unsigned int uint32; + typedef unsigned int uint; - // JPEG compression parameters structure. - struct params - { - inline params() : m_quality(85), m_subsampling(H2V2), m_no_chroma_discrim_flag(false), m_two_pass_flag(false) { } + // JPEG chroma subsampling factors. Y_ONLY (grayscale images) and H2V2 (color images) are the most common. + enum subsampling_t { Y_ONLY = 0, H1V1 = 1, H2V1 = 2, H2V2 = 3 }; - inline bool check() const - { - if ((m_quality < 1) || (m_quality > 100)) return false; - if ((uint)m_subsampling > (uint)H2V2) return false; - return true; - } + // JPEG compression parameters structure. + struct params + { + inline params() : m_quality(85), m_subsampling(H2V2), m_no_chroma_discrim_flag(false), m_two_pass_flag(false), m_use_std_tables(false) { } - // Quality: 1-100, higher is better. Typical values are around 50-95. - int m_quality; + inline bool check() const + { + if ((m_quality < 1) || (m_quality > 100)) return false; + if ((uint)m_subsampling > (uint)H2V2) return false; + return true; + } - // m_subsampling: - // 0 = Y (grayscale) only - // 1 = YCbCr, no subsampling (H1V1, YCbCr 1x1x1, 3 blocks per MCU) - // 2 = YCbCr, H2V1 subsampling (YCbCr 2x1x1, 4 blocks per MCU) - // 3 = YCbCr, H2V2 subsampling (YCbCr 4x1x1, 6 blocks per MCU-- very common) - subsampling_t m_subsampling; + // Quality: 1-100, higher is better. Typical values are around 50-95. + int m_quality; - // Disables CbCr discrimination - only intended for testing. - // If true, the Y quantization table is also used for the CbCr channels. - bool m_no_chroma_discrim_flag; + // m_subsampling: + // 0 = Y (grayscale) only + // 1 = YCbCr, no subsampling (H1V1, YCbCr 1x1x1, 3 blocks per MCU) + // 2 = YCbCr, H2V1 subsampling (YCbCr 2x1x1, 4 blocks per MCU) + // 3 = YCbCr, H2V2 subsampling (YCbCr 4x1x1, 6 blocks per MCU-- very common) + subsampling_t m_subsampling; - bool m_two_pass_flag; - }; - - // Writes JPEG image to a file. - // num_channels must be 1 (Y) or 3 (RGB), image pitch must be width*num_channels. - bool compress_image_to_jpeg_file(const char *pFilename, int width, int height, int num_channels, const uint8 *pImage_data, const params &comp_params = params()); + // Disables CbCr discrimination - only intended for testing. + // If true, the Y quantization table is also used for the CbCr channels. + bool m_no_chroma_discrim_flag; - // Writes JPEG image to memory buffer. - // On entry, buf_size is the size of the output buffer pointed at by pBuf, which should be at least ~1024 bytes. - // If return value is true, buf_size will be set to the size of the compressed data. - bool compress_image_to_jpeg_file_in_memory(void *pBuf, int &buf_size, int width, int height, int num_channels, const uint8 *pImage_data, const params &comp_params = params()); - - // Output stream abstract class - used by the jpeg_encoder class to write to the output stream. - // put_buf() is generally called with len==JPGE_OUT_BUF_SIZE bytes, but for headers it'll be called with smaller amounts. - class output_stream - { - public: - virtual ~output_stream() { }; - virtual bool put_buf(const void* Pbuf, int len) = 0; - template inline bool put_obj(const T& obj) { return put_buf(&obj, sizeof(T)); } - }; - - // Lower level jpeg_encoder class - useful if more control is needed than the above helper functions. - class jpeg_encoder - { - public: - jpeg_encoder(); - ~jpeg_encoder(); + bool m_two_pass_flag; - // Initializes the compressor. - // pStream: The stream object to use for writing compressed data. - // params - Compression parameters structure, defined above. - // width, height - Image dimensions. - // channels - May be 1, or 3. 1 indicates grayscale, 3 indicates RGB source data. - // Returns false on out of memory or if a stream write fails. - bool init(output_stream *pStream, int width, int height, int src_channels, const params &comp_params = params()); - - const params &get_params() const { return m_params; } - - // Deinitializes the compressor, freeing any allocated memory. May be called at any time. - void deinit(); + // By default we use the same quantization tables as mozjpeg's default. + // Set to true to use the traditional tables from JPEG Annex K. + bool m_use_std_tables; + }; - uint get_total_passes() const { return m_params.m_two_pass_flag ? 2 : 1; } - inline uint get_cur_pass() { return m_pass_num; } + // Writes JPEG image to a file. + // num_channels must be 1 (Y) or 3 (RGB), image pitch must be width*num_channels. + bool compress_image_to_jpeg_file(const char* pFilename, int width, int height, int num_channels, const uint8* pImage_data, const params& comp_params = params()); - // Call this method with each source scanline. - // width * src_channels bytes per scanline is expected (RGB or Y format). - // You must call with NULL after all scanlines are processed to finish compression. - // Returns false on out of memory or if a stream write fails. - bool process_scanline(const void* pScanline); - - private: - jpeg_encoder(const jpeg_encoder &); - jpeg_encoder &operator =(const jpeg_encoder &); + // Writes JPEG image to memory buffer. + // On entry, buf_size is the size of the output buffer pointed at by pBuf, which should be at least ~1024 bytes. + // If return value is true, buf_size will be set to the size of the compressed data. + bool compress_image_to_jpeg_file_in_memory(void* pBuf, int& buf_size, int width, int height, int num_channels, const uint8* pImage_data, const params& comp_params = params()); - typedef int32 sample_array_t; - - output_stream *m_pStream; - params m_params; - uint8 m_num_components; - uint8 m_comp_h_samp[3], m_comp_v_samp[3]; - int m_image_x, m_image_y, m_image_bpp, m_image_bpl; - int m_image_x_mcu, m_image_y_mcu; - int m_image_bpl_xlt, m_image_bpl_mcu; - int m_mcus_per_row; - int m_mcu_x, m_mcu_y; - uint8 *m_mcu_lines[16]; - uint8 m_mcu_y_ofs; - sample_array_t m_sample_array[64]; - int16 m_coefficient_array[64]; - int32 m_quantization_tables[2][64]; - uint m_huff_codes[4][256]; - uint8 m_huff_code_sizes[4][256]; - uint8 m_huff_bits[4][17]; - uint8 m_huff_val[4][256]; - uint32 m_huff_count[4][256]; - int m_last_dc_val[3]; - enum { JPGE_OUT_BUF_SIZE = 2048 }; - uint8 m_out_buf[JPGE_OUT_BUF_SIZE]; - uint8 *m_pOut_buf; - uint m_out_buf_left; - uint32 m_bit_buffer; - uint m_bits_in; - uint8 m_pass_num; - bool m_all_stream_writes_succeeded; - - void optimize_huffman_table(int table_num, int table_len); - void emit_byte(uint8 i); - void emit_word(uint i); - void emit_marker(int marker); - void emit_jfif_app0(); - void emit_dqt(); - void emit_sof(); - void emit_dht(uint8 *bits, uint8 *val, int index, bool ac_flag); - void emit_dhts(); - void emit_sos(); - void emit_markers(); - void compute_huffman_table(uint *codes, uint8 *code_sizes, uint8 *bits, uint8 *val); - void compute_quant_table(int32 *dst, int16 *src); - void adjust_quant_table(int32 *dst, int32 *src); - void first_pass_init(); - bool second_pass_init(); - bool jpg_open(int p_x_res, int p_y_res, int src_channels); - void load_block_8_8_grey(int x); - void load_block_8_8(int x, int y, int c); - void load_block_16_8(int x, int c); - void load_block_16_8_8(int x, int c); - void load_quantized_coefficients(int component_num); - void flush_output_buffer(); - void put_bits(uint bits, uint len); - void code_coefficients_pass_one(int component_num); - void code_coefficients_pass_two(int component_num); - void code_block(int component_num); - void process_mcu_row(); - bool terminate_pass_one(); - bool terminate_pass_two(); - bool process_end_of_image(); - void load_mcu(const void* src); - void clear(); - void init(); - }; + // Output stream abstract class - used by the jpeg_encoder class to write to the output stream. + // put_buf() is generally called with len==JPGE_OUT_BUF_SIZE bytes, but for headers it'll be called with smaller amounts. + class output_stream + { + public: + virtual ~output_stream() { }; + virtual bool put_buf(const void* Pbuf, int len) = 0; + template inline bool put_obj(const T& obj) { return put_buf(&obj, sizeof(T)); } + }; + + // Lower level jpeg_encoder class - useful if more control is needed than the above helper functions. + class jpeg_encoder + { + public: + jpeg_encoder(); + ~jpeg_encoder(); + + // Initializes the compressor. + // pStream: The stream object to use for writing compressed data. + // params - Compression parameters structure, defined above. + // width, height - Image dimensions. + // channels - May be 1, or 3. 1 indicates grayscale, 3 indicates RGB source data. + // Returns false on out of memory or if a stream write fails. + bool init(output_stream* pStream, int width, int height, int src_channels, const params& comp_params = params()); + + const params& get_params() const { return m_params; } + + // Deinitializes the compressor, freeing any allocated memory. May be called at any time. + void deinit(); + + uint get_total_passes() const { return m_params.m_two_pass_flag ? 2 : 1; } + inline uint get_cur_pass() { return m_pass_num; } + + // Call this method with each source scanline. + // width * src_channels bytes per scanline is expected (RGB or Y format). + // You must call with NULL after all scanlines are processed to finish compression. + // Returns false on out of memory or if a stream write fails. + bool process_scanline(const void* pScanline); + + private: + jpeg_encoder(const jpeg_encoder&); + jpeg_encoder& operator =(const jpeg_encoder&); + + typedef int32 sample_array_t; + + output_stream* m_pStream; + params m_params; + uint8 m_num_components; + uint8 m_comp_h_samp[3], m_comp_v_samp[3]; + int m_image_x, m_image_y, m_image_bpp, m_image_bpl; + int m_image_x_mcu, m_image_y_mcu; + int m_image_bpl_xlt, m_image_bpl_mcu; + int m_mcus_per_row; + int m_mcu_x, m_mcu_y; + uint8* m_mcu_lines[16]; + uint8 m_mcu_y_ofs; + sample_array_t m_sample_array[64]; + int16 m_coefficient_array[64]; + int32 m_quantization_tables[2][64]; + uint m_huff_codes[4][256]; + uint8 m_huff_code_sizes[4][256]; + uint8 m_huff_bits[4][17]; + uint8 m_huff_val[4][256]; + uint32 m_huff_count[4][256]; + int m_last_dc_val[3]; + enum { JPGE_OUT_BUF_SIZE = 2048 }; + uint8 m_out_buf[JPGE_OUT_BUF_SIZE]; + uint8* m_pOut_buf; + uint m_out_buf_left; + uint32 m_bit_buffer; + uint m_bits_in; + uint8 m_pass_num; + bool m_all_stream_writes_succeeded; + + void optimize_huffman_table(int table_num, int table_len); + void emit_byte(uint8 i); + void emit_word(uint i); + void emit_marker(int marker); + void emit_jfif_app0(); + void emit_dqt(); + void emit_sof(); + void emit_dht(uint8* bits, uint8* val, int index, bool ac_flag); + void emit_dhts(); + void emit_sos(); + void emit_markers(); + void compute_huffman_table(uint* codes, uint8* code_sizes, uint8* bits, uint8* val); + void compute_quant_table(int32* dst, int16* src); + void adjust_quant_table(int32* dst, int32* src); + void first_pass_init(); + bool second_pass_init(); + bool jpg_open(int p_x_res, int p_y_res, int src_channels); + void load_block_8_8_grey(int x); + void load_block_8_8(int x, int y, int c); + void load_block_16_8(int x, int c); + void load_block_16_8_8(int x, int c); + void load_quantized_coefficients(int component_num); + void flush_output_buffer(); + void put_bits(uint bits, uint len); + void code_coefficients_pass_one(int component_num); + void code_coefficients_pass_two(int component_num); + void code_block(int component_num); + void process_mcu_row(); + bool terminate_pass_one(); + bool terminate_pass_two(); + bool process_end_of_image(); + void load_mcu(const void* src); + void clear(); + void init(); + }; } // namespace jpge diff --git a/ext/jpge/publicdomain.txt b/ext/jpge/publicdomain.txt deleted file mode 100644 index 639860563a..0000000000 --- a/ext/jpge/publicdomain.txt +++ /dev/null @@ -1 +0,0 @@ -This is https://code.google.com/p/jpeg-compressor/ , public domain code. \ No newline at end of file diff --git a/ext/miniupnp-build/Android.mk b/ext/miniupnp-build/Android.mk index 6675d326a5..5412b339d4 100644 --- a/ext/miniupnp-build/Android.mk +++ b/ext/miniupnp-build/Android.mk @@ -28,9 +28,9 @@ LOCAL_CPPFLAGS := -fno-exceptions -std=gnu++11 -fno-rtti -Wno-reorder LOCAL_C_INCLUDES := $(LOCAL_PATH)/.. $(LOCAL_PATH)/../miniupnp .. ifeq ($(findstring armeabi-v7a,$(TARGET_ARCH_ABI)),armeabi-v7a) -LOCAL_CFLAGS := $(LOCAL_CFLAGS) -DARM -DARMEABI_V7A +LOCAL_CFLAGS := $(LOCAL_CFLAGS) else ifeq ($(TARGET_ARCH_ABI),armeabi) -LOCAL_CFLAGS := $(LOCAL_CFLAGS) -DARM -DARMEABI -march=armv6 +LOCAL_CFLAGS := $(LOCAL_CFLAGS) -march=armv6 else ifeq ($(TARGET_ARCH_ABI),x86) LOCAL_CFLAGS := $(LOCAL_CFLAGS) -D_M_IX86 else ifeq ($(TARGET_ARCH_ABI),x86_64) diff --git a/ext/native/tools/zimtool/zimtool.vcxproj b/ext/native/tools/zimtool/zimtool.vcxproj index 5c91fc8a23..ee9eca5b90 100644 --- a/ext/native/tools/zimtool/zimtool.vcxproj +++ b/ext/native/tools/zimtool/zimtool.vcxproj @@ -71,7 +71,7 @@ true true true - NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + NDEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) true ../../../../;../../ext;../../;../../../../ext;../prebuilt MultiThreaded @@ -91,7 +91,7 @@ Level3 Disabled true - _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + _DEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) true ../../../../;../../ext;../../;../../../../ext;../prebuilt MultiThreadedDebug @@ -105,4 +105,4 @@ - + \ No newline at end of file diff --git a/ext/snappy/snappy-internal.h b/ext/snappy/snappy-internal.h index 1e1c307fef..720ccd8282 100644 --- a/ext/snappy/snappy-internal.h +++ b/ext/snappy/snappy-internal.h @@ -46,16 +46,16 @@ class WorkingMemory { // Allocates and clears a hash table using memory in "*this", // stores the number of buckets in "*table_size" and returns a pointer to // the base of the hash table. - uint16* GetHashTable(size_t fragment_size, int* table_size) const; + uint16_t* GetHashTable(size_t fragment_size, int* table_size) const; char* GetScratchInput() const { return input_; } char* GetScratchOutput() const { return output_; } private: - char* mem_; // the allocated memory, never nullptr - size_t size_; // the size of the allocated memory, never 0 - uint16* table_; // the pointer to the hashtable - char* input_; // the pointer to the input scratch buffer - char* output_; // the pointer to the output scratch buffer + char* mem_; // the allocated memory, never nullptr + size_t size_; // the size of the allocated memory, never 0 + uint16_t* table_; // the pointer to the hashtable + char* input_; // the pointer to the input scratch buffer + char* output_; // the pointer to the output scratch buffer // No copying WorkingMemory(const WorkingMemory&); @@ -76,7 +76,7 @@ class WorkingMemory { char* CompressFragment(const char* input, size_t input_length, char* op, - uint16* table, + uint16_t* table, const int table_size); // Find the largest n such that @@ -89,12 +89,18 @@ char* CompressFragment(const char* input, // Does not read *(s1 + (s2_limit - s2)) or beyond. // Requires that s2_limit >= s2. // +// In addition populate *data with the next 5 bytes from the end of the match. +// This is only done if 8 bytes are available (s2_limit - s2 >= 8). The point is +// that on some arch's this can be done faster in this routine than subsequent +// loading from s2 + n. +// // Separate implementation for 64-bit, little-endian cpus. #if !defined(SNAPPY_IS_BIG_ENDIAN) && \ - (defined(ARCH_K8) || defined(ARCH_PPC) || defined(ARCH_ARM)) + (defined(__x86_64__) || defined(_M_X64) || defined(ARCH_PPC) || defined(ARCH_ARM)) static inline std::pair FindMatchLength(const char* s1, const char* s2, - const char* s2_limit) { + const char* s2_limit, + uint64_t* data) { assert(s2_limit >= s2); size_t matched = 0; @@ -103,12 +109,71 @@ static inline std::pair FindMatchLength(const char* s1, // uncommon code paths that determine, without extra effort, whether the match // length is less than 8. In short, we are hoping to avoid a conditional // branch, and perhaps get better code layout from the C++ compiler. - if (SNAPPY_PREDICT_TRUE(s2 <= s2_limit - 8)) { - uint64 a1 = UNALIGNED_LOAD64(s1); - uint64 a2 = UNALIGNED_LOAD64(s2); - if (a1 != a2) { - return std::pair(Bits::FindLSBSetNonZero64(a1 ^ a2) >> 3, - true); + if (SNAPPY_PREDICT_TRUE(s2 <= s2_limit - 16)) { + uint64_t a1 = UNALIGNED_LOAD64(s1); + uint64_t a2 = UNALIGNED_LOAD64(s2); + if (SNAPPY_PREDICT_TRUE(a1 != a2)) { + // This code is critical for performance. The reason is that it determines + // how much to advance `ip` (s2). This obviously depends on both the loads + // from the `candidate` (s1) and `ip`. Furthermore the next `candidate` + // depends on the advanced `ip` calculated here through a load, hash and + // new candidate hash lookup (a lot of cycles). This makes s1 (ie. + // `candidate`) the variable that limits throughput. This is the reason we + // go through hoops to have this function update `data` for the next iter. + // The straightforward code would use *data, given by + // + // *data = UNALIGNED_LOAD64(s2 + matched_bytes) (Latency of 5 cycles), + // + // as input for the hash table lookup to find next candidate. However + // this forces the load on the data dependency chain of s1, because + // matched_bytes directly depends on s1. However matched_bytes is 0..7, so + // we can also calculate *data by + // + // *data = AlignRight(UNALIGNED_LOAD64(s2), UNALIGNED_LOAD64(s2 + 8), + // matched_bytes); + // + // The loads do not depend on s1 anymore and are thus off the bottleneck. + // The straightforward implementation on x86_64 would be to use + // + // shrd rax, rdx, cl (cl being matched_bytes * 8) + // + // unfortunately shrd with a variable shift has a 4 cycle latency. So this + // only wins 1 cycle. The BMI2 shrx instruction is a 1 cycle variable + // shift instruction but can only shift 64 bits. If we focus on just + // obtaining the least significant 4 bytes, we can obtain this by + // + // *data = ConditionalMove(matched_bytes < 4, UNALIGNED_LOAD64(s2), + // UNALIGNED_LOAD64(s2 + 4) >> ((matched_bytes & 3) * 8); + // + // Writen like above this is not a big win, the conditional move would be + // a cmp followed by a cmov (2 cycles) followed by a shift (1 cycle). + // However matched_bytes < 4 is equal to + // static_cast(xorval) != 0. Writen that way, the conditional + // move (2 cycles) can execute in parallel with FindLSBSetNonZero64 + // (tzcnt), which takes 3 cycles. + uint64_t xorval = a1 ^ a2; + int shift = Bits::FindLSBSetNonZero64(xorval); + size_t matched_bytes = shift >> 3; +#ifndef __x86_64__ + *data = UNALIGNED_LOAD64(s2 + matched_bytes); +#else + // Ideally this would just be + // + // a2 = static_cast(xorval) == 0 ? a3 : a2; + // + // However clang correctly infers that the above statement participates on + // a critical data dependency chain and thus, unfortunately, refuses to + // use a conditional move (it's tuned to cut data dependencies). In this + // case there is a longer parallel chain anyway AND this will be fairly + // unpredictable. + uint64_t a3 = UNALIGNED_LOAD64(s2 + 4); + asm("testl %k2, %k2\n\t" + "cmovzq %1, %0\n\t" + : "+r"(a2) + : "r"(a3), "r"(xorval)); + *data = a2 >> (shift & (3 * 8)); +#endif + return std::pair(matched_bytes, true); } else { matched = 8; s2 += 8; @@ -119,14 +184,27 @@ static inline std::pair FindMatchLength(const char* s1, // time until we find a 64-bit block that doesn't match; then we find // the first non-matching bit and use that to calculate the total // length of the match. - while (SNAPPY_PREDICT_TRUE(s2 <= s2_limit - 8)) { - if (UNALIGNED_LOAD64(s2) == UNALIGNED_LOAD64(s1 + matched)) { + while (SNAPPY_PREDICT_TRUE(s2 <= s2_limit - 16)) { + uint64_t a1 = UNALIGNED_LOAD64(s1 + matched); + uint64_t a2 = UNALIGNED_LOAD64(s2); + if (a1 == a2) { s2 += 8; matched += 8; } else { - uint64 x = UNALIGNED_LOAD64(s2) ^ UNALIGNED_LOAD64(s1 + matched); - int matching_bits = Bits::FindLSBSetNonZero64(x); - matched += matching_bits >> 3; + uint64_t xorval = a1 ^ a2; + int shift = Bits::FindLSBSetNonZero64(xorval); + size_t matched_bytes = shift >> 3; +#ifndef __x86_64__ + *data = UNALIGNED_LOAD64(s2 + matched_bytes); +#else + uint64_t a3 = UNALIGNED_LOAD64(s2 + 4); + asm("testl %k2, %k2\n\t" + "cmovzq %1, %0\n\t" + : "+r"(a2) + : "r"(a3), "r"(xorval)); + *data = a2 >> (shift & (3 * 8)); +#endif + matched += matched_bytes; assert(matched >= 8); return std::pair(matched, false); } @@ -136,6 +214,9 @@ static inline std::pair FindMatchLength(const char* s1, ++s2; ++matched; } else { + if (s2 <= s2_limit - 8) { + *data = UNALIGNED_LOAD64(s2); + } return std::pair(matched, matched < 8); } } @@ -144,7 +225,8 @@ static inline std::pair FindMatchLength(const char* s1, #else static inline std::pair FindMatchLength(const char* s1, const char* s2, - const char* s2_limit) { + const char* s2_limit, + uint64_t* data) { // Implementation based on the x86-64 version, above. assert(s2_limit >= s2); int matched = 0; @@ -155,15 +237,17 @@ static inline std::pair FindMatchLength(const char* s1, matched += 4; } if (LittleEndian::IsLittleEndian() && s2 <= s2_limit - 4) { - uint32 x = UNALIGNED_LOAD32(s2) ^ UNALIGNED_LOAD32(s1 + matched); + uint32_t x = UNALIGNED_LOAD32(s2) ^ UNALIGNED_LOAD32(s1 + matched); int matching_bits = Bits::FindLSBSetNonZero(x); matched += matching_bits >> 3; + s2 += matching_bits >> 3; } else { while ((s2 < s2_limit) && (s1[matched] == *s2)) { ++s2; ++matched; } } + if (s2 <= s2_limit - 8) *data = LittleEndian::Load64(s2); return std::pair(matched, matched < 8); } #endif @@ -190,7 +274,8 @@ static const int kMaximumTagLength = 5; // COPY_4_BYTE_OFFSET plus the actual o // because of efficiency reasons: // (1) Extracting a byte is faster than a bit-field // (2) It properly aligns copy offset so we do not need a <<8 -static const uint16 char_table[256] = { +static constexpr uint16_t char_table[256] = { + // clang-format off 0x0001, 0x0804, 0x1001, 0x2001, 0x0002, 0x0805, 0x1002, 0x2002, 0x0003, 0x0806, 0x1003, 0x2003, 0x0004, 0x0807, 0x1004, 0x2004, 0x0005, 0x0808, 0x1005, 0x2005, 0x0006, 0x0809, 0x1006, 0x2006, @@ -222,7 +307,8 @@ static const uint16 char_table[256] = { 0x0039, 0x0f04, 0x1039, 0x2039, 0x003a, 0x0f05, 0x103a, 0x203a, 0x003b, 0x0f06, 0x103b, 0x203b, 0x003c, 0x0f07, 0x103c, 0x203c, 0x0801, 0x0f08, 0x103d, 0x203d, 0x1001, 0x0f09, 0x103e, 0x203e, - 0x1801, 0x0f0a, 0x103f, 0x203f, 0x2001, 0x0f0b, 0x1040, 0x2040 + 0x1801, 0x0f0a, 0x103f, 0x203f, 0x2001, 0x0f0b, 0x1040, 0x2040, + // clang-format on }; } // end namespace internal diff --git a/ext/snappy/snappy-sinksource.cpp b/ext/snappy/snappy-sinksource.cpp index 369a13215b..8214964a7e 100644 --- a/ext/snappy/snappy-sinksource.cpp +++ b/ext/snappy/snappy-sinksource.cpp @@ -26,23 +26,31 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -#include +#include +#include #include "snappy-sinksource.h" namespace snappy { -Source::~Source() { } +Source::~Source() = default; -Sink::~Sink() { } +Sink::~Sink() = default; char* Sink::GetAppendBuffer(size_t length, char* scratch) { + // TODO: Switch to [[maybe_unused]] when we can assume C++17. + (void)length; + return scratch; } char* Sink::GetAppendBufferVariable( size_t min_size, size_t desired_size_hint, char* scratch, size_t scratch_size, size_t* allocated_size) { + // TODO: Switch to [[maybe_unused]] when we can assume C++17. + (void)min_size; + (void)desired_size_hint; + *allocated_size = scratch_size; return scratch; } @@ -55,7 +63,7 @@ void Sink::AppendAndTakeOwnership( (*deleter)(deleter_arg, bytes, n); } -ByteArraySource::~ByteArraySource() { } +ByteArraySource::~ByteArraySource() = default; size_t ByteArraySource::Available() const { return left_; } @@ -74,22 +82,26 @@ UncheckedByteArraySink::~UncheckedByteArraySink() { } void UncheckedByteArraySink::Append(const char* data, size_t n) { // Do no copying if the caller filled in the result of GetAppendBuffer() if (data != dest_) { - memcpy(dest_, data, n); + std::memcpy(dest_, data, n); } dest_ += n; } char* UncheckedByteArraySink::GetAppendBuffer(size_t len, char* scratch) { + // TODO: Switch to [[maybe_unused]] when we can assume C++17. + (void)len; + (void)scratch; + return dest_; } void UncheckedByteArraySink::AppendAndTakeOwnership( - char* data, size_t n, + char* bytes, size_t n, void (*deleter)(void*, const char*, size_t), void *deleter_arg) { - if (data != dest_) { - memcpy(dest_, data, n); - (*deleter)(deleter_arg, data, n); + if (bytes != dest_) { + std::memcpy(dest_, bytes, n); + (*deleter)(deleter_arg, bytes, n); } dest_ += n; } @@ -97,6 +109,11 @@ void UncheckedByteArraySink::AppendAndTakeOwnership( char* UncheckedByteArraySink::GetAppendBufferVariable( size_t min_size, size_t desired_size_hint, char* scratch, size_t scratch_size, size_t* allocated_size) { + // TODO: Switch to [[maybe_unused]] when we can assume C++17. + (void)min_size; + (void)scratch; + (void)scratch_size; + *allocated_size = desired_size_hint; return dest_; } diff --git a/ext/snappy/snappy-sinksource.h b/ext/snappy/snappy-sinksource.h index 8afcdaaa2c..3c74e1bb6e 100644 --- a/ext/snappy/snappy-sinksource.h +++ b/ext/snappy/snappy-sinksource.h @@ -146,10 +146,10 @@ class Source { class ByteArraySource : public Source { public: ByteArraySource(const char* p, size_t n) : ptr_(p), left_(n) { } - virtual ~ByteArraySource(); - virtual size_t Available() const; - virtual const char* Peek(size_t* len); - virtual void Skip(size_t n); + ~ByteArraySource() override; + size_t Available() const override; + const char* Peek(size_t* len) override; + void Skip(size_t n) override; private: const char* ptr_; size_t left_; @@ -159,15 +159,15 @@ class ByteArraySource : public Source { class UncheckedByteArraySink : public Sink { public: explicit UncheckedByteArraySink(char* dest) : dest_(dest) { } - virtual ~UncheckedByteArraySink(); - virtual void Append(const char* data, size_t n); - virtual char* GetAppendBuffer(size_t len, char* scratch); - virtual char* GetAppendBufferVariable( + ~UncheckedByteArraySink() override; + void Append(const char* data, size_t n) override; + char* GetAppendBuffer(size_t len, char* scratch) override; + char* GetAppendBufferVariable( size_t min_size, size_t desired_size_hint, char* scratch, - size_t scratch_size, size_t* allocated_size); - virtual void AppendAndTakeOwnership( + size_t scratch_size, size_t* allocated_size) override; + void AppendAndTakeOwnership( char* bytes, size_t n, void (*deleter)(void*, const char*, size_t), - void *deleter_arg); + void *deleter_arg) override; // Return the current output pointer so that a caller can see how // many bytes were produced. diff --git a/ext/snappy/snappy-stubs-internal.cpp b/ext/snappy/snappy-stubs-internal.cpp index 66ed2e9039..0bc8c2d344 100644 --- a/ext/snappy/snappy-stubs-internal.cpp +++ b/ext/snappy/snappy-stubs-internal.cpp @@ -33,7 +33,7 @@ namespace snappy { -void Varint::Append32(std::string* s, uint32 value) { +void Varint::Append32(std::string* s, uint32_t value) { char buf[Varint::kMax32]; const char* p = Varint::Encode32(buf, value); s->append(buf, p - buf); diff --git a/ext/snappy/snappy-stubs-internal.h b/ext/snappy/snappy-stubs-internal.h index 48698958e7..5936cb72f8 100644 --- a/ext/snappy/snappy-stubs-internal.h +++ b/ext/snappy/snappy-stubs-internal.h @@ -35,11 +35,13 @@ #include "config.h" #endif -#include +#include -#include -#include -#include +#include +#include +#include +#include +#include #ifdef HAVE_SYS_MMAN_H #include @@ -67,19 +69,11 @@ #include "snappy-stubs-public.h" -#if defined(__x86_64__) - -// Enable 64-bit optimized versions of some routines. -#define ARCH_K8 1 - -#elif defined(__ppc64__) - +// Used to enable 64-bit optimized versions of some routines. +#if defined(__PPC64__) || defined(__powerpc64__) #define ARCH_PPC 1 - -#elif defined(__aarch64__) - +#elif defined(__aarch64__) || defined(_M_ARM64) #define ARCH_ARM 1 - #endif // Needed by OS X, among others. @@ -93,7 +87,7 @@ #ifdef ARRAYSIZE #undef ARRAYSIZE #endif -#define ARRAYSIZE(a) (sizeof(a) / sizeof(*(a))) +#define ARRAYSIZE(a) int{sizeof(a) / sizeof(*(a))} // Static prediction hints. #ifdef HAVE_BUILTIN_EXPECT @@ -104,6 +98,13 @@ #define SNAPPY_PREDICT_TRUE(x) x #endif +// Inlining hints. +#ifdef HAVE_ATTRIBUTE_ALWAYS_INLINE +#define SNAPPY_ATTRIBUTE_ALWAYS_INLINE __attribute__((always_inline)) +#else +#define SNAPPY_ATTRIBUTE_ALWAYS_INLINE +#endif + // This is only used for recomputing the tag byte table used during // decompression; for simplicity we just remove it from the open-source // version (anyone who wants to regenerate it can just do the call @@ -115,201 +116,47 @@ namespace snappy { -static const uint32 kuint32max = static_cast(0xFFFFFFFF); -static const int64 kint64max = static_cast(0x7FFFFFFFFFFFFFFFLL); +static const uint32_t kuint32max = std::numeric_limits::max(); +static const int64_t kint64max = std::numeric_limits::max(); // Potentially unaligned loads and stores. -// x86, PowerPC, and ARM64 can simply do these loads and stores native. - -#if defined(__i386__) || defined(__x86_64__) || defined(__powerpc__) || \ - defined(__aarch64__) - -#define UNALIGNED_LOAD16(_p) (*reinterpret_cast(_p)) -#define UNALIGNED_LOAD32(_p) (*reinterpret_cast(_p)) -#define UNALIGNED_LOAD64(_p) (*reinterpret_cast(_p)) - -#define UNALIGNED_STORE16(_p, _val) (*reinterpret_cast(_p) = (_val)) -#define UNALIGNED_STORE32(_p, _val) (*reinterpret_cast(_p) = (_val)) -#define UNALIGNED_STORE64(_p, _val) (*reinterpret_cast(_p) = (_val)) - -// ARMv7 and newer support native unaligned accesses, but only of 16-bit -// and 32-bit values (not 64-bit); older versions either raise a fatal signal, -// do an unaligned read and rotate the words around a bit, or do the reads very -// slowly (trip through kernel mode). There's no simple #define that says just -// "ARMv7 or higher", so we have to filter away all ARMv5 and ARMv6 -// sub-architectures. -// -// This is a mess, but there's not much we can do about it. -// -// To further complicate matters, only LDR instructions (single reads) are -// allowed to be unaligned, not LDRD (two reads) or LDM (many reads). Unless we -// explicitly tell the compiler that these accesses can be unaligned, it can and -// will combine accesses. On armcc, the way to signal this is done by accessing -// through the type (uint32 __packed *), but GCC has no such attribute -// (it ignores __attribute__((packed)) on individual variables). However, -// we can tell it that a _struct_ is unaligned, which has the same effect, -// so we do that. - -#elif defined(__arm__) && \ - !defined(__ARM_ARCH_4__) && \ - !defined(__ARM_ARCH_4T__) && \ - !defined(__ARM_ARCH_5__) && \ - !defined(__ARM_ARCH_5T__) && \ - !defined(__ARM_ARCH_5TE__) && \ - !defined(__ARM_ARCH_5TEJ__) && \ - !defined(__ARM_ARCH_6__) && \ - !defined(__ARM_ARCH_6J__) && \ - !defined(__ARM_ARCH_6K__) && \ - !defined(__ARM_ARCH_6Z__) && \ - !defined(__ARM_ARCH_6ZK__) && \ - !defined(__ARM_ARCH_6T2__) - -#if __GNUC__ -#define ATTRIBUTE_PACKED __attribute__((__packed__)) -#else -#define ATTRIBUTE_PACKED -#endif - -namespace base { -namespace internal { - -struct Unaligned16Struct { - uint16 value; - uint8 dummy; // To make the size non-power-of-two. -} ATTRIBUTE_PACKED; - -struct Unaligned32Struct { - uint32 value; - uint8 dummy; // To make the size non-power-of-two. -} ATTRIBUTE_PACKED; - -} // namespace internal -} // namespace base - -#define UNALIGNED_LOAD16(_p) \ - ((reinterpret_cast(_p))->value) -#define UNALIGNED_LOAD32(_p) \ - ((reinterpret_cast(_p))->value) - -#define UNALIGNED_STORE16(_p, _val) \ - ((reinterpret_cast< ::snappy::base::internal::Unaligned16Struct *>(_p))->value = \ - (_val)) -#define UNALIGNED_STORE32(_p, _val) \ - ((reinterpret_cast< ::snappy::base::internal::Unaligned32Struct *>(_p))->value = \ - (_val)) - -// TODO: NEON supports unaligned 64-bit loads and stores. -// See if that would be more efficient on platforms supporting it, -// at least for copies. - -inline uint64 UNALIGNED_LOAD64(const void *p) { - uint64 t; - memcpy(&t, p, sizeof t); - return t; +inline uint16_t UNALIGNED_LOAD16(const void *p) { + // Compiles to a single movzx/ldrh on clang/gcc/msvc. + uint16_t v; + std::memcpy(&v, p, sizeof(v)); + return v; } -inline void UNALIGNED_STORE64(void *p, uint64 v) { - memcpy(p, &v, sizeof v); +inline uint32_t UNALIGNED_LOAD32(const void *p) { + // Compiles to a single mov/ldr on clang/gcc/msvc. + uint32_t v; + std::memcpy(&v, p, sizeof(v)); + return v; } -#else - -// These functions are provided for architectures that don't support -// unaligned loads and stores. - -inline uint16 UNALIGNED_LOAD16(const void *p) { - uint16 t; - memcpy(&t, p, sizeof t); - return t; +inline uint64_t UNALIGNED_LOAD64(const void *p) { + // Compiles to a single mov/ldr on clang/gcc/msvc. + uint64_t v; + std::memcpy(&v, p, sizeof(v)); + return v; } -inline uint32 UNALIGNED_LOAD32(const void *p) { - uint32 t; - memcpy(&t, p, sizeof t); - return t; +inline void UNALIGNED_STORE16(void *p, uint16_t v) { + // Compiles to a single mov/strh on clang/gcc/msvc. + std::memcpy(p, &v, sizeof(v)); } -inline uint64 UNALIGNED_LOAD64(const void *p) { - uint64 t; - memcpy(&t, p, sizeof t); - return t; +inline void UNALIGNED_STORE32(void *p, uint32_t v) { + // Compiles to a single mov/str on clang/gcc/msvc. + std::memcpy(p, &v, sizeof(v)); } -inline void UNALIGNED_STORE16(void *p, uint16 v) { - memcpy(p, &v, sizeof v); +inline void UNALIGNED_STORE64(void *p, uint64_t v) { + // Compiles to a single mov/str on clang/gcc/msvc. + std::memcpy(p, &v, sizeof(v)); } -inline void UNALIGNED_STORE32(void *p, uint32 v) { - memcpy(p, &v, sizeof v); -} - -inline void UNALIGNED_STORE64(void *p, uint64 v) { - memcpy(p, &v, sizeof v); -} - -#endif - -// The following guarantees declaration of the byte swap functions. -#if defined(SNAPPY_IS_BIG_ENDIAN) - -#ifdef HAVE_SYS_BYTEORDER_H -#include -#endif - -#ifdef HAVE_SYS_ENDIAN_H -#include -#endif - -#ifdef _MSC_VER -#include -#define bswap_16(x) _byteswap_ushort(x) -#define bswap_32(x) _byteswap_ulong(x) -#define bswap_64(x) _byteswap_uint64(x) - -#elif defined(__APPLE__) -// Mac OS X / Darwin features -#include -#define bswap_16(x) OSSwapInt16(x) -#define bswap_32(x) OSSwapInt32(x) -#define bswap_64(x) OSSwapInt64(x) - -#elif defined(HAVE_BYTESWAP_H) -#include - -#elif defined(bswap32) -// FreeBSD defines bswap{16,32,64} in (already #included). -#define bswap_16(x) bswap16(x) -#define bswap_32(x) bswap32(x) -#define bswap_64(x) bswap64(x) - -#elif defined(BSWAP_64) -// Solaris 10 defines BSWAP_{16,32,64} in (already #included). -#define bswap_16(x) BSWAP_16(x) -#define bswap_32(x) BSWAP_32(x) -#define bswap_64(x) BSWAP_64(x) - -#else - -inline uint16 bswap_16(uint16 x) { - return (x << 8) | (x >> 8); -} - -inline uint32 bswap_32(uint32 x) { - x = ((x & 0xff00ff00UL) >> 8) | ((x & 0x00ff00ffUL) << 8); - return (x >> 16) | (x << 16); -} - -inline uint64 bswap_64(uint64 x) { - x = ((x & 0xff00ff00ff00ff00ULL) >> 8) | ((x & 0x00ff00ff00ff00ffULL) << 8); - x = ((x & 0xffff0000ffff0000ULL) >> 16) | ((x & 0x0000ffff0000ffffULL) << 16); - return (x >> 32) | (x << 32); -} - -#endif - -#endif // defined(SNAPPY_IS_BIG_ENDIAN) - // Convert to little-endian storage, opposite of network format. // Convert x from host to little endian: x = LittleEndian.FromHost(x); // convert x from little endian to host: x = LittleEndian.ToHost(x); @@ -321,44 +168,77 @@ inline uint64 bswap_64(uint64 x) { // x = LittleEndian.Load16(p); class LittleEndian { public: - // Conversion functions. -#if defined(SNAPPY_IS_BIG_ENDIAN) - - static uint16 FromHost16(uint16 x) { return bswap_16(x); } - static uint16 ToHost16(uint16 x) { return bswap_16(x); } - - static uint32 FromHost32(uint32 x) { return bswap_32(x); } - static uint32 ToHost32(uint32 x) { return bswap_32(x); } - - static bool IsLittleEndian() { return false; } - -#else // !defined(SNAPPY_IS_BIG_ENDIAN) - - static uint16 FromHost16(uint16 x) { return x; } - static uint16 ToHost16(uint16 x) { return x; } - - static uint32 FromHost32(uint32 x) { return x; } - static uint32 ToHost32(uint32 x) { return x; } - - static bool IsLittleEndian() { return true; } - -#endif // !defined(SNAPPY_IS_BIG_ENDIAN) - // Functions to do unaligned loads and stores in little-endian order. - static uint16 Load16(const void *p) { - return ToHost16(UNALIGNED_LOAD16(p)); + static inline uint16_t Load16(const void *ptr) { + const uint8_t* const buffer = reinterpret_cast(ptr); + + // Compiles to a single mov/str on recent clang and gcc. + return (static_cast(buffer[0])) | + (static_cast(buffer[1]) << 8); } - static void Store16(void *p, uint16 v) { - UNALIGNED_STORE16(p, FromHost16(v)); + static inline uint32_t Load32(const void *ptr) { + const uint8_t* const buffer = reinterpret_cast(ptr); + + // Compiles to a single mov/str on recent clang and gcc. + return (static_cast(buffer[0])) | + (static_cast(buffer[1]) << 8) | + (static_cast(buffer[2]) << 16) | + (static_cast(buffer[3]) << 24); } - static uint32 Load32(const void *p) { - return ToHost32(UNALIGNED_LOAD32(p)); + static inline uint64_t Load64(const void *ptr) { + const uint8_t* const buffer = reinterpret_cast(ptr); + + // Compiles to a single mov/str on recent clang and gcc. + return (static_cast(buffer[0])) | + (static_cast(buffer[1]) << 8) | + (static_cast(buffer[2]) << 16) | + (static_cast(buffer[3]) << 24) | + (static_cast(buffer[4]) << 32) | + (static_cast(buffer[5]) << 40) | + (static_cast(buffer[6]) << 48) | + (static_cast(buffer[7]) << 56); } - static void Store32(void *p, uint32 v) { - UNALIGNED_STORE32(p, FromHost32(v)); + static inline void Store16(void *dst, uint16_t value) { + uint8_t* const buffer = reinterpret_cast(dst); + + // Compiles to a single mov/str on recent clang and gcc. + buffer[0] = static_cast(value); + buffer[1] = static_cast(value >> 8); + } + + static void Store32(void *dst, uint32_t value) { + uint8_t* const buffer = reinterpret_cast(dst); + + // Compiles to a single mov/str on recent clang and gcc. + buffer[0] = static_cast(value); + buffer[1] = static_cast(value >> 8); + buffer[2] = static_cast(value >> 16); + buffer[3] = static_cast(value >> 24); + } + + static void Store64(void* dst, uint64_t value) { + uint8_t* const buffer = reinterpret_cast(dst); + + // Compiles to a single mov/str on recent clang and gcc. + buffer[0] = static_cast(value); + buffer[1] = static_cast(value >> 8); + buffer[2] = static_cast(value >> 16); + buffer[3] = static_cast(value >> 24); + buffer[4] = static_cast(value >> 32); + buffer[5] = static_cast(value >> 40); + buffer[6] = static_cast(value >> 48); + buffer[7] = static_cast(value >> 56); + } + + static inline constexpr bool IsLittleEndian() { +#if defined(SNAPPY_IS_BIG_ENDIAN) + return false; +#else + return true; +#endif // defined(SNAPPY_IS_BIG_ENDIAN) } }; @@ -366,19 +246,17 @@ class LittleEndian { class Bits { public: // Return floor(log2(n)) for positive integer n. - static int Log2FloorNonZero(uint32 n); + static int Log2FloorNonZero(uint32_t n); // Return floor(log2(n)) for positive integer n. Returns -1 iff n == 0. - static int Log2Floor(uint32 n); + static int Log2Floor(uint32_t n); // Return the first set least / most significant bit, 0-indexed. Returns an // undefined value if n == 0. FindLSBSetNonZero() is similar to ffs() except // that it's 0-indexed. - static int FindLSBSetNonZero(uint32 n); + static int FindLSBSetNonZero(uint32_t n); -#if defined(ARCH_K8) || defined(ARCH_PPC) || defined(ARCH_ARM) - static int FindLSBSetNonZero64(uint64 n); -#endif // defined(ARCH_K8) || defined(ARCH_PPC) || defined(ARCH_ARM) + static int FindLSBSetNonZero64(uint64_t n); private: // No copying @@ -386,9 +264,9 @@ class Bits { void operator=(const Bits&); }; -#ifdef HAVE_BUILTIN_CTZ +#if defined(HAVE_BUILTIN_CTZ) -inline int Bits::Log2FloorNonZero(uint32 n) { +inline int Bits::Log2FloorNonZero(uint32_t n) { assert(n != 0); // (31 ^ x) is equivalent to (31 - x) for x in [0, 31]. An easy proof // represents subtraction in base 2 and observes that there's no carry. @@ -399,66 +277,52 @@ inline int Bits::Log2FloorNonZero(uint32 n) { return 31 ^ __builtin_clz(n); } -inline int Bits::Log2Floor(uint32 n) { +inline int Bits::Log2Floor(uint32_t n) { return (n == 0) ? -1 : Bits::Log2FloorNonZero(n); } -inline int Bits::FindLSBSetNonZero(uint32 n) { +inline int Bits::FindLSBSetNonZero(uint32_t n) { assert(n != 0); return __builtin_ctz(n); } -#if defined(ARCH_K8) || defined(ARCH_PPC) || defined(ARCH_ARM) -inline int Bits::FindLSBSetNonZero64(uint64 n) { - assert(n != 0); - return __builtin_ctzll(n); -} -#endif // defined(ARCH_K8) || defined(ARCH_PPC) || defined(ARCH_ARM) - #elif defined(_MSC_VER) -inline int Bits::Log2FloorNonZero(uint32 n) { +inline int Bits::Log2FloorNonZero(uint32_t n) { assert(n != 0); + // NOLINTNEXTLINE(runtime/int): The MSVC intrinsic demands unsigned long. unsigned long where; _BitScanReverse(&where, n); return static_cast(where); } -inline int Bits::Log2Floor(uint32 n) { +inline int Bits::Log2Floor(uint32_t n) { + // NOLINTNEXTLINE(runtime/int): The MSVC intrinsic demands unsigned long. unsigned long where; if (_BitScanReverse(&where, n)) return static_cast(where); return -1; } -inline int Bits::FindLSBSetNonZero(uint32 n) { +inline int Bits::FindLSBSetNonZero(uint32_t n) { assert(n != 0); + // NOLINTNEXTLINE(runtime/int): The MSVC intrinsic demands unsigned long. unsigned long where; if (_BitScanForward(&where, n)) return static_cast(where); return 32; } -#if defined(ARCH_K8) || defined(ARCH_PPC) || defined(ARCH_ARM) -inline int Bits::FindLSBSetNonZero64(uint64 n) { - assert(n != 0); - unsigned long where; - if (_BitScanForward64(&where, n)) - return static_cast(where); - return 64; -} -#endif // defined(ARCH_K8) || defined(ARCH_PPC) || defined(ARCH_ARM) - #else // Portable versions. -inline int Bits::Log2FloorNonZero(uint32 n) { +inline int Bits::Log2FloorNonZero(uint32_t n) { assert(n != 0); int log = 0; - uint32 value = n; + uint32_t value = n; for (int i = 4; i >= 0; --i) { int shift = (1 << i); - uint32 x = value >> shift; + uint32_t x = value >> shift; if (x != 0) { value = x; log += shift; @@ -468,16 +332,16 @@ inline int Bits::Log2FloorNonZero(uint32 n) { return log; } -inline int Bits::Log2Floor(uint32 n) { +inline int Bits::Log2Floor(uint32_t n) { return (n == 0) ? -1 : Bits::Log2FloorNonZero(n); } -inline int Bits::FindLSBSetNonZero(uint32 n) { +inline int Bits::FindLSBSetNonZero(uint32_t n) { assert(n != 0); int rc = 31; for (int i = 4, shift = 1 << 4; i >= 0; --i) { - const uint32 x = n << shift; + const uint32_t x = n << shift; if (x != 0) { n = x; rc -= shift; @@ -487,27 +351,48 @@ inline int Bits::FindLSBSetNonZero(uint32 n) { return rc; } -#if defined(ARCH_K8) || defined(ARCH_PPC) || defined(ARCH_ARM) +#endif // End portable versions. + +#if defined(HAVE_BUILTIN_CTZ) + +inline int Bits::FindLSBSetNonZero64(uint64_t n) { + assert(n != 0); + return __builtin_ctzll(n); +} + +#elif defined(_MSC_VER) && (defined(_M_X64) || defined(_M_ARM64)) +// _BitScanForward64() is only available on x64 and ARM64. + +inline int Bits::FindLSBSetNonZero64(uint64_t n) { + assert(n != 0); + // NOLINTNEXTLINE(runtime/int): The MSVC intrinsic demands unsigned long. + unsigned long where; + if (_BitScanForward64(&where, n)) + return static_cast(where); + return 64; +} + +#else // Portable version. + // FindLSBSetNonZero64() is defined in terms of FindLSBSetNonZero(). -inline int Bits::FindLSBSetNonZero64(uint64 n) { +inline int Bits::FindLSBSetNonZero64(uint64_t n) { assert(n != 0); - const uint32 bottombits = static_cast(n); + const uint32_t bottombits = static_cast(n); if (bottombits == 0) { - // Bottom bits are zero, so scan in top bits - return 32 + FindLSBSetNonZero(static_cast(n >> 32)); + // Bottom bits are zero, so scan the top bits. + return 32 + FindLSBSetNonZero(static_cast(n >> 32)); } else { return FindLSBSetNonZero(bottombits); } } -#endif // defined(ARCH_K8) || defined(ARCH_PPC) || defined(ARCH_ARM) -#endif // End portable versions. +#endif // End portable version. // Variable-length integer encoding. class Varint { public: - // Maximum lengths of varint encoding of uint32. + // Maximum lengths of varint encoding of uint32_t. static const int kMax32 = 5; // Attempts to parse a varint32 from a prefix of the bytes in [ptr,limit-1]. @@ -516,23 +401,23 @@ class Varint { // past the last byte of the varint32. Else returns NULL. On success, // "result <= limit". static const char* Parse32WithLimit(const char* ptr, const char* limit, - uint32* OUTPUT); + uint32_t* OUTPUT); // REQUIRES "ptr" points to a buffer of length sufficient to hold "v". // EFFECTS Encodes "v" into "ptr" and returns a pointer to the // byte just past the last encoded byte. - static char* Encode32(char* ptr, uint32 v); + static char* Encode32(char* ptr, uint32_t v); // EFFECTS Appends the varint representation of "value" to "*s". - static void Append32(std::string* s, uint32 value); + static void Append32(std::string* s, uint32_t value); }; inline const char* Varint::Parse32WithLimit(const char* p, const char* l, - uint32* OUTPUT) { + uint32_t* OUTPUT) { const unsigned char* ptr = reinterpret_cast(p); const unsigned char* limit = reinterpret_cast(l); - uint32 b, result; + uint32_t b, result; if (ptr >= limit) return NULL; b = *(ptr++); result = b & 127; if (b < 128) goto done; if (ptr >= limit) return NULL; @@ -549,30 +434,30 @@ inline const char* Varint::Parse32WithLimit(const char* p, return reinterpret_cast(ptr); } -inline char* Varint::Encode32(char* sptr, uint32 v) { +inline char* Varint::Encode32(char* sptr, uint32_t v) { // Operate on characters as unsigneds - unsigned char* ptr = reinterpret_cast(sptr); - static const int B = 128; - if (v < (1<<7)) { - *(ptr++) = v; - } else if (v < (1<<14)) { - *(ptr++) = v | B; - *(ptr++) = v>>7; - } else if (v < (1<<21)) { - *(ptr++) = v | B; - *(ptr++) = (v>>7) | B; - *(ptr++) = v>>14; - } else if (v < (1<<28)) { - *(ptr++) = v | B; - *(ptr++) = (v>>7) | B; - *(ptr++) = (v>>14) | B; - *(ptr++) = v>>21; + uint8_t* ptr = reinterpret_cast(sptr); + static const uint8_t B = 128; + if (v < (1 << 7)) { + *(ptr++) = static_cast(v); + } else if (v < (1 << 14)) { + *(ptr++) = static_cast(v | B); + *(ptr++) = static_cast(v >> 7); + } else if (v < (1 << 21)) { + *(ptr++) = static_cast(v | B); + *(ptr++) = static_cast((v >> 7) | B); + *(ptr++) = static_cast(v >> 14); + } else if (v < (1 << 28)) { + *(ptr++) = static_cast(v | B); + *(ptr++) = static_cast((v >> 7) | B); + *(ptr++) = static_cast((v >> 14) | B); + *(ptr++) = static_cast(v >> 21); } else { - *(ptr++) = v | B; - *(ptr++) = (v>>7) | B; - *(ptr++) = (v>>14) | B; - *(ptr++) = (v>>21) | B; - *(ptr++) = v>>28; + *(ptr++) = static_cast(v | B); + *(ptr++) = static_cast((v>>7) | B); + *(ptr++) = static_cast((v>>14) | B); + *(ptr++) = static_cast((v>>21) | B); + *(ptr++) = static_cast(v >> 28); } return reinterpret_cast(ptr); } diff --git a/ext/snappy/snappy-stubs-public.h b/ext/snappy/snappy-stubs-public.h index fddafbc8c7..645d288960 100644 --- a/ext/snappy/snappy-stubs-public.h +++ b/ext/snappy/snappy-stubs-public.h @@ -35,31 +35,28 @@ #ifndef THIRD_PARTY_SNAPPY_OPENSOURCE_SNAPPY_STUBS_PUBLIC_H_ #define THIRD_PARTY_SNAPPY_OPENSOURCE_SNAPPY_STUBS_PUBLIC_H_ -#include +#include + +#if 0 // HAVE_SYS_UIO_H +#include +#endif // HAVE_SYS_UIO_H + +#define SNAPPY_MAJOR 1 +#define SNAPPY_MINOR 1 +#define SNAPPY_PATCHLEVEL 8 +#define SNAPPY_VERSION \ + ((SNAPPY_MAJOR << 16) | (SNAPPY_MINOR << 8) | SNAPPY_PATCHLEVEL) namespace snappy { -typedef int8_t int8; -typedef uint8_t uint8; -typedef int16_t int16; -typedef uint16_t uint16; -typedef int32_t int32; -typedef uint32_t uint32; -typedef int64_t int64; -typedef uint64_t uint64; - -typedef std::string string; - -#ifndef DISALLOW_COPY_AND_ASSIGN -#define DISALLOW_COPY_AND_ASSIGN(TypeName) \ - TypeName(const TypeName&); \ - void operator=(const TypeName&) -#endif - +#if !0 // !HAVE_SYS_UIO_H +// Windows does not have an iovec type, yet the concept is universally useful. +// It is simple to define it ourselves, so we put it inside our own namespace. struct iovec { void* iov_base; size_t iov_len; }; +#endif // !HAVE_SYS_UIO_H } // namespace snappy diff --git a/ext/snappy/snappy.cpp b/ext/snappy/snappy.cpp index 74891a6a3f..2a89d49d2f 100644 --- a/ext/snappy/snappy.cpp +++ b/ext/snappy/snappy.cpp @@ -26,9 +26,9 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -#include "snappy.h" #include "snappy-internal.h" #include "snappy-sinksource.h" +#include "snappy.h" #if !defined(SNAPPY_HAVE_SSSE3) // __SSSE3__ is defined by GCC and Clang. Visual Studio doesn't target SIMD @@ -68,34 +68,91 @@ #include #endif -#include - #include +#include +#include +#include +#include +#include #include +#include #include namespace snappy { +namespace { + +// The amount of slop bytes writers are using for unconditional copies. +constexpr int kSlopBytes = 64; + +using internal::char_table; using internal::COPY_1_BYTE_OFFSET; using internal::COPY_2_BYTE_OFFSET; -using internal::LITERAL; -using internal::char_table; +using internal::COPY_4_BYTE_OFFSET; using internal::kMaximumTagLength; +using internal::LITERAL; + +// We translate the information encoded in a tag through a lookup table to a +// format that requires fewer instructions to decode. Effectively we store +// the length minus the tag part of the offset. The lowest significant byte +// thus stores the length. While total length - offset is given by +// entry - ExtractOffset(type). The nice thing is that the subtraction +// immediately sets the flags for the necessary check that offset >= length. +// This folds the cmp with sub. We engineer the long literals and copy-4 to +// always fail this check, so their presence doesn't affect the fast path. +// To prevent literals from triggering the guard against offset < length (offset +// does not apply to literals) the table is giving them a spurious offset of +// 256. +inline constexpr int16_t MakeEntry(int16_t len, int16_t offset) { + return len - (offset << 8); +} + +inline constexpr int16_t LengthMinusOffset(int data, int type) { + return type == 3 ? 0xFF // copy-4 (or type == 3) + : type == 2 ? MakeEntry(data + 1, 0) // copy-2 + : type == 1 ? MakeEntry((data & 7) + 4, data >> 3) // copy-1 + : data < 60 ? MakeEntry(data + 1, 1) // note spurious offset. + : 0xFF; // long literal +} + +inline constexpr int16_t LengthMinusOffset(uint8_t tag) { + return LengthMinusOffset(tag >> 2, tag & 3); +} + +template +struct index_sequence {}; + +template +struct make_index_sequence : make_index_sequence {}; + +template +struct make_index_sequence<0, Is...> : index_sequence {}; + +template +constexpr std::array MakeTable(index_sequence) { + return std::array{LengthMinusOffset(seq)...}; +} + +// We maximally co-locate the two tables so that only one register needs to be +// reserved for the table address. +struct { + alignas(64) const std::array length_minus_offset; + uint32_t extract_masks[4]; // Used for extracting offset based on tag type. +} table = {MakeTable(make_index_sequence<256>{}), {0, 0xFF, 0xFFFF, 0}}; // Any hash function will produce a valid compressed bitstream, but a good // hash function reduces the number of collisions and thus yields better // compression for compressible input, and more speed for incompressible // input. Of course, it doesn't hurt if the hash function is reasonably fast // either, as it gets called a lot. -static inline uint32 HashBytes(uint32 bytes, int shift) { - uint32 kMul = 0x1e35a7bd; - return (bytes * kMul) >> shift; -} -static inline uint32 Hash(const char* p, int shift) { - return HashBytes(UNALIGNED_LOAD32(p), shift); +inline uint32_t HashBytes(uint32_t bytes, uint32_t mask) { + constexpr uint32_t kMagic = 0x1e35a7bd; + return ((kMagic * bytes) >> (32 - kMaxHashTableBits)) & mask; } -size_t MaxCompressedLength(size_t source_len) { +} // namespace + +size_t MaxCompressedLength(size_t source_bytes) { // Compressed data can be defined as: // compressed := item* literal* // item := literal* copy @@ -116,24 +173,34 @@ size_t MaxCompressedLength(size_t source_len) { // I.e., 6 bytes of input turn into 7 bytes of "compressed" data. // // This last factor dominates the blowup, so the final estimate is: - return 32 + source_len + source_len/6; + return 32 + source_bytes + source_bytes / 6; } namespace { void UnalignedCopy64(const void* src, void* dst) { char tmp[8]; - memcpy(tmp, src, 8); - memcpy(dst, tmp, 8); + std::memcpy(tmp, src, 8); + std::memcpy(dst, tmp, 8); } void UnalignedCopy128(const void* src, void* dst) { - // memcpy gets vectorized when the appropriate compiler options are used. - // For example, x86 compilers targeting SSE2+ will optimize to an SSE2 load - // and store. + // std::memcpy() gets vectorized when the appropriate compiler options are + // used. For example, x86 compilers targeting SSE2+ will optimize to an SSE2 + // load and store. char tmp[16]; - memcpy(tmp, src, 16); - memcpy(dst, tmp, 16); + std::memcpy(tmp, src, 16); + std::memcpy(dst, tmp, 16); +} + +template +inline void ConditionalUnalignedCopy128(const char* src, char* dst) { + if (use_16bytes_chunk) { + UnalignedCopy128(src, dst); + } else { + UnalignedCopy64(src, dst); + UnalignedCopy64(src + 8, dst + 8); + } } // Copy [src, src+(op_limit-op)) to [op, (op_limit-op)) a byte at a time. Used @@ -145,7 +212,8 @@ void UnalignedCopy128(const void* src, void* dst) { // After IncrementalCopySlow(src, op, op_limit), the result will have eleven // copies of "ab" // ababababababababababab -// Note that this does not match the semantics of either memcpy() or memmove(). +// Note that this does not match the semantics of either std::memcpy() or +// std::memmove(). inline char* IncrementalCopySlow(const char* src, char* op, char* const op_limit) { // TODO: Remove pragma when LLVM is aware this @@ -162,37 +230,171 @@ inline char* IncrementalCopySlow(const char* src, char* op, #if SNAPPY_HAVE_SSSE3 -// This is a table of shuffle control masks that can be used as the source +// Computes the bytes for shuffle control mask (please read comments on +// 'pattern_generation_masks' as well) for the given index_offset and +// pattern_size. For example, when the 'offset' is 6, it will generate a +// repeating pattern of size 6. So, the first 16 byte indexes will correspond to +// the pattern-bytes {0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5, 0, 1, 2, 3} and the +// next 16 byte indexes will correspond to the pattern-bytes {4, 5, 0, 1, 2, 3, +// 4, 5, 0, 1, 2, 3, 4, 5, 0, 1}. These byte index sequences are generated by +// calling MakePatternMaskBytes(0, 6, index_sequence<16>()) and +// MakePatternMaskBytes(16, 6, index_sequence<16>()) respectively. +template +inline constexpr std::array MakePatternMaskBytes( + int index_offset, int pattern_size, index_sequence) { + return {static_cast((index_offset + indexes) % pattern_size)...}; +} + +// Computes the shuffle control mask bytes array for given pattern-sizes and +// returns an array. +template +inline constexpr std::array, + sizeof...(pattern_sizes_minus_one)> +MakePatternMaskBytesTable(int index_offset, + index_sequence) { + return {MakePatternMaskBytes( + index_offset, pattern_sizes_minus_one + 1, + make_index_sequence())...}; +} + +// This is an array of shuffle control masks that can be used as the source // operand for PSHUFB to permute the contents of the destination XMM register // into a repeating byte pattern. -alignas(16) const char pshufb_fill_patterns[7][16] = { - {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}, - {0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0}, - {0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3}, - {0, 1, 2, 3, 4, 0, 1, 2, 3, 4, 0, 1, 2, 3, 4, 0}, - {0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5, 0, 1, 2, 3}, - {0, 1, 2, 3, 4, 5, 6, 0, 1, 2, 3, 4, 5, 6, 0, 1}, -}; +alignas(16) constexpr std::array, + 16> pattern_generation_masks = + MakePatternMaskBytesTable( + /*index_offset=*/0, + /*pattern_sizes_minus_one=*/make_index_sequence<16>()); + +// Similar to 'pattern_generation_masks', this table is used to "rotate" the +// pattern so that we can copy the *next 16 bytes* consistent with the pattern. +// Basically, pattern_reshuffle_masks is a continuation of +// pattern_generation_masks. It follows that, pattern_reshuffle_masks is same as +// pattern_generation_masks for offsets 1, 2, 4, 8 and 16. +alignas(16) constexpr std::array, + 16> pattern_reshuffle_masks = + MakePatternMaskBytesTable( + /*index_offset=*/16, + /*pattern_sizes_minus_one=*/make_index_sequence<16>()); + +SNAPPY_ATTRIBUTE_ALWAYS_INLINE +static inline __m128i LoadPattern(const char* src, const size_t pattern_size) { + __m128i generation_mask = _mm_load_si128(reinterpret_cast( + pattern_generation_masks[pattern_size - 1].data())); + // Uninitialized bytes are masked out by the shuffle mask. + // TODO: remove annotation and macro defs once MSan is fixed. + SNAPPY_ANNOTATE_MEMORY_IS_INITIALIZED(src + pattern_size, 16 - pattern_size); + return _mm_shuffle_epi8( + _mm_loadu_si128(reinterpret_cast(src)), generation_mask); +} + +SNAPPY_ATTRIBUTE_ALWAYS_INLINE +static inline std::pair<__m128i /* pattern */, __m128i /* reshuffle_mask */> +LoadPatternAndReshuffleMask(const char* src, const size_t pattern_size) { + __m128i pattern = LoadPattern(src, pattern_size); + + // This mask will generate the next 16 bytes in-place. Doing so enables us to + // write data by at most 4 _mm_storeu_si128. + // + // For example, suppose pattern is: abcdefabcdefabcd + // Shuffling with this mask will generate: efabcdefabcdefab + // Shuffling again will generate: cdefabcdefabcdef + __m128i reshuffle_mask = _mm_load_si128(reinterpret_cast( + pattern_reshuffle_masks[pattern_size - 1].data())); + return {pattern, reshuffle_mask}; +} #endif // SNAPPY_HAVE_SSSE3 -// Copy [src, src+(op_limit-op)) to [op, (op_limit-op)) but faster than +// Fallback for when we need to copy while extending the pattern, for example +// copying 10 bytes from 3 positions back abc -> abcabcabcabca. +// +// REQUIRES: [dst - offset, dst + 64) is a valid address range. +SNAPPY_ATTRIBUTE_ALWAYS_INLINE +static inline bool Copy64BytesWithPatternExtension(char* dst, size_t offset) { +#if SNAPPY_HAVE_SSSE3 + if (SNAPPY_PREDICT_TRUE(offset <= 16)) { + switch (offset) { + case 0: + return false; + case 1: { + std::memset(dst, dst[-1], 64); + return true; + } + case 2: + case 4: + case 8: + case 16: { + __m128i pattern = LoadPattern(dst - offset, offset); + for (int i = 0; i < 4; i++) { + _mm_storeu_si128(reinterpret_cast<__m128i*>(dst + 16 * i), pattern); + } + return true; + } + default: { + auto pattern_and_reshuffle_mask = + LoadPatternAndReshuffleMask(dst - offset, offset); + __m128i pattern = pattern_and_reshuffle_mask.first; + __m128i reshuffle_mask = pattern_and_reshuffle_mask.second; + for (int i = 0; i < 4; i++) { + _mm_storeu_si128(reinterpret_cast<__m128i*>(dst + 16 * i), pattern); + pattern = _mm_shuffle_epi8(pattern, reshuffle_mask); + } + return true; + } + } + } +#else + if (SNAPPY_PREDICT_TRUE(offset < 16)) { + if (SNAPPY_PREDICT_FALSE(offset == 0)) return false; + // Extend the pattern to the first 16 bytes. + for (int i = 0; i < 16; i++) dst[i] = dst[i - offset]; + // Find a multiple of pattern >= 16. + static std::array pattern_sizes = []() { + std::array res; + for (int i = 1; i < 16; i++) res[i] = (16 / i + 1) * i; + return res; + }(); + offset = pattern_sizes[offset]; + for (int i = 1; i < 4; i++) { + std::memcpy(dst + i * 16, dst + i * 16 - offset, 16); + } + return true; + } +#endif // SNAPPY_HAVE_SSSE3 + + // Very rare. + for (int i = 0; i < 4; i++) { + std::memcpy(dst + i * 16, dst + i * 16 - offset, 16); + } + return true; +} + +// Copy [src, src+(op_limit-op)) to [op, op_limit) but faster than // IncrementalCopySlow. buf_limit is the address past the end of the writable // region of the buffer. inline char* IncrementalCopy(const char* src, char* op, char* const op_limit, char* const buf_limit) { +#if SNAPPY_HAVE_SSSE3 + constexpr int big_pattern_size_lower_bound = 16; +#else + constexpr int big_pattern_size_lower_bound = 8; +#endif + // Terminology: // // slop = buf_limit - op // pat = op - src - // len = limit - op + // len = op_limit - op assert(src < op); - assert(op <= op_limit); + assert(op < op_limit); assert(op_limit <= buf_limit); - // NOTE: The compressor always emits 4 <= len <= 64. It is ok to assume that - // to optimize this function but we have to also handle other cases in case - // the input does not satisfy these conditions. + // NOTE: The copy tags use 3 or 6 bits to store the copy length, so len <= 64. + assert(op_limit - op <= 64); + // NOTE: In practice the compressor always emits len >= 4, so it is ok to + // assume that to optimize this function, but this is not guaranteed by the + // compression format, so we have to also handle len < 4 in case the input + // does not satisfy these conditions. size_t pattern_size = op - src; // The cases are split into different branches to allow the branch predictor, @@ -216,11 +418,13 @@ inline char* IncrementalCopy(const char* src, char* op, char* const op_limit, // input. In general if we always predict len <= 16 it would be an ok // prediction. // - // In order to be fast we want a pattern >= 8 bytes and an unrolled loop - // copying 2x 8 bytes at a time. + // In order to be fast we want a pattern >= 16 bytes (or 8 bytes in non-SSE) + // and an unrolled loop copying 1x 16 bytes (or 2x 8 bytes in non-SSE) at a + // time. - // Handle the uncommon case where pattern is less than 8 bytes. - if (SNAPPY_PREDICT_FALSE(pattern_size < 8)) { + // Handle the uncommon case where pattern is less than 16 (or 8 in non-SSE) + // bytes. + if (pattern_size < big_pattern_size_lower_bound) { #if SNAPPY_HAVE_SSSE3 // Load the first eight bytes into an 128-bit XMM register, then use PSHUFB // to permute the register's contents in-place into a repeating sequence of @@ -234,25 +438,58 @@ inline char* IncrementalCopy(const char* src, char* op, char* const op_limit, // The non-SSE fallback implementation suffers from store-forwarding stalls // because its loads and stores partly overlap. By expanding the pattern // in-place, we avoid the penalty. - if (SNAPPY_PREDICT_TRUE(op <= buf_limit - 16)) { - const __m128i shuffle_mask = _mm_load_si128( - reinterpret_cast(pshufb_fill_patterns) - + pattern_size - 1); - const __m128i pattern = _mm_shuffle_epi8( - _mm_loadl_epi64(reinterpret_cast(src)), shuffle_mask); - // Uninitialized bytes are masked out by the shuffle mask. - // TODO: remove annotation and macro defs once MSan is fixed. - SNAPPY_ANNOTATE_MEMORY_IS_INITIALIZED(&pattern, sizeof(pattern)); - pattern_size *= 16 / pattern_size; - char* op_end = std::min(op_limit, buf_limit - 15); - while (op < op_end) { - _mm_storeu_si128(reinterpret_cast<__m128i*>(op), pattern); - op += pattern_size; + + // Typically, the op_limit is the gating factor so try to simplify the loop + // based on that. + if (SNAPPY_PREDICT_TRUE(op_limit <= buf_limit - 15)) { + auto pattern_and_reshuffle_mask = + LoadPatternAndReshuffleMask(src, pattern_size); + __m128i pattern = pattern_and_reshuffle_mask.first; + __m128i reshuffle_mask = pattern_and_reshuffle_mask.second; + + // There is at least one, and at most four 16-byte blocks. Writing four + // conditionals instead of a loop allows FDO to layout the code with + // respect to the actual probabilities of each length. + // TODO: Replace with loop with trip count hint. + _mm_storeu_si128(reinterpret_cast<__m128i*>(op), pattern); + + if (op + 16 < op_limit) { + pattern = _mm_shuffle_epi8(pattern, reshuffle_mask); + _mm_storeu_si128(reinterpret_cast<__m128i*>(op + 16), pattern); } - if (SNAPPY_PREDICT_TRUE(op >= op_limit)) return op_limit; + if (op + 32 < op_limit) { + pattern = _mm_shuffle_epi8(pattern, reshuffle_mask); + _mm_storeu_si128(reinterpret_cast<__m128i*>(op + 32), pattern); + } + if (op + 48 < op_limit) { + pattern = _mm_shuffle_epi8(pattern, reshuffle_mask); + _mm_storeu_si128(reinterpret_cast<__m128i*>(op + 48), pattern); + } + return op_limit; } - return IncrementalCopySlow(src, op, op_limit); -#else // !SNAPPY_HAVE_SSSE3 + char* const op_end = buf_limit - 15; + if (SNAPPY_PREDICT_TRUE(op < op_end)) { + auto pattern_and_reshuffle_mask = + LoadPatternAndReshuffleMask(src, pattern_size); + __m128i pattern = pattern_and_reshuffle_mask.first; + __m128i reshuffle_mask = pattern_and_reshuffle_mask.second; + + // This code path is relatively cold however so we save code size + // by avoiding unrolling and vectorizing. + // + // TODO: Remove pragma when when cold regions don't get + // vectorized or unrolled. +#ifdef __clang__ +#pragma clang loop unroll(disable) +#endif + do { + _mm_storeu_si128(reinterpret_cast<__m128i*>(op), pattern); + pattern = _mm_shuffle_epi8(pattern, reshuffle_mask); + op += 16; + } while (SNAPPY_PREDICT_TRUE(op < op_end)); + } + return IncrementalCopySlow(op - pattern_size, op, op_limit); +#else // !SNAPPY_HAVE_SSSE3 // If plenty of buffer space remains, expand the pattern to at least 8 // bytes. The way the following loop is written, we need 8 bytes of buffer // space if pattern_size >= 4, 11 bytes if pattern_size is 1 or 3, and 10 @@ -271,34 +508,30 @@ inline char* IncrementalCopy(const char* src, char* op, char* const op_limit, } #endif // SNAPPY_HAVE_SSSE3 } - assert(pattern_size >= 8); + assert(pattern_size >= big_pattern_size_lower_bound); + constexpr bool use_16bytes_chunk = big_pattern_size_lower_bound == 16; - // Copy 2x 8 bytes at a time. Because op - src can be < 16, a single - // UnalignedCopy128 might overwrite data in op. UnalignedCopy64 is safe - // because expanding the pattern to at least 8 bytes guarantees that - // op - src >= 8. + // Copy 1x 16 bytes (or 2x 8 bytes in non-SSE) at a time. Because op - src can + // be < 16 in non-SSE, a single UnalignedCopy128 might overwrite data in op. + // UnalignedCopy64 is safe because expanding the pattern to at least 8 bytes + // guarantees that op - src >= 8. // // Typically, the op_limit is the gating factor so try to simplify the loop // based on that. - if (SNAPPY_PREDICT_TRUE(op_limit <= buf_limit - 16)) { + if (SNAPPY_PREDICT_TRUE(op_limit <= buf_limit - 15)) { // There is at least one, and at most four 16-byte blocks. Writing four // conditionals instead of a loop allows FDO to layout the code with respect // to the actual probabilities of each length. // TODO: Replace with loop with trip count hint. - UnalignedCopy64(src, op); - UnalignedCopy64(src + 8, op + 8); - + ConditionalUnalignedCopy128(src, op); if (op + 16 < op_limit) { - UnalignedCopy64(src + 16, op + 16); - UnalignedCopy64(src + 24, op + 24); + ConditionalUnalignedCopy128(src + 16, op + 16); } if (op + 32 < op_limit) { - UnalignedCopy64(src + 32, op + 32); - UnalignedCopy64(src + 40, op + 40); + ConditionalUnalignedCopy128(src + 32, op + 32); } if (op + 48 < op_limit) { - UnalignedCopy64(src + 48, op + 48); - UnalignedCopy64(src + 56, op + 56); + ConditionalUnalignedCopy128(src + 48, op + 48); } return op_limit; } @@ -312,12 +545,10 @@ inline char* IncrementalCopy(const char* src, char* op, char* const op_limit, #ifdef __clang__ #pragma clang loop unroll(disable) #endif - for (char *op_end = buf_limit - 16; op < op_end; op += 16, src += 16) { - UnalignedCopy64(src, op); - UnalignedCopy64(src + 8, op + 8); + for (char* op_end = buf_limit - 16; op < op_end; op += 16, src += 16) { + ConditionalUnalignedCopy128(src, op); } - if (op >= op_limit) - return op_limit; + if (op >= op_limit) return op_limit; // We only take this branch if we didn't have enough slop and we can do a // single 8 byte copy. @@ -332,11 +563,9 @@ inline char* IncrementalCopy(const char* src, char* op, char* const op_limit, } // namespace template -static inline char* EmitLiteral(char* op, - const char* literal, - int len) { +static inline char* EmitLiteral(char* op, const char* literal, int len) { // The vast majority of copies are below 16 bytes, for which a - // call to memcpy is overkill. This fast path can sometimes + // call to std::memcpy() is overkill. This fast path can sometimes // copy up to 15 bytes too much, but that is okay in the // main loop, since we have a bit to go on for both sides: // @@ -345,7 +574,7 @@ static inline char* EmitLiteral(char* op, // if not, allow_fast_path = false. // - The output will always have 32 spare bytes (see // MaxCompressedLength). - assert(len > 0); // Zero-length literals are disallowed + assert(len > 0); // Zero-length literals are disallowed int n = len - 1; if (allow_fast_path && len <= 16) { // Fits in tag byte @@ -366,11 +595,11 @@ static inline char* EmitLiteral(char* op, // Encode in upcoming bytes. // Write 4 bytes, though we may care about only 1 of them. The output buffer // is guaranteed to have at least 3 more spaces left as 'len >= 61' holds - // here and there is a memcpy of size 'len' below. + // here and there is a std::memcpy() of size 'len' below. LittleEndian::Store32(op, n); op += count; } - memcpy(op, literal, len); + std::memcpy(op, literal, len); return op + len; } @@ -381,15 +610,22 @@ static inline char* EmitCopyAtMost64(char* op, size_t offset, size_t len) { assert(offset < 65536); assert(len_less_than_12 == (len < 12)); - if (len_less_than_12 && SNAPPY_PREDICT_TRUE(offset < 2048)) { - // offset fits in 11 bits. The 3 highest go in the top of the first byte, - // and the rest go in the second byte. - *op++ = (char)(COPY_1_BYTE_OFFSET + ((len - 4) << 2) + ((offset >> 3) & 0xe0)); - *op++ = offset & 0xff; + if (len_less_than_12) { + uint32_t u = (uint32_t)((len << 2) + (offset << 8)); + uint32_t copy1 = COPY_1_BYTE_OFFSET - (4 << 2) + ((offset >> 3) & 0xe0); + uint32_t copy2 = COPY_2_BYTE_OFFSET - (1 << 2); + // It turns out that offset < 2048 is a difficult to predict branch. + // `perf record` shows this is the highest percentage of branch misses in + // benchmarks. This code produces branch free code, the data dependency + // chain that bottlenecks the throughput is so long that a few extra + // instructions are completely free (IPC << 6 because of data deps). + u += offset < 2048 ? copy1 : copy2; + LittleEndian::Store32(op, u); + op += offset < 2048 ? 2 : 3; } else { // Write 4 bytes, though we only care about 3 of them. The output buffer // is required to have some slack, so the extra byte won't overrun it. - uint32 u = (uint32)(COPY_2_BYTE_OFFSET + ((len - 1) << 2) + (offset << 8)); + uint32_t u = COPY_2_BYTE_OFFSET + (uint32_t)(((len - 1) << 2) + (offset << 8)); LittleEndian::Store32(op, u); op += 3; } @@ -428,7 +664,7 @@ static inline char* EmitCopy(char* op, size_t offset, size_t len) { } bool GetUncompressedLength(const char* start, size_t n, size_t* result) { - uint32 v = 0; + uint32_t v = 0; const char* limit = start + n; if (Varint::Parse32WithLimit(start, limit, &v) != NULL) { *result = v; @@ -439,7 +675,7 @@ bool GetUncompressedLength(const char* start, size_t n, size_t* result) { } namespace { -uint32 CalculateTableSize(uint32 input_size) { +uint32_t CalculateTableSize(uint32_t input_size) { static_assert( kMaxHashTableSize >= kMinHashTableSize, "kMaxHashTableSize should be greater or equal to kMinHashTableSize."); @@ -458,11 +694,11 @@ uint32 CalculateTableSize(uint32 input_size) { namespace internal { WorkingMemory::WorkingMemory(size_t input_size) { const size_t max_fragment_size = std::min(input_size, kBlockSize); - const size_t table_size = CalculateTableSize((uint32)max_fragment_size); + const size_t table_size = CalculateTableSize((uint32_t)max_fragment_size); size_ = table_size * sizeof(*table_) + max_fragment_size + MaxCompressedLength(max_fragment_size); mem_ = std::allocator().allocate(size_); - table_ = reinterpret_cast(mem_); + table_ = reinterpret_cast(mem_); input_ = mem_ + table_size * sizeof(*table_); output_ = input_ + max_fragment_size; } @@ -471,58 +707,15 @@ WorkingMemory::~WorkingMemory() { std::allocator().deallocate(mem_, size_); } -uint16* WorkingMemory::GetHashTable(size_t fragment_size, - int* table_size) const { - const size_t htsize = CalculateTableSize((uint32)fragment_size); +uint16_t* WorkingMemory::GetHashTable(size_t fragment_size, + int* table_size) const { + const size_t htsize = CalculateTableSize((uint32_t)fragment_size); memset(table_, 0, htsize * sizeof(*table_)); *table_size = (int)htsize; return table_; } } // end namespace internal -// For 0 <= offset <= 4, GetUint32AtOffset(GetEightBytesAt(p), offset) will -// equal UNALIGNED_LOAD32(p + offset). Motivation: On x86-64 hardware we have -// empirically found that overlapping loads such as -// UNALIGNED_LOAD32(p) ... UNALIGNED_LOAD32(p+1) ... UNALIGNED_LOAD32(p+2) -// are slower than UNALIGNED_LOAD64(p) followed by shifts and casts to uint32. -// -// We have different versions for 64- and 32-bit; ideally we would avoid the -// two functions and just inline the UNALIGNED_LOAD64 call into -// GetUint32AtOffset, but GCC (at least not as of 4.6) is seemingly not clever -// enough to avoid loading the value multiple times then. For 64-bit, the load -// is done when GetEightBytesAt() is called, whereas for 32-bit, the load is -// done at GetUint32AtOffset() time. - -#ifdef ARCH_K8 - -typedef uint64 EightBytesReference; - -static inline EightBytesReference GetEightBytesAt(const char* ptr) { - return UNALIGNED_LOAD64(ptr); -} - -static inline uint32 GetUint32AtOffset(uint64 v, int offset) { - assert(offset >= 0); - assert(offset <= 4); - return v >> (LittleEndian::IsLittleEndian() ? 8 * offset : 32 - 8 * offset); -} - -#else - -typedef const char* EightBytesReference; - -static inline EightBytesReference GetEightBytesAt(const char* ptr) { - return ptr; -} - -static inline uint32 GetUint32AtOffset(const char* v, int offset) { - assert(offset >= 0); - assert(offset <= 4); - return UNALIGNED_LOAD32(v + offset); -} - -#endif - // Flat array compression that does not emit the "uncompressed length" // prefix. Compresses "input" string to the "*op" buffer. // @@ -535,29 +728,25 @@ static inline uint32 GetUint32AtOffset(const char* v, int offset) { // Returns an "end" pointer into "op" buffer. // "end - op" is the compressed size of "input". namespace internal { -char* CompressFragment(const char* input, - size_t input_size, - char* op, - uint16* table, - const int table_size) { +char* CompressFragment(const char* input, size_t input_size, char* op, + uint16_t* table, const int table_size) { // "ip" is the input pointer, and "op" is the output pointer. const char* ip = input; assert(input_size <= kBlockSize); assert((table_size & (table_size - 1)) == 0); // table must be power of two - const int shift = 32 - Bits::Log2Floor(table_size); - assert(static_cast(kuint32max >> shift) == table_size - 1); + const uint32_t mask = table_size - 1; const char* ip_end = input + input_size; const char* base_ip = ip; - // Bytes in [next_emit, ip) will be emitted as literal bytes. Or - // [next_emit, ip_end) after the main loop. - const char* next_emit = ip; const size_t kInputMarginBytes = 15; if (SNAPPY_PREDICT_TRUE(input_size >= kInputMarginBytes)) { const char* ip_limit = input + input_size - kInputMarginBytes; - for (uint32 next_hash = Hash(++ip, shift); ; ) { - assert(next_emit < ip); + for (uint32_t preload = LittleEndian::Load32(ip + 1);;) { + // Bytes in [next_emit, ip) will be emitted as literal bytes. Or + // [next_emit, ip_end) after the main loop. + const char* next_emit = ip++; + uint64_t data = LittleEndian::Load64(ip); // The body of this loop calls EmitLiteral once and then EmitCopy one or // more times. (The exception is that when we're close to exhausting // the input we goto emit_remainder.) @@ -583,28 +772,60 @@ char* CompressFragment(const char* input, // The "skip" variable keeps track of how many bytes there are since the // last match; dividing it by 32 (ie. right-shifting by five) gives the // number of bytes to move ahead for each iteration. - uint32 skip = 32; + uint32_t skip = 32; - const char* next_ip = ip; const char* candidate; - do { - ip = next_ip; - uint32 hash = next_hash; - assert(hash == Hash(ip, shift)); - uint32 bytes_between_hash_lookups = skip >> 5; + if (ip_limit - ip >= 16) { + auto delta = ip - base_ip; + for (int j = 0; j < 4; ++j) { + for (int k = 0; k < 4; ++k) { + int i = 4 * j + k; + // These for-loops are meant to be unrolled. So we can freely + // special case the first iteration to use the value already + // loaded in preload. + uint32_t dword = i == 0 ? preload : static_cast(data); + assert(dword == LittleEndian::Load32(ip + i)); + uint32_t hash = HashBytes(dword, mask); + candidate = base_ip + table[hash]; + assert(candidate >= base_ip); + assert(candidate < ip + i); + table[hash] = (uint16_t)(delta + i); + if (SNAPPY_PREDICT_FALSE(LittleEndian::Load32(candidate) == dword)) { + *op = LITERAL | (i << 2); + UnalignedCopy128(next_emit, op + 1); + ip += i; + op = op + i + 2; + goto emit_match; + } + data >>= 8; + } + data = LittleEndian::Load64(ip + 4 * j + 4); + } + ip += 16; + skip += 16; + } + while (true) { + assert(static_cast(data) == LittleEndian::Load32(ip)); + uint32_t hash = HashBytes((uint32_t)data, mask); + uint32_t bytes_between_hash_lookups = skip >> 5; skip += bytes_between_hash_lookups; - next_ip = ip + bytes_between_hash_lookups; + const char* next_ip = ip + bytes_between_hash_lookups; if (SNAPPY_PREDICT_FALSE(next_ip > ip_limit)) { + ip = next_emit; goto emit_remainder; } - next_hash = Hash(next_ip, shift); candidate = base_ip + table[hash]; assert(candidate >= base_ip); assert(candidate < ip); - table[hash] = (uint16)(ip - base_ip); - } while (SNAPPY_PREDICT_TRUE(UNALIGNED_LOAD32(ip) != - UNALIGNED_LOAD32(candidate))); + table[hash] = (uint16_t)(ip - base_ip); + if (SNAPPY_PREDICT_FALSE(static_cast(data) == + LittleEndian::Load32(candidate))) { + break; + } + data = LittleEndian::Load32(next_ip); + ip = next_ip; + } // Step 2: A 4-byte match has been found. We'll later see if more // than 4 bytes match. But, prior to the match, input @@ -620,15 +841,13 @@ char* CompressFragment(const char* input, // though we don't yet know how big the literal will be. We handle that // by proceeding to the next iteration of the main loop. We also can exit // this loop via goto if we get close to exhausting the input. - EightBytesReference input_bytes; - uint32 candidate_bytes = 0; - + emit_match: do { // We have a 4-byte match at ip, and no need to emit any // "literal bytes" prior to ip. const char* base = ip; std::pair p = - FindMatchLength(candidate + 4, ip + 4, ip_end); + FindMatchLength(candidate + 4, ip + 4, ip_end, &data); size_t matched = 4 + p.first; ip += matched; size_t offset = base - candidate; @@ -638,32 +857,40 @@ char* CompressFragment(const char* input, } else { op = EmitCopy(op, offset, matched); } - next_emit = ip; if (SNAPPY_PREDICT_FALSE(ip >= ip_limit)) { goto emit_remainder; } + // Expect 5 bytes to match + assert((data & 0xFFFFFFFFFF) == + (LittleEndian::Load64(ip) & 0xFFFFFFFFFF)); // We are now looking for a 4-byte match again. We read // table[Hash(ip, shift)] for that. To improve compression, - // we also update table[Hash(ip - 1, shift)] and table[Hash(ip, shift)]. - input_bytes = GetEightBytesAt(ip - 1); - uint32 prev_hash = HashBytes(GetUint32AtOffset(input_bytes, 0), shift); - table[prev_hash] = (uint16)(ip - base_ip - 1); - uint32 cur_hash = HashBytes(GetUint32AtOffset(input_bytes, 1), shift); - candidate = base_ip + table[cur_hash]; - candidate_bytes = UNALIGNED_LOAD32(candidate); - table[cur_hash] = (uint16)(ip - base_ip); - } while (GetUint32AtOffset(input_bytes, 1) == candidate_bytes); - - next_hash = HashBytes(GetUint32AtOffset(input_bytes, 2), shift); - ++ip; + // we also update table[Hash(ip - 1, mask)] and table[Hash(ip, mask)]. + table[HashBytes(LittleEndian::Load32(ip - 1), mask)] = (uint16_t)(ip - base_ip - 1); + uint32_t hash = HashBytes((uint32_t)data, mask); + candidate = base_ip + table[hash]; + table[hash] = (uint16_t)(ip - base_ip); + // Measurements on the benchmarks have shown the following probabilities + // for the loop to exit (ie. avg. number of iterations is reciprocal). + // BM_Flat/6 txt1 p = 0.3-0.4 + // BM_Flat/7 txt2 p = 0.35 + // BM_Flat/8 txt3 p = 0.3-0.4 + // BM_Flat/9 txt3 p = 0.34-0.4 + // BM_Flat/10 pb p = 0.4 + // BM_Flat/11 gaviota p = 0.1 + // BM_Flat/12 cp p = 0.5 + // BM_Flat/13 c p = 0.3 + } while (static_cast(data) == LittleEndian::Load32(candidate)); + // Because the least significant 5 bytes matched, we can utilize data + // for the next iteration. + preload = (uint32_t)(data >> 8); } } - emit_remainder: +emit_remainder: // Emit the remaining bytes as a literal - if (next_emit < ip_end) { - op = EmitLiteral(op, next_emit, - (int)(ip_end - next_emit)); + if (ip < ip_end) { + op = EmitLiteral(op, ip, (int)(ip_end - ip)); } return op; @@ -672,7 +899,12 @@ char* CompressFragment(const char* input, // Called back at avery compression call to trace parameters and sizes. static inline void Report(const char *algorithm, size_t compressed_size, - size_t uncompressed_size) {} + size_t uncompressed_size) { + // TODO: Switch to [[maybe_unused]] when we can assume C++17. + (void)algorithm; + (void)compressed_size; + (void)uncompressed_size; +} // Signature of output types needed by decompression code. // The decompression code is templatized on a type that obeys this @@ -684,12 +916,28 @@ static inline void Report(const char *algorithm, size_t compressed_size, // // Called before decompression // void SetExpectedLength(size_t length); // +// // For performance a writer may choose to donate the cursor variable to the +// // decompression function. The decompression will inject it in all its +// // function calls to the writer. Keeping the important output cursor as a +// // function local stack variable allows the compiler to keep it in +// // register, which greatly aids performance by avoiding loads and stores of +// // this variable in the fast path loop iterations. +// T GetOutputPtr() const; +// +// // At end of decompression the loop donates the ownership of the cursor +// // variable back to the writer by calling this function. +// void SetOutputPtr(T op); +// // // Called after decompression // bool CheckLength() const; // // // Called repeatedly during decompression -// bool Append(const char* ip, size_t length); -// bool AppendFromSelf(uint32 offset, size_t length); +// // Each function get a pointer to the op (output pointer), that the writer +// // can use and update. Note it's important that these functions get fully +// // inlined so that no actual address of the local variable needs to be +// // taken. +// bool Append(const char* ip, size_t length, T* op); +// bool AppendFromSelf(uint32_t offset, size_t length, T* op); // // // The rules for how TryFastAppend differs from Append are somewhat // // convoluted: @@ -711,25 +959,25 @@ static inline void Report(const char *algorithm, size_t compressed_size, // // as it is unlikely that one would implement a fast path accepting // // this much data. // // -// bool TryFastAppend(const char* ip, size_t available, size_t length); +// bool TryFastAppend(const char* ip, size_t available, size_t length, T* op); // }; -static inline uint32 ExtractLowBytes(uint32 v, int n) { +static inline uint32_t ExtractLowBytes(uint32_t v, int n) { assert(n >= 0); assert(n <= 4); #if SNAPPY_HAVE_BMI2 return _bzhi_u32(v, 8 * n); #else - // This needs to be wider than uint32 otherwise `mask << 32` will be + // This needs to be wider than uint32_t otherwise `mask << 32` will be // undefined. - uint64 mask = 0xffffffff; + uint64_t mask = 0xffffffff; return v & ~(mask << (8 * n)); #endif } -static inline bool LeftShiftOverflows(uint8 value, uint32 shift) { +static inline bool LeftShiftOverflows(uint8_t value, uint32_t shift) { assert(shift < 32); - static const uint8 masks[] = { + static const uint8_t masks[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // @@ -737,15 +985,194 @@ static inline bool LeftShiftOverflows(uint8 value, uint32 shift) { return (value & masks[shift]) != 0; } +inline bool Copy64BytesWithPatternExtension(ptrdiff_t dst, size_t offset) { + // TODO: Switch to [[maybe_unused]] when we can assume C++17. + (void)dst; + return offset != 0; +} + +void MemCopy(char* dst, const uint8_t* src, size_t size) { + std::memcpy(dst, src, size); +} + +void MemCopy(ptrdiff_t dst, const uint8_t* src, size_t size) { + // TODO: Switch to [[maybe_unused]] when we can assume C++17. + (void)dst; + (void)src; + (void)size; +} + +void MemMove(char* dst, const void* src, size_t size) { + std::memmove(dst, src, size); +} + +void MemMove(ptrdiff_t dst, const void* src, size_t size) { + // TODO: Switch to [[maybe_unused]] when we can assume C++17. + (void)dst; + (void)src; + (void)size; +} + +SNAPPY_ATTRIBUTE_ALWAYS_INLINE +size_t AdvanceToNextTag(const uint8_t** ip_p, size_t* tag) { + const uint8_t*& ip = *ip_p; + // This section is crucial for the throughput of the decompression loop. + // The latency of an iteration is fundamentally constrained by the + // following data chain on ip. + // ip -> c = Load(ip) -> ip1 = ip + 1 + (c & 3) -> ip = ip1 or ip2 + // ip2 = ip + 2 + (c >> 2) + // This amounts to 8 cycles. + // 5 (load) + 1 (c & 3) + 1 (lea ip1, [ip + (c & 3) + 1]) + 1 (cmov) + size_t literal_len = *tag >> 2; + size_t tag_type = *tag; + bool is_literal; +#if defined(__GNUC__) && defined(__x86_64__) && defined(__GCC_ASM_FLAG_OUTPUTS__) + // TODO clang misses the fact that the (c & 3) already correctly + // sets the zero flag. + asm("and $3, %k[tag_type]\n\t" + : [tag_type] "+r"(tag_type), "=@ccz"(is_literal)); +#else + tag_type &= 3; + is_literal = (tag_type == 0); +#endif + // TODO + // This is code is subtle. Loading the values first and then cmov has less + // latency then cmov ip and then load. However clang would move the loads + // in an optimization phase, volatile prevents this transformation. + // Note that we have enough slop bytes (64) that the loads are always valid. + size_t tag_literal = + static_cast(ip)[1 + literal_len]; + size_t tag_copy = static_cast(ip)[tag_type]; + *tag = is_literal ? tag_literal : tag_copy; + const uint8_t* ip_copy = ip + 1 + tag_type; + const uint8_t* ip_literal = ip + 2 + literal_len; + ip = is_literal ? ip_literal : ip_copy; +#if defined(__GNUC__) && defined(__x86_64__) + // TODO Clang is "optimizing" zero-extension (a totally free + // operation) this means that after the cmov of tag, it emits another movzb + // tag, byte(tag). It really matters as it's on the core chain. This dummy + // asm, persuades clang to do the zero-extension at the load (it's automatic) + // removing the expensive movzb. + asm("" ::"r"(tag_copy)); +#endif + return tag_type; +} + +// Extract the offset for copy-1 and copy-2 returns 0 for literals or copy-4. +inline uint32_t ExtractOffset(uint32_t val, size_t tag_type) { + return val & table.extract_masks[tag_type]; +}; + +// Core decompression loop, when there is enough data available. +// Decompresses the input buffer [ip, ip_limit) into the output buffer +// [op, op_limit_min_slop). Returning when either we are too close to the end +// of the input buffer, or we exceed op_limit_min_slop or when a exceptional +// tag is encountered (literal of length > 60) or a copy-4. +// Returns {ip, op} at the points it stopped decoding. +// TODO This function probably does not need to be inlined, as it +// should decode large chunks at a time. This allows runtime dispatch to +// implementations based on CPU capability (BMI2 / perhaps 32 / 64 byte memcpy). +template +std::pair DecompressBranchless( + const uint8_t* ip, const uint8_t* ip_limit, ptrdiff_t op, T op_base, + ptrdiff_t op_limit_min_slop) { + // We unroll the inner loop twice so we need twice the spare room. + op_limit_min_slop -= kSlopBytes; + if (2 * (kSlopBytes + 1) < ip_limit - ip && op < op_limit_min_slop) { + const uint8_t* const ip_limit_min_slop = ip_limit - 2 * kSlopBytes - 1; + ip++; + // ip points just past the tag and we are touching at maximum kSlopBytes + // in an iteration. + size_t tag = ip[-1]; + do { + // The throughput is limited by instructions, unrolling the inner loop + // twice reduces the amount of instructions checking limits and also + // leads to reduced mov's. + for (int i = 0; i < 2; i++) { + const uint8_t* old_ip = ip; + assert(tag == ip[-1]); + // For literals tag_type = 0, hence we will always obtain 0 from + // ExtractLowBytes. For literals offset will thus be kLiteralOffset. + ptrdiff_t len_min_offset = table.length_minus_offset[tag]; + size_t tag_type = AdvanceToNextTag(&ip, &tag); + uint32_t next = LittleEndian::Load32(old_ip); + size_t len = len_min_offset & 0xFF; + len_min_offset -= ExtractOffset(next, tag_type); + if (SNAPPY_PREDICT_FALSE(len_min_offset > 0)) { + if (SNAPPY_PREDICT_FALSE(len & 0x80)) { + // Exceptional case (long literal or copy 4). + // Actually doing the copy here is negatively impacting the main + // loop due to compiler incorrectly allocating a register for + // this fallback. Hence we just break. + break_loop: + ip = old_ip; + goto exit; + } + // Only copy-1 or copy-2 tags can get here. + assert(tag_type == 1 || tag_type == 2); + std::ptrdiff_t delta = op + len_min_offset - len; + // Guard against copies before the buffer start. + if (SNAPPY_PREDICT_FALSE(delta < 0 || + !Copy64BytesWithPatternExtension( + op_base + op, len - len_min_offset))) { + goto break_loop; + } + op += len; + continue; + } + std::ptrdiff_t delta = op + len_min_offset - len; + if (SNAPPY_PREDICT_FALSE(delta < 0)) { +#if defined(__GNUC__) && defined(__x86_64__) + // TODO + // When validating, both code path reduced to `op += len`. Ie. this + // becomes effectively + // + // if (delta < 0) if (tag_type != 0) goto break_loop; + // op += len; + // + // The compiler interchanges the predictable and almost always false + // first if-statement with the completely unpredictable second + // if-statement, putting an unpredictable branch on every iteration. + // This empty asm is worth almost 2x, which I think qualifies for an + // award for the most load-bearing empty statement. + asm(""); +#endif + + // Due to the spurious offset in literals have this will trigger + // at the start of a block when op is still smaller than 256. + if (tag_type != 0) goto break_loop; + MemCopy(op_base + op, old_ip, 64); + op += len; + continue; + } + + // For copies we need to copy from op_base + delta, for literals + // we need to copy from ip instead of from the stream. + const void* from = + tag_type ? reinterpret_cast(op_base + delta) : old_ip; + MemMove(op_base + op, from, 64); + op += len; + } + } while (ip < ip_limit_min_slop && op < op_limit_min_slop); + exit: + ip--; + assert(ip <= ip_limit); + } + return {ip, op}; +} + // Helper class for decompression class SnappyDecompressor { private: - Source* reader_; // Underlying source of bytes to decompress - const char* ip_; // Points to next buffered byte - const char* ip_limit_; // Points just past buffered bytes - uint32 peeked_; // Bytes peeked from reader (need to skip) - bool eof_; // Hit end of input without an error? - char scratch_[kMaximumTagLength]; // See RefillTag(). + Source* reader_; // Underlying source of bytes to decompress + const char* ip_; // Points to next buffered byte + const char* ip_limit_; // Points just past buffered bytes + // If ip < ip_limit_min_maxtaglen_ it's safe to read kMaxTagLength from + // buffer. + const char* ip_limit_min_maxtaglen_; + uint32_t peeked_; // Bytes peeked from reader (need to skip) + bool eof_; // Hit end of input without an error? + char scratch_[kMaximumTagLength]; // See RefillTag(). // Ensure that all of the tag metadata for the next tag is available // in [ip_..ip_limit_-1]. Also ensures that [ip,ip+4] is readable even @@ -754,14 +1181,14 @@ class SnappyDecompressor { // Returns true on success, false on error or end of input. bool RefillTag(); + void ResetLimit(const char* ip) { + ip_limit_min_maxtaglen_ = + ip_limit_ - std::min(ip_limit_ - ip, kMaximumTagLength - 1); + } + public: explicit SnappyDecompressor(Source* reader) - : reader_(reader), - ip_(NULL), - ip_limit_(NULL), - peeked_(0), - eof_(false) { - } + : reader_(reader), ip_(NULL), ip_limit_(NULL), peeked_(0), eof_(false) {} ~SnappyDecompressor() { // Advance past any bytes we peeked at from the reader @@ -769,18 +1196,16 @@ class SnappyDecompressor { } // Returns true iff we have hit the end of the input without an error. - bool eof() const { - return eof_; - } + bool eof() const { return eof_; } // Read the uncompressed length stored at the start of the compressed data. // On success, stores the length in *result and returns true. // On failure, returns false. - bool ReadUncompressedLength(uint32* result) { - assert(ip_ == NULL); // Must not have read anything yet + bool ReadUncompressedLength(uint32_t* result) { + assert(ip_ == NULL); // Must not have read anything yet // Length is encoded in 1..5 bytes *result = 0; - uint32 shift = 0; + uint32_t shift = 0; while (true) { if (shift >= 32) return false; size_t n; @@ -788,8 +1213,8 @@ class SnappyDecompressor { if (n == 0) return false; const unsigned char c = *(reinterpret_cast(ip)); reader_->Skip(1); - uint32 val = c & 0x7f; - if (LeftShiftOverflows(static_cast(val), shift)) return false; + uint32_t val = c & 0x7f; + if (LeftShiftOverflows(static_cast(val), shift)) return false; *result |= val << shift; if (c < 128) { break; @@ -805,38 +1230,44 @@ class SnappyDecompressor { #if defined(__GNUC__) && defined(__x86_64__) __attribute__((aligned(32))) #endif - void DecompressAllTags(Writer* writer) { - // In x86, pad the function body to start 16 bytes later. This function has - // a couple of hotspots that are highly sensitive to alignment: we have - // observed regressions by more than 20% in some metrics just by moving the - // exact same code to a different position in the benchmark binary. - // - // Putting this code on a 32-byte-aligned boundary + 16 bytes makes us hit - // the "lucky" case consistently. Unfortunately, this is a very brittle - // workaround, and future differences in code generation may reintroduce - // this regression. If you experience a big, difficult to explain, benchmark - // performance regression here, first try removing this hack. -#if defined(__GNUC__) && defined(__x86_64__) - // Two 8-byte "NOP DWORD ptr [EAX + EAX*1 + 00000000H]" instructions. - asm(".byte 0x0f, 0x1f, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00"); - asm(".byte 0x0f, 0x1f, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00"); -#endif - + void + DecompressAllTags(Writer* writer) { const char* ip = ip_; + ResetLimit(ip); + auto op = writer->GetOutputPtr(); // We could have put this refill fragment only at the beginning of the loop. // However, duplicating it at the end of each branch gives the compiler more // scope to optimize the expression based on the local // context, which overall increases speed. - #define MAYBE_REFILL() \ - if (ip_limit_ - ip < kMaximumTagLength) { \ - ip_ = ip; \ - if (!RefillTag()) return; \ - ip = ip_; \ - } +#define MAYBE_REFILL() \ + if (SNAPPY_PREDICT_FALSE(ip >= ip_limit_min_maxtaglen_)) { \ + ip_ = ip; \ + if (SNAPPY_PREDICT_FALSE(!RefillTag())) goto exit; \ + ip = ip_; \ + ResetLimit(ip); \ + } \ + preload = static_cast(*ip) + // At the start of the for loop below the least significant byte of preload + // contains the tag. + uint32_t preload; MAYBE_REFILL(); - for ( ;; ) { - const unsigned char c = *(reinterpret_cast(ip++)); + for (;;) { + { + ptrdiff_t op_limit_min_slop; + auto op_base = writer->GetBase(&op_limit_min_slop); + if (op_base) { + auto res = + DecompressBranchless(reinterpret_cast(ip), + reinterpret_cast(ip_limit_), + op - op_base, op_base, op_limit_min_slop); + ip = reinterpret_cast(res.first); + op = op_base + res.second; + MAYBE_REFILL(); + } + } + const uint8_t c = static_cast(preload); + ip++; // Ratio of iterations that have LITERAL vs non-LITERAL for different // inputs. @@ -852,70 +1283,102 @@ class SnappyDecompressor { // bin 24% 76% if (SNAPPY_PREDICT_FALSE((c & 0x3) == LITERAL)) { size_t literal_length = (c >> 2) + 1u; - if (writer->TryFastAppend(ip, ip_limit_ - ip, literal_length)) { + if (writer->TryFastAppend(ip, ip_limit_ - ip, literal_length, &op)) { assert(literal_length < 61); ip += literal_length; // NOTE: There is no MAYBE_REFILL() here, as TryFastAppend() // will not return true unless there's already at least five spare // bytes in addition to the literal. + preload = static_cast(*ip); continue; } if (SNAPPY_PREDICT_FALSE(literal_length >= 61)) { // Long literal. const size_t literal_length_length = literal_length - 60; literal_length = - ExtractLowBytes(LittleEndian::Load32(ip), (int)literal_length_length) + + ExtractLowBytes(LittleEndian::Load32(ip), (uint32_t)literal_length_length) + 1; ip += literal_length_length; } size_t avail = ip_limit_ - ip; while (avail < literal_length) { - if (!writer->Append(ip, avail)) return; + if (!writer->Append(ip, avail, &op)) goto exit; literal_length -= avail; reader_->Skip(peeked_); size_t n; ip = reader_->Peek(&n); avail = n; - peeked_ = (uint32)avail; - if (avail == 0) return; // Premature end of input + peeked_ = (uint32_t)avail; + if (avail == 0) goto exit; ip_limit_ = ip + avail; + ResetLimit(ip); } - if (!writer->Append(ip, literal_length)) { - return; - } + if (!writer->Append(ip, literal_length, &op)) goto exit; ip += literal_length; MAYBE_REFILL(); } else { - const size_t entry = char_table[c]; - const size_t trailer = - ExtractLowBytes(LittleEndian::Load32(ip), (int)entry >> 11); - const size_t length = entry & 0xff; - ip += entry >> 11; + if (SNAPPY_PREDICT_FALSE((c & 3) == COPY_4_BYTE_OFFSET)) { + const size_t copy_offset = LittleEndian::Load32(ip); + const size_t length = (c >> 2) + 1; + ip += 4; - // copy_offset/256 is encoded in bits 8..10. By just fetching - // those bits, we get copy_offset (since the bit-field starts at - // bit 8). - const size_t copy_offset = entry & 0x700; - if (!writer->AppendFromSelf(copy_offset + trailer, length)) { - return; + if (!writer->AppendFromSelf(copy_offset, length, &op)) goto exit; + } else { + const ptrdiff_t entry = table.length_minus_offset[c]; + preload = LittleEndian::Load32(ip); + const uint32_t trailer = ExtractLowBytes(preload, c & 3); + const uint32_t length = entry & 0xff; + assert(length > 0); + + // copy_offset/256 is encoded in bits 8..10. By just fetching + // those bits, we get copy_offset (since the bit-field starts at + // bit 8). + const uint32_t copy_offset = (uint32_t)(trailer - entry + length); + if (!writer->AppendFromSelf(copy_offset, length, &op)) goto exit; + + ip += (c & 3); + // By using the result of the previous load we reduce the critical + // dependency chain of ip to 4 cycles. + preload >>= (c & 3) * 8; + if (ip < ip_limit_min_maxtaglen_) continue; } MAYBE_REFILL(); } } - #undef MAYBE_REFILL + exit: + writer->SetOutputPtr(op); } }; +constexpr uint32_t CalculateNeeded(uint8_t tag) { + return ((tag & 3) == 0 && tag >= (60 * 4)) + ? (tag >> 2) - 58 + : (0x05030201 >> ((tag * 8) & 31)) & 0xFF; +} + +#if __cplusplus >= 201402L +constexpr bool VerifyCalculateNeeded() { + for (int i = 0; i < 1; i++) { + if (CalculateNeeded(i) != (char_table[i] >> 11) + 1) return false; + } + return true; +} + +// Make sure CalculateNeeded is correct by verifying it against the established +// table encoding the number of added bytes needed. +static_assert(VerifyCalculateNeeded(), ""); +#endif // c++14 + bool SnappyDecompressor::RefillTag() { const char* ip = ip_; if (ip == ip_limit_) { // Fetch a new fragment from the reader - reader_->Skip(peeked_); // All peeked bytes are used up + reader_->Skip(peeked_); // All peeked bytes are used up size_t n; ip = reader_->Peek(&n); - peeked_ = (uint32)n; + peeked_ = (uint32_t)n; eof_ = (n == 0); if (eof_) return false; ip_limit_ = ip + n; @@ -924,26 +1387,31 @@ bool SnappyDecompressor::RefillTag() { // Read the tag character assert(ip < ip_limit_); const unsigned char c = *(reinterpret_cast(ip)); - const uint32 entry = char_table[c]; - const uint32 needed = (entry >> 11) + 1; // +1 byte for 'c' + // At this point make sure that the data for the next tag is consecutive. + // For copy 1 this means the next 2 bytes (tag and 1 byte offset) + // For copy 2 the next 3 bytes (tag and 2 byte offset) + // For copy 4 the next 5 bytes (tag and 4 byte offset) + // For all small literals we only need 1 byte buf for literals 60...63 the + // length is encoded in 1...4 extra bytes. + const uint32_t needed = CalculateNeeded(c); assert(needed <= sizeof(scratch_)); // Read more bytes from reader if needed - uint32 nbuf = (uint32)(ip_limit_ - ip); + uint32_t nbuf = (uint32_t)(ip_limit_ - ip); if (nbuf < needed) { // Stitch together bytes from ip and reader to form the word // contents. We store the needed bytes in "scratch_". They // will be consumed immediately by the caller since we do not // read more than we need. - memmove(scratch_, ip, nbuf); + std::memmove(scratch_, ip, nbuf); reader_->Skip(peeked_); // All peeked bytes are used up peeked_ = 0; while (nbuf < needed) { size_t length; const char* src = reader_->Peek(&length); if (length == 0) return false; - uint32 to_add = std::min(needed - nbuf, (uint32)length); - memcpy(scratch_ + nbuf, src, to_add); + uint32_t to_add = std::min(needed - nbuf, (uint32_t)length); + std::memcpy(scratch_ + nbuf, src, to_add); nbuf += to_add; reader_->Skip(to_add); } @@ -953,7 +1421,7 @@ bool SnappyDecompressor::RefillTag() { } else if (nbuf < kMaximumTagLength) { // Have enough bytes, but move into scratch_ so that we do not // read past end of input - memmove(scratch_, ip, nbuf); + std::memmove(scratch_, ip, nbuf); reader_->Skip(peeked_); // All peeked bytes are used up peeked_ = 0; ip_ = scratch_; @@ -969,18 +1437,17 @@ template static bool InternalUncompress(Source* r, Writer* writer) { // Read the uncompressed length from the front of the compressed input SnappyDecompressor decompressor(r); - uint32 uncompressed_len = 0; + uint32_t uncompressed_len = 0; if (!decompressor.ReadUncompressedLength(&uncompressed_len)) return false; - return InternalUncompressAllTags(&decompressor, writer, (uint32)r->Available(), + return InternalUncompressAllTags(&decompressor, writer, (uint32_t)r->Available(), uncompressed_len); } template static bool InternalUncompressAllTags(SnappyDecompressor* decompressor, - Writer* writer, - uint32 compressed_len, - uint32 uncompressed_len) { + Writer* writer, uint32_t compressed_len, + uint32_t uncompressed_len) { Report("snappy_uncompress", compressed_len, uncompressed_len); writer->SetExpectedLength(uncompressed_len); @@ -991,7 +1458,7 @@ static bool InternalUncompressAllTags(SnappyDecompressor* decompressor, return (decompressor->eof() && writer->CheckLength()); } -bool GetUncompressedLength(Source* source, uint32* result) { +bool GetUncompressedLength(Source* source, uint32_t* result) { SnappyDecompressor decompressor(source); return decompressor.ReadUncompressedLength(result); } @@ -1001,8 +1468,8 @@ size_t Compress(Source* reader, Sink* writer) { size_t N = reader->Available(); const size_t uncompressed_size = N; char ulength[Varint::kMax32]; - char* p = Varint::Encode32(ulength, (uint32)N); - writer->Append(ulength, p-ulength); + char* p = Varint::Encode32(ulength, (uint32_t)N); + writer->Append(ulength, p - ulength); written += (p - ulength); internal::WorkingMemory wmem(N); @@ -1022,13 +1489,13 @@ size_t Compress(Source* reader, Sink* writer) { fragment_size = num_to_read; } else { char* scratch = wmem.GetScratchInput(); - memcpy(scratch, fragment, bytes_read); + std::memcpy(scratch, fragment, bytes_read); reader->Skip(bytes_read); while (bytes_read < num_to_read) { fragment = reader->Peek(&fragment_size); size_t n = std::min(fragment_size, num_to_read - bytes_read); - memcpy(scratch + bytes_read, fragment, n); + std::memcpy(scratch + bytes_read, fragment, n); bytes_read += n; reader->Skip(n); } @@ -1040,7 +1507,7 @@ size_t Compress(Source* reader, Sink* writer) { // Get encoding table for compression int table_size; - uint16* table = wmem.GetHashTable(num_to_read, &table_size); + uint16_t* table = wmem.GetHashTable(num_to_read, &table_size); // Compress input_fragment and append to dest const int max_output = (int)MaxCompressedLength(num_to_read); @@ -1115,17 +1582,14 @@ class SnappyIOVecWriter { : nullptr), curr_iov_remaining_(iov_count ? iov->iov_len : 0), total_written_(0), - output_limit_(-1) {} - - inline void SetExpectedLength(size_t len) { - output_limit_ = len; + output_limit_(-1) { } - inline bool CheckLength() const { - return total_written_ == output_limit_; - } + inline void SetExpectedLength(size_t len) { output_limit_ = len; } - inline bool Append(const char* ip, size_t len) { + inline bool CheckLength() const { return total_written_ == output_limit_; } + + inline bool Append(const char* ip, size_t len, char**) { if (total_written_ + len > output_limit_) { return false; } @@ -1133,6 +1597,13 @@ class SnappyIOVecWriter { return AppendNoCheck(ip, len); } + char* GetOutputPtr() { return nullptr; } + char* GetBase(ptrdiff_t*) { return nullptr; } + void SetOutputPtr(char* op) { + // TODO: Switch to [[maybe_unused]] when we can assume C++17. + (void)op; + } + inline bool AppendNoCheck(const char* ip, size_t len) { while (len > 0) { if (curr_iov_remaining_ == 0) { @@ -1146,7 +1617,7 @@ class SnappyIOVecWriter { } const size_t to_write = std::min(len, curr_iov_remaining_); - memcpy(curr_iov_output_, ip, to_write); + std::memcpy(curr_iov_output_, ip, to_write); curr_iov_output_ += to_write; curr_iov_remaining_ -= to_write; total_written_ += to_write; @@ -1157,7 +1628,8 @@ class SnappyIOVecWriter { return true; } - inline bool TryFastAppend(const char* ip, size_t available, size_t len) { + inline bool TryFastAppend(const char* ip, size_t available, size_t len, + char**) { const size_t space_left = output_limit_ - total_written_; if (len <= 16 && available >= 16 + kMaximumTagLength && space_left >= 16 && curr_iov_remaining_ >= 16) { @@ -1172,7 +1644,7 @@ class SnappyIOVecWriter { return false; } - inline bool AppendFromSelf(size_t offset, size_t len) { + inline bool AppendFromSelf(size_t offset, size_t len, char**) { // See SnappyArrayWriter::AppendFromSelf for an explanation of // the "offset - 1u" trick. if (offset - 1u >= total_written_) { @@ -1228,6 +1700,7 @@ class SnappyIOVecWriter { if (to_copy > len) { to_copy = len; } + assert(to_copy > 0); IncrementalCopy(GetIOVecPointer(from_iov, from_iov_offset), curr_iov_output_, curr_iov_output_ + to_copy, @@ -1270,59 +1743,74 @@ class SnappyArrayWriter { char* base_; char* op_; char* op_limit_; + // If op < op_limit_min_slop_ then it's safe to unconditionally write + // kSlopBytes starting at op. + char* op_limit_min_slop_; public: inline explicit SnappyArrayWriter(char* dst) : base_(dst), op_(dst), - op_limit_(dst) { - } + op_limit_(dst), + op_limit_min_slop_(dst) {} // Safe default see invariant. inline void SetExpectedLength(size_t len) { op_limit_ = op_ + len; + // Prevent pointer from being past the buffer. + op_limit_min_slop_ = op_limit_ - std::min(kSlopBytes - 1, len); } - inline bool CheckLength() const { - return op_ == op_limit_; - } + inline bool CheckLength() const { return op_ == op_limit_; } - inline bool Append(const char* ip, size_t len) { - char* op = op_; + char* GetOutputPtr() { return op_; } + char* GetBase(ptrdiff_t* op_limit_min_slop) { + *op_limit_min_slop = op_limit_min_slop_ - base_; + return base_; + } + void SetOutputPtr(char* op) { op_ = op; } + + inline bool Append(const char* ip, size_t len, char** op_p) { + char* op = *op_p; const size_t space_left = op_limit_ - op; - if (space_left < len) { - return false; - } - memcpy(op, ip, len); - op_ = op + len; + if (space_left < len) return false; + std::memcpy(op, ip, len); + *op_p = op + len; return true; } - inline bool TryFastAppend(const char* ip, size_t available, size_t len) { - char* op = op_; + inline bool TryFastAppend(const char* ip, size_t available, size_t len, + char** op_p) { + char* op = *op_p; const size_t space_left = op_limit_ - op; if (len <= 16 && available >= 16 + kMaximumTagLength && space_left >= 16) { // Fast path, used for the majority (about 95%) of invocations. UnalignedCopy128(ip, op); - op_ = op + len; + *op_p = op + len; return true; } else { return false; } } - inline bool AppendFromSelf(size_t offset, size_t len) { - char* const op_end = op_ + len; + SNAPPY_ATTRIBUTE_ALWAYS_INLINE + inline bool AppendFromSelf(size_t offset, size_t len, char** op_p) { + assert(len > 0); + char* const op = *op_p; + assert(op >= base_); + char* const op_end = op + len; // Check if we try to append from before the start of the buffer. - // Normally this would just be a check for "produced < offset", - // but "produced <= offset - 1u" is equivalent for every case - // except the one where offset==0, where the right side will wrap around - // to a very big number. This is convenient, as offset==0 is another - // invalid case that we also want to catch, so that we do not go - // into an infinite loop. - if (Produced() <= offset - 1u || op_end > op_limit_) return false; - op_ = IncrementalCopy(op_ - offset, op_, op_end, op_limit_); + if (SNAPPY_PREDICT_FALSE(static_cast(op - base_) < offset)) + return false; + if (SNAPPY_PREDICT_FALSE((kSlopBytes < 64 && len > kSlopBytes) || + op >= op_limit_min_slop_ || offset < len)) { + if (op_end > op_limit_ || offset == 0) return false; + *op_p = IncrementalCopy(op - offset, op, op_end, op_limit_); + return true; + } + std::memmove(op, op - offset, kSlopBytes); + *op_p = op_end; return true; } inline size_t Produced() const { @@ -1332,8 +1820,9 @@ class SnappyArrayWriter { inline void Flush() {} }; -bool RawUncompress(const char* compressed, size_t n, char* uncompressed) { - ByteArraySource reader(compressed, n); +bool RawUncompress(const char* compressed, size_t compressed_length, + char* uncompressed) { + ByteArraySource reader(compressed, compressed_length); return RawUncompress(&reader, uncompressed); } @@ -1342,9 +1831,10 @@ bool RawUncompress(Source* compressed, char* uncompressed) { return InternalUncompress(compressed, &output); } -bool Uncompress(const char* compressed, size_t n, std::string* uncompressed) { +bool Uncompress(const char* compressed, size_t compressed_length, + std::string* uncompressed) { size_t ulength; - if (!GetUncompressedLength(compressed, n, &ulength)) { + if (!GetUncompressedLength(compressed, compressed_length, &ulength)) { return false; } // On 32-bit builds: max_size() < kuint32max. Check for that instead @@ -1353,7 +1843,8 @@ bool Uncompress(const char* compressed, size_t n, std::string* uncompressed) { return false; } STLStringResizeUninitialized(uncompressed, ulength); - return RawUncompress(compressed, n, string_as_array(uncompressed)); + return RawUncompress(compressed, compressed_length, + string_as_array(uncompressed)); } // A Writer that drops everything on the floor and just does validation @@ -1363,32 +1854,44 @@ class SnappyDecompressionValidator { size_t produced_; public: - inline SnappyDecompressionValidator() : expected_(0), produced_(0) { } - inline void SetExpectedLength(size_t len) { - expected_ = len; + inline SnappyDecompressionValidator() : expected_(0), produced_(0) {} + inline void SetExpectedLength(size_t len) { expected_ = len; } + size_t GetOutputPtr() { return produced_; } + size_t GetBase(ptrdiff_t* op_limit_min_slop) { + *op_limit_min_slop = std::numeric_limits::max() - kSlopBytes + 1; + return 1; } - inline bool CheckLength() const { - return expected_ == produced_; + void SetOutputPtr(size_t op) { produced_ = op; } + inline bool CheckLength() const { return expected_ == produced_; } + inline bool Append(const char* ip, size_t len, size_t* produced) { + // TODO: Switch to [[maybe_unused]] when we can assume C++17. + (void)ip; + + *produced += len; + return *produced <= expected_; } - inline bool Append(const char* ip, size_t len) { - produced_ += len; - return produced_ <= expected_; - } - inline bool TryFastAppend(const char* ip, size_t available, size_t length) { + inline bool TryFastAppend(const char* ip, size_t available, size_t length, + size_t* produced) { + // TODO: Switch to [[maybe_unused]] when we can assume C++17. + (void)ip; + (void)available; + (void)length; + (void)produced; + return false; } - inline bool AppendFromSelf(size_t offset, size_t len) { + inline bool AppendFromSelf(size_t offset, size_t len, size_t* produced) { // See SnappyArrayWriter::AppendFromSelf for an explanation of // the "offset - 1u" trick. - if (produced_ <= offset - 1u) return false; - produced_ += len; - return produced_ <= expected_; + if (*produced <= offset - 1u) return false; + *produced += len; + return *produced <= expected_; } inline void Flush() {} }; -bool IsValidCompressedBuffer(const char* compressed, size_t n) { - ByteArraySource reader(compressed, n); +bool IsValidCompressedBuffer(const char* compressed, size_t compressed_length) { + ByteArraySource reader(compressed, compressed_length); SnappyDecompressionValidator writer; return InternalUncompress(&reader, &writer); } @@ -1398,9 +1901,7 @@ bool IsValidCompressed(Source* compressed) { return InternalUncompress(compressed, &writer); } -void RawCompress(const char* input, - size_t input_length, - char* compressed, +void RawCompress(const char* input, size_t input_length, char* compressed, size_t* compressed_length) { ByteArraySource reader(input, input_length); UncheckedByteArraySink writer(compressed); @@ -1443,13 +1944,14 @@ class SnappyScatteredWriter { size_t full_size_; // Pointer into current output block - char* op_base_; // Base of output block - char* op_ptr_; // Pointer to next unfilled byte in block - char* op_limit_; // Pointer just past block + char* op_base_; // Base of output block + char* op_ptr_; // Pointer to next unfilled byte in block + char* op_limit_; // Pointer just past block + // If op < op_limit_min_slop_ then it's safe to unconditionally write + // kSlopBytes starting at op. + char* op_limit_min_slop_; - inline size_t Size() const { - return full_size_ + (op_ptr_ - op_base_); - } + inline size_t Size() const { return full_size_ + (op_ptr_ - op_base_); } bool SlowAppend(const char* ip, size_t len); bool SlowAppendFromSelf(size_t offset, size_t len); @@ -1460,60 +1962,79 @@ class SnappyScatteredWriter { full_size_(0), op_base_(NULL), op_ptr_(NULL), - op_limit_(NULL) { + op_limit_(NULL), + op_limit_min_slop_(NULL) {} + char* GetOutputPtr() { return op_ptr_; } + char* GetBase(ptrdiff_t* op_limit_min_slop) { + *op_limit_min_slop = op_limit_min_slop_ - op_base_; + return op_base_; } + void SetOutputPtr(char* op) { op_ptr_ = op; } inline void SetExpectedLength(size_t len) { assert(blocks_.empty()); expected_ = len; } - inline bool CheckLength() const { - return Size() == expected_; - } + inline bool CheckLength() const { return Size() == expected_; } // Return the number of bytes actually uncompressed so far - inline size_t Produced() const { - return Size(); - } + inline size_t Produced() const { return Size(); } - inline bool Append(const char* ip, size_t len) { - size_t avail = op_limit_ - op_ptr_; + inline bool Append(const char* ip, size_t len, char** op_p) { + char* op = *op_p; + size_t avail = op_limit_ - op; if (len <= avail) { // Fast path - memcpy(op_ptr_, ip, len); - op_ptr_ += len; + std::memcpy(op, ip, len); + *op_p = op + len; return true; } else { - return SlowAppend(ip, len); + op_ptr_ = op; + bool res = SlowAppend(ip, len); + *op_p = op_ptr_; + return res; } } - inline bool TryFastAppend(const char* ip, size_t available, size_t length) { - char* op = op_ptr_; + inline bool TryFastAppend(const char* ip, size_t available, size_t length, + char** op_p) { + char* op = *op_p; const int space_left = (int)(op_limit_ - op); if (length <= 16 && available >= 16 + kMaximumTagLength && space_left >= 16) { // Fast path, used for the majority (about 95%) of invocations. UnalignedCopy128(ip, op); - op_ptr_ = op + length; + *op_p = op + length; return true; } else { return false; } } - inline bool AppendFromSelf(size_t offset, size_t len) { - char* const op_end = op_ptr_ + len; - // See SnappyArrayWriter::AppendFromSelf for an explanation of - // the "offset - 1u" trick. - if (SNAPPY_PREDICT_TRUE(offset - 1u < (size_t)(op_ptr_ - op_base_) && - op_end <= op_limit_)) { - // Fast path: src and dst in current block. - op_ptr_ = IncrementalCopy(op_ptr_ - offset, op_ptr_, op_end, op_limit_); + inline bool AppendFromSelf(size_t offset, size_t len, char** op_p) { + char* op = *op_p; + assert(op >= op_base_); + // Check if we try to append from before the start of the buffer. + if (SNAPPY_PREDICT_FALSE((kSlopBytes < 64 && len > kSlopBytes) || + static_cast(op - op_base_) < offset || + op >= op_limit_min_slop_ || offset < len)) { + if (offset == 0) return false; + if (SNAPPY_PREDICT_FALSE(static_cast(op - op_base_) < offset || + op + len > op_limit_)) { + op_ptr_ = op; + bool res = SlowAppendFromSelf(offset, len); + *op_p = op_ptr_; + return res; + } + *op_p = IncrementalCopy(op - offset, op, op + len, op_limit_); return true; } - return SlowAppendFromSelf(offset, len); + // Fast path + char* const op_end = op + len; + std::memmove(op, op - offset, kSlopBytes); + *op_p = op_end; + return true; } // Called at the end of the decompress. We ask the allocator @@ -1521,12 +2042,12 @@ class SnappyScatteredWriter { inline void Flush() { allocator_.Flush(Produced()); } }; -template +template bool SnappyScatteredWriter::SlowAppend(const char* ip, size_t len) { size_t avail = op_limit_ - op_ptr_; while (len > avail) { // Completely fill this block - memcpy(op_ptr_, ip, avail); + std::memcpy(op_ptr_, ip, avail); op_ptr_ += avail; assert(op_limit_ - op_ptr_ == 0); full_size_ += (op_ptr_ - op_base_); @@ -1534,25 +2055,25 @@ bool SnappyScatteredWriter::SlowAppend(const char* ip, size_t len) { ip += avail; // Bounds check - if (full_size_ + len > expected_) { - return false; - } + if (full_size_ + len > expected_) return false; // Make new block size_t bsize = std::min(kBlockSize, expected_ - full_size_); op_base_ = allocator_.Allocate((int)bsize); op_ptr_ = op_base_; op_limit_ = op_base_ + bsize; + op_limit_min_slop_ = op_limit_ - std::min(kSlopBytes - 1, bsize); + blocks_.push_back(op_base_); avail = bsize; } - memcpy(op_ptr_, ip, len); + std::memcpy(op_ptr_, ip, len); op_ptr_ += len; return true; } -template +template bool SnappyScatteredWriter::SlowAppendFromSelf(size_t offset, size_t len) { // Overflow check @@ -1567,18 +2088,26 @@ bool SnappyScatteredWriter::SlowAppendFromSelf(size_t offset, // nice if we do not rely on that, since we can get better compression if we // allow cross-block copies and thus might want to change the compressor in // the future. + // TODO Replace this with a properly optimized path. This is not + // triggered right now. But this is so super slow, that it would regress + // performance unacceptably if triggered. size_t src = cur - offset; + char* op = op_ptr_; while (len-- > 0) { - char c = blocks_[src >> kBlockLog][src & (kBlockSize-1)]; - Append(&c, 1); + char c = blocks_[src >> kBlockLog][src & (kBlockSize - 1)]; + if (!Append(&c, 1, &op)) { + op_ptr_ = op; + return false; + } src++; } + op_ptr_ = op; return true; } class SnappySinkAllocator { public: - explicit SnappySinkAllocator(Sink* dest): dest_(dest) {} + explicit SnappySinkAllocator(Sink* dest) : dest_(dest) {} ~SnappySinkAllocator() {} char* Allocate(int size) { @@ -1594,10 +2123,9 @@ class SnappySinkAllocator { // to the blocks. void Flush(size_t size) { size_t size_written = 0; - size_t block_size; - for (int i = 0; i < blocks_.size(); ++i) { - block_size = std::min(blocks_[i].size, size - size_written); - dest_->AppendAndTakeOwnership(blocks_[i].data, block_size, + for (Datablock& block : blocks_) { + size_t block_size = std::min(block.size, size - size_written); + dest_->AppendAndTakeOwnership(block.data, block_size, &SnappySinkAllocator::Deleter, NULL); size_written += block_size; } @@ -1612,6 +2140,10 @@ class SnappySinkAllocator { }; static void Deleter(void* arg, const char* bytes, size_t size) { + // TODO: Switch to [[maybe_unused]] when we can assume C++17. + (void)arg; + (void)size; + delete[] bytes; } @@ -1631,15 +2163,15 @@ size_t UncompressAsMuchAsPossible(Source* compressed, Sink* uncompressed) { bool Uncompress(Source* compressed, Sink* uncompressed) { // Read the uncompressed length from the front of the compressed input SnappyDecompressor decompressor(compressed); - uint32 uncompressed_len = 0; + uint32_t uncompressed_len = 0; if (!decompressor.ReadUncompressedLength(&uncompressed_len)) { return false; } char c; size_t allocated_size; - char* buf = uncompressed->GetAppendBufferVariable( - 1, uncompressed_len, &c, 1, &allocated_size); + char* buf = uncompressed->GetAppendBufferVariable(1, uncompressed_len, &c, 1, + &allocated_size); const size_t compressed_len = compressed->Available(); // If we can get a flat buffer, then use it, otherwise do block by block @@ -1647,13 +2179,13 @@ bool Uncompress(Source* compressed, Sink* uncompressed) { if (allocated_size >= uncompressed_len) { SnappyArrayWriter writer(buf); bool result = InternalUncompressAllTags(&decompressor, &writer, - (uint32)compressed_len, uncompressed_len); + (uint32_t)compressed_len, uncompressed_len); uncompressed->Append(buf, writer.Produced()); return result; } else { SnappySinkAllocator allocator(uncompressed); SnappyScatteredWriter writer(allocator); - return InternalUncompressAllTags(&decompressor, &writer, (uint32)compressed_len, + return InternalUncompressAllTags(&decompressor, &writer, (uint32_t)compressed_len, uncompressed_len); } } diff --git a/ext/snappy/snappy.h b/ext/snappy/snappy.h index e9805bfb7d..e4fdad3354 100644 --- a/ext/snappy/snappy.h +++ b/ext/snappy/snappy.h @@ -39,7 +39,9 @@ #ifndef THIRD_PARTY_SNAPPY_SNAPPY_H__ #define THIRD_PARTY_SNAPPY_SNAPPY_H__ -#include +#include +#include + #include #include "snappy-stubs-public.h" @@ -63,7 +65,7 @@ namespace snappy { // Also note that this leaves "*source" in a state that is unsuitable for // further operations, such as RawUncompress(). You will need to rewind // or recreate the source yourself before attempting any further calls. - bool GetUncompressedLength(Source* source, uint32* result); + bool GetUncompressedLength(Source* source, uint32_t* result); // ------------------------------------------------------------------------ // Higher-level string based routines (should be sufficient for most users) diff --git a/ext/xbrz/xbrz.h b/ext/xbrz/xbrz.h index 1f680110ee..e1e57930bb 100644 --- a/ext/xbrz/xbrz.h +++ b/ext/xbrz/xbrz.h @@ -19,8 +19,9 @@ #undef min #undef max +#include "ppsspp_config.h" #include //size_t -#if defined(IOS) +#if PPSSPP_PLATFORM(IOS) #include #else #include //uint32_t diff --git a/ext/xxhash.h b/ext/xxhash.h index 1790daf267..8f61dd6276 100644 --- a/ext/xxhash.h +++ b/ext/xxhash.h @@ -71,6 +71,7 @@ XXH64 13.8 GB/s 1.9 GB/s XXH32 6.8 GB/s 6.0 GB/s */ +#include "ppsspp_config.h" #if defined (__cplusplus) extern "C" { #endif @@ -1366,7 +1367,7 @@ XXH32_endian_align(const xxh_u8* input, size_t len, xxh_u32 seed, XXH_alignment xxh_u32 v4 = seed - XXH_PRIME32_1; do { -#if defined(ARM) && defined(__GNUC__) +#if PPSSPP_ARCH(ARM) && defined(__GNUC__) __builtin_prefetch(input + 0xc0, 0, 0); #endif v1 = XXH32_round(v1, XXH_get32bits(input)); input += 4; diff --git a/ffmpeg b/ffmpeg index 4738685f4a..0b28335ace 160000 --- a/ffmpeg +++ b/ffmpeg @@ -1 +1 @@ -Subproject commit 4738685f4ac27c1775a238d1e602f399627b5e6f +Subproject commit 0b28335acea4f429ae798c5e75232e54881bf164 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 3967b35ba1..bab4a13390 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-all.zip diff --git a/headless/Headless.cpp b/headless/Headless.cpp index f0d240e2f1..234b7b5ddb 100644 --- a/headless/Headless.cpp +++ b/headless/Headless.cpp @@ -89,7 +89,14 @@ std::string System_GetProperty(SystemProperty prop) { return ""; } std::vector System_GetPropertyStringVec(SystemProperty prop) { return std::vector(); } int System_GetPropertyInt(SystemProperty prop) { return -1; } float System_GetPropertyFloat(SystemProperty prop) { return -1.0f; } -bool System_GetPropertyBool(SystemProperty prop) { return false; } +bool System_GetPropertyBool(SystemProperty prop) { + switch (prop) { + case SYSPROP_CAN_JIT: + return true; + default: + return false; + } +} void System_SendMessage(const char *command, const char *parameter) {} void System_InputBoxGetString(const std::string &title, const std::string &defaultValue, std::function cb) { cb(false, ""); } diff --git a/headless/Headless.vcxproj b/headless/Headless.vcxproj index ec1d4b2e76..c5cfc506c3 100644 --- a/headless/Headless.vcxproj +++ b/headless/Headless.vcxproj @@ -175,7 +175,7 @@ NotUsing Level3 - _CRTDBG_MAP_ALLOC;USING_WIN_UI;GLEW_STATIC;_CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_ARCH_32=1;_CONSOLE;_UNICODE;UNICODE;%(PreprocessorDefinitions);_CRT_SECURE_NO_WARNINGS + _CRTDBG_MAP_ALLOC;USING_WIN_UI;GLEW_STATIC;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_ARCH_32=1;_CONSOLE;_UNICODE;UNICODE;%(PreprocessorDefinitions) ../ffmpeg/Windows/x86/include;../dx9sdk/Include/DX11;../Common;..;../Core;../ext/glew EnableFastChecks StreamingSIMDExtensions2 @@ -208,7 +208,7 @@ NotUsing Level3 - _CRTDBG_MAP_ALLOC;USING_WIN_UI;GLEW_STATIC;_CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_ARCH_64=1;_CONSOLE;_UNICODE;UNICODE;%(PreprocessorDefinitions) + _CRTDBG_MAP_ALLOC;USING_WIN_UI;GLEW_STATIC;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_ARCH_64=1;_CONSOLE;_UNICODE;UNICODE;%(PreprocessorDefinitions) ../ffmpeg/Windows/x86_64/include;../dx9sdk/Include/DX11;../Common;..;../Core;../ext/glew NotSet Precise @@ -241,7 +241,7 @@ NotUsing Level3 - _CRTDBG_MAP_ALLOC;USING_WIN_UI;GLEW_STATIC;_CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_ARCH_64=1;_CONSOLE;_UNICODE;UNICODE;%(PreprocessorDefinitions) + _CRTDBG_MAP_ALLOC;USING_WIN_UI;GLEW_STATIC;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_ARCH_64=1;_CONSOLE;_UNICODE;UNICODE;%(PreprocessorDefinitions) ../ffmpeg/Windows/aarch64/include;../dx9sdk/Include/DX11;../Common;..;../Core;../ext/glew Precise false @@ -270,7 +270,7 @@ NotUsing Level3 - _CRTDBG_MAP_ALLOC;USING_WIN_UI;GLEW_STATIC;_CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_ARCH_32=1;_CONSOLE;_UNICODE;UNICODE;%(PreprocessorDefinitions) + _CRTDBG_MAP_ALLOC;USING_WIN_UI;GLEW_STATIC;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_ARCH_32=1;_CONSOLE;_UNICODE;UNICODE;%(PreprocessorDefinitions) ../ffmpeg/Windows/arm/include;../dx9sdk/Include/DX11;../Common;..;../Core;../ext/glew Precise false @@ -302,7 +302,7 @@ NotUsing true true - USING_WIN_UI;GLEW_STATIC;_CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_ARCH_32=1;_CONSOLE;_UNICODE;UNICODE;%(PreprocessorDefinitions);_CRT_SECURE_NO_WARNINGS + USING_WIN_UI;GLEW_STATIC;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_ARCH_32=1;_CONSOLE;_UNICODE;UNICODE;%(PreprocessorDefinitions) ../ffmpeg/Windows/x86/include;../dx9sdk/Include/DX11;../Common;..;../Core;../ext/glew false StreamingSIMDExtensions2 @@ -338,7 +338,7 @@ NotUsing true true - USING_WIN_UI;GLEW_STATIC;_CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_ARCH_64=1;_CONSOLE;_UNICODE;UNICODE;%(PreprocessorDefinitions) + USING_WIN_UI;GLEW_STATIC;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_ARCH_64=1;_CONSOLE;_UNICODE;UNICODE;%(PreprocessorDefinitions) ../ffmpeg/Windows/x86_64/include;../dx9sdk/Include/DX11;../Common;..;../Core;../ext/glew false NotSet @@ -376,7 +376,7 @@ NotUsing true true - USING_WIN_UI;GLEW_STATIC;_CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_ARCH_64=1;_CONSOLE;_UNICODE;UNICODE;%(PreprocessorDefinitions) + USING_WIN_UI;GLEW_STATIC;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_ARCH_64=1;_CONSOLE;_UNICODE;UNICODE;%(PreprocessorDefinitions) ../ffmpeg/Windows/aarch64/include;../dx9sdk/Include/DX11;../Common;..;../Core;../ext/glew false Precise @@ -410,7 +410,7 @@ NotUsing true true - USING_WIN_UI;GLEW_STATIC;_CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_ARCH_32=1;_CONSOLE;_UNICODE;UNICODE;%(PreprocessorDefinitions) + USING_WIN_UI;GLEW_STATIC;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_ARCH_32=1;_CONSOLE;_UNICODE;UNICODE;%(PreprocessorDefinitions) ../ffmpeg/Windows/arm/include;../dx9sdk/Include/DX11;../Common;..;../Core;../ext/glew false Precise diff --git a/headless/WindowsHeadlessHost.cpp b/headless/WindowsHeadlessHost.cpp index 27b06ea7b7..98ea0a4462 100644 --- a/headless/WindowsHeadlessHost.cpp +++ b/headless/WindowsHeadlessHost.cpp @@ -15,6 +15,7 @@ // Official git repository and contact information can be found at // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. +#include "ppsspp_config.h" #include #include "headless/WindowsHeadlessHost.h" diff --git a/ios/ViewController.mm b/ios/ViewController.mm index 59bd731ee4..a0d89b5959 100644 --- a/ios/ViewController.mm +++ b/ios/ViewController.mm @@ -96,6 +96,7 @@ static double lastStartPress = 0.0f; static bool simulateAnalog = false; static bool threadEnabled = true; static bool threadStopped = false; +static UITouch *g_touches[10]; __unsafe_unretained ViewController* sharedViewController; static GraphicsContext *graphicsContext; @@ -107,7 +108,7 @@ static LocationHelper *locationHelper; } @property (nonatomic, strong) EAGLContext* context; -@property (nonatomic, strong) NSMutableArray* touches; + //@property (nonatomic) iCadeReaderView* iCadeView; #if __IPHONE_OS_VERSION_MAX_ALLOWED > __IPHONE_6_1 @property (nonatomic) GCController *gameController __attribute__((weak_import)); @@ -127,7 +128,7 @@ static LocationHelper *locationHelper; self = [super init]; if (self) { sharedViewController = self; - self.touches = [NSMutableArray array]; + memset(g_touches, 0, sizeof(g_touches)); iCadeToKeyMap[iCadeJoystickUp] = NKCODE_DPAD_UP; iCadeToKeyMap[iCadeJoystickRight] = NKCODE_DPAD_RIGHT; @@ -384,37 +385,38 @@ static LocationHelper *locationHelper; NativeTouch(input); } -- (NSDictionary*)touchDictBy:(UITouch*)touch -{ - for (NSDictionary* dict in self.touches) { - if ([dict objectForKey:@"touch"] == touch) - return dict; - } - return nil; -} - -- (int)freeTouchIndex -{ - int index = 0; - - for (NSDictionary* dict in self.touches) - { - int i = [[dict objectForKey:@"index"] intValue]; - if (index == i) - index = i+1; +int ToTouchID(UITouch *uiTouch, bool allowAllocate) { + // Find the id for the touch. Avoid 0 (mouse.) + for (int localId = 1; localId < (int)ARRAY_SIZE(g_touches); ++localId) { + if (g_touches[localId] == uiTouch) { + return localId; + } } - return index; + // Allocate a new one, perhaps? + if (allowAllocate) { + for (int localId = 1; localId < (int)ARRAY_SIZE(g_touches); ++localId) { + if (g_touches[localId] == 0) { + g_touches[localId] = uiTouch; + return localId; + } + } + + // None were free. Ignore? + return 0; + } + + return -1; } + - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { for(UITouch* touch in touches) { - NSDictionary* dict = @{@"touch":touch,@"index":@([self freeTouchIndex])}; - [self.touches addObject:dict]; CGPoint point = [touch locationInView:self.view]; - [self touchX:point.x y:point.y code:1 pointerId:[[dict objectForKey:@"index"] intValue]]; + int touchId = ToTouchID(touch, true); + [self touchX:point.x y:point.y code:1 pointerId:touchId]; } } @@ -423,8 +425,8 @@ static LocationHelper *locationHelper; for(UITouch* touch in touches) { CGPoint point = [touch locationInView:self.view]; - NSDictionary* dict = [self touchDictBy:touch]; - [self touchX:point.x y:point.y code:0 pointerId:[[dict objectForKey:@"index"] intValue]]; + int touchId = ToTouchID(touch, true); + [self touchX:point.x y:point.y code:0 pointerId: touchId]; } } @@ -433,9 +435,11 @@ static LocationHelper *locationHelper; for(UITouch* touch in touches) { CGPoint point = [touch locationInView:self.view]; - NSDictionary* dict = [self touchDictBy:touch]; - [self touchX:point.x y:point.y code:2 pointerId:[[dict objectForKey:@"index"] intValue]]; - [self.touches removeObject:dict]; + int touchId = ToTouchID(touch, false); + if (touchId >= 0) { + [self touchX:point.x y:point.y code:2 pointerId: touchId]; + g_touches[touchId] = nullptr; + } } } @@ -444,9 +448,11 @@ static LocationHelper *locationHelper; for(UITouch* touch in touches) { CGPoint point = [touch locationInView:self.view]; - NSDictionary* dict = [self touchDictBy:touch]; - [self touchX:point.x y:point.y code:2 pointerId:[[dict objectForKey:@"index"] intValue]]; - [self.touches removeObject:dict]; + int touchId = ToTouchID(touch, false); + if (touchId >= 0) { + [self touchX:point.x y:point.y code:2 pointerId: touchId]; + g_touches[touchId] = nullptr; + } } } diff --git a/ios/main.mm b/ios/main.mm index 4b3e768d5c..e53b7c7abf 100644 --- a/ios/main.mm +++ b/ios/main.mm @@ -14,22 +14,61 @@ #import "PPSSPPUIApplication.h" #import "ViewController.h" +#include "Common/MemoryUtil.h" #include "Common/System/NativeApp.h" #include "Common/System/System.h" +#include "Common/StringUtils.h" #include "Common/Profiler/Profiler.h" -#define CS_OPS_STATUS 0 /* return status */ -#define CS_DEBUGGED 0x10000000 /* process is currently or has previously been debugged and allowed to run with invalid pages */ -#define PT_TRACE_ME 0 /* child declares it's being traced */ -#define PT_SIGEXC 12 /* signals as exceptions for current_proc */ - static int (*csops)(pid_t pid, unsigned int ops, void * useraddr, size_t usersize); static boolean_t (*exc_server)(mach_msg_header_t *, mach_msg_header_t *); static int (*ptrace)(int request, pid_t pid, caddr_t addr, int data); -bool cs_debugged() { +#define CS_OPS_STATUS 0 /* return status */ +#define CS_DEBUGGED 0x10000000 /* process is currently or has previously been debugged and allowed to run with invalid pages */ +#define PT_ATTACHEXC 14 /* attach to running process with signal exception */ +#define PT_DETACH 11 /* stop tracing a process */ +#define ptrace(a, b, c, d) syscall(SYS_ptrace, a, b, c, d) + +bool get_debugged() { int flags; - return !csops(getpid(), CS_OPS_STATUS, &flags, sizeof(flags)) && flags & CS_DEBUGGED; + int rv = csops(getpid(), CS_OPS_STATUS, &flags, sizeof(flags)); + if (rv==0 && flags&CS_DEBUGGED) return true; + + pid_t pid = fork(); + if (pid > 0) { + int st,rv,i=0; + do { + usleep(500); + rv = waitpid(pid, &st, 0); + } while (rv<0 && i++<10); + if (rv<0) fprintf(stderr, "Unable to wait for child?\n"); + } else if (pid == 0) { + pid_t ppid = getppid(); + int rv = ptrace(PT_ATTACHEXC, ppid, 0, 0); + if (rv) { + perror("Unable to attach to process"); + exit(1); + } + for (int i=0; i<100; i++) { + usleep(1000); + errno = 0; + rv = ptrace(PT_DETACH, ppid, 0, 0); + if (rv==0) break; + } + if (rv) { + perror("Unable to detach from process"); + exit(1); + } + exit(0); + } else { + perror("Unable to fork"); + } + + rv = csops(getpid(), CS_OPS_STATUS, &flags, sizeof(flags)); + if (rv==0 && flags&CS_DEBUGGED) return true; + + return false; } kern_return_t catch_exception_raise(mach_port_t exception_port, @@ -52,11 +91,14 @@ static float g_safeInsetRight = 0.0; static float g_safeInsetTop = 0.0; static float g_safeInsetBottom = 0.0; +static bool g_jitAvailable = false; +static int g_iosVersionMajor; +static int g_iosVersionMinor; std::string System_GetProperty(SystemProperty prop) { switch (prop) { case SYSPROP_NAME: - return "iOS:"; + return StringFromFormat("iOS %d.%d", g_iosVersionMajor, g_iosVersionMinor); case SYSPROP_LANGREGION: return "en_US"; default: @@ -78,6 +120,8 @@ int System_GetPropertyInt(SystemProperty prop) { return 44100; case SYSPROP_DEVICE_TYPE: return DEVICE_TYPE_MOBILE; + case SYSPROP_SYSTEMVERSION: + return g_iosVersionMajor; default: return -1; } @@ -110,6 +154,9 @@ bool System_GetPropertyBool(SystemProperty prop) { #else return false; #endif + case SYSPROP_CAN_JIT: + return g_jitAvailable; + default: return false; } @@ -157,11 +204,9 @@ PermissionStatus System_GetPermissionStatus(SystemPermission permission) { retur FOUNDATION_EXTERN void AudioServicesPlaySystemSoundWithVibration(unsigned long, objc_object*, NSDictionary*); -BOOL SupportsTaptic() -{ +BOOL SupportsTaptic() { // we're on an iOS version that cannot instantiate UISelectionFeedbackGenerator, so no. - if(!NSClassFromString(@"UISelectionFeedbackGenerator")) - { + if(!NSClassFromString(@"UISelectionFeedbackGenerator")) { return NO; } @@ -175,9 +220,7 @@ BOOL SupportsTaptic() } void Vibrate(int mode) { - - if(SupportsTaptic()) - { + if (SupportsTaptic()) { PPSSPPUIApplication* app = (PPSSPPUIApplication*)[UIApplication sharedApplication]; if(app.feedbackGenerator == nil) { @@ -185,9 +228,7 @@ void Vibrate(int mode) { [app.feedbackGenerator prepare]; } [app.feedbackGenerator selectionChanged]; - } - else - { + } else { NSMutableDictionary *dictionary = [NSMutableDictionary dictionary]; NSArray *pattern = @[@YES, @30, @NO, @2]; @@ -200,30 +241,33 @@ void Vibrate(int mode) { int main(int argc, char *argv[]) { + // Hacky hacks to try to enable JIT by pretending to be a debugger. csops = reinterpret_cast(dlsym(dlopen(nullptr, RTLD_LAZY), "csops")); exc_server = reinterpret_cast(dlsym(dlopen(NULL, RTLD_LAZY), "exc_server")); ptrace = reinterpret_cast(dlsym(dlopen(NULL, RTLD_LAZY), "ptrace")); // see https://github.com/hrydgard/ppsspp/issues/11905 - if (!cs_debugged()) { - pid_t pid = fork(); - if (pid == 0) { - ptrace(PT_TRACE_ME, 0, nullptr, 0); - exit(0); - } else if (pid < 0) { - perror("Unable to fork"); - ptrace(PT_TRACE_ME, 0, nullptr, 0); - ptrace(PT_SIGEXC, 0, nullptr, 0); - - mach_port_t port = MACH_PORT_NULL; - mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &port); - mach_port_insert_right(mach_task_self(), port, port, MACH_MSG_TYPE_MAKE_SEND); - task_set_exception_ports(mach_task_self(), EXC_MASK_SOFTWARE, port, EXCEPTION_DEFAULT, THREAD_STATE_NONE); - pthread_t thread; - pthread_create(&thread, nullptr, exception_handler, reinterpret_cast(&port)); - } + // Tried checking for JIT support here with AllocateExecutableMemory and ProtectMemoryPages, + // but it just succeeds, and then fails when you try to execute from it. + + // So, we'll just resort to a version check. + + std::string version = [[UIDevice currentDevice].systemVersion UTF8String]; + if (2 != sscanf(version.c_str(), "%d.%d", &g_iosVersionMajor, &g_iosVersionMinor)) { + // Just set it to 14.0 if the parsing fails for whatever reason. + g_iosVersionMajor = 14; + g_iosVersionMinor = 0; } + g_jitAvailable = get_debugged(); +/* + if (g_iosVersionMajor > 14 || (g_iosVersionMajor == 14 && g_iosVersionMinor >= 4)) { + g_jitAvailable = false; + } else { + g_jitAvailable = true; + } +*/ + PROFILE_INIT(); @autoreleasepool { diff --git a/libretro/LibretroGLCoreContext.cpp b/libretro/LibretroGLCoreContext.cpp index 7b571bdaf0..0b5a57b91b 100644 --- a/libretro/LibretroGLCoreContext.cpp +++ b/libretro/LibretroGLCoreContext.cpp @@ -1,4 +1,4 @@ - +#include "ppsspp_config.h" #include "Common/Log.h" #include "Core/Config.h" #include "Core/ConfigValues.h" @@ -17,7 +17,7 @@ bool LibretroGLCoreContext::Init() { void LibretroGLCoreContext::CreateDrawContext() { if (!glewInitDone) { -#if !defined(IOS) && !defined(USING_GLES2) +#if !PPSSPP_PLATFORM(IOS) && !defined(USING_GLES2) if (glewInit() != GLEW_OK) { ERROR_LOG(G3D, "glewInit() failed.\n"); return; diff --git a/libretro/Makefile b/libretro/Makefile index a047d9a2e8..a79d4f71ea 100644 --- a/libretro/Makefile +++ b/libretro/Makefile @@ -105,7 +105,6 @@ else ifneq (,$(findstring arm64,$(platform))) GL_LIB := -lGL endif CPUFLAGS += -D__arm64__ -DARM64_ASM -D__NEON_OPT - PLATCFLAGS += -DARM64 LDFLAGS += -lrt -ldl PLATFORM_EXT := unix @@ -147,7 +146,6 @@ else ifneq (,$(findstring osx,$(platform))) # iOS else ifneq (,$(findstring ios,$(platform))) TARGET := $(TARGET_NAME)_libretro_ios.dylib - PLATCFLAGS += -DIOS LDFLAGS += -dynamiclib -marm fpic = -fPIC GLES = 1 @@ -171,7 +169,7 @@ else ifneq (,$(findstring ios,$(platform))) CXX += -miphoneos-version-min=5.0 PLATCFLAGS += -miphoneos-version-min=5.0 endif - PLATCFLAGS += -DIOS -DHAVE_POSIX_MEMALIGN + PLATCFLAGS += -DHAVE_POSIX_MEMALIGN CPUFLAGS += -DARMv5_ONLY -DARM PLATFORM_EXT := unix TARGET_ARCH = arm @@ -253,7 +251,6 @@ else ifneq (,$(findstring armv,$(platform))) CPUFLAGS += -mfloat-abi=hard endif CPUFLAGS += -D__arm__ -DARM_ASM - PLATCFLAGS += -DARM LDFLAGS += -lrt -ldl # emscripten @@ -304,7 +301,7 @@ else ifneq (,$(findstring windows_msvc2017,$(platform))) filter_out1 = $(filter-out $(firstword $1),$1) filter_out2 = $(call filter_out1,$(call filter_out1,$1)) - reg_query = $(call filter_out2,$(subst $2,,$(shell reg query "$2" -v "$1" 2>nul))) + reg_query = $(call filter_out2,$(subst $2,,$(shell reg query "$2" -v "$1" 2>/dev/null))) fix_path = $(subst $(SPACE),\ ,$(subst \,/,$1)) b1 := ( @@ -415,7 +412,7 @@ else ifneq (,$(findstring windows_msvc2019,$(platform))) filter_out1 = $(filter-out $(firstword $1),$1) filter_out2 = $(call filter_out1,$(call filter_out1,$1)) - reg_query = $(call filter_out2,$(subst $2,,$(shell reg query "$2" -v "$1" 2>nul))) + reg_query = $(call filter_out2,$(subst $2,,$(shell reg query "$2" -v "$1" 2>/dev/null))) fix_path = $(subst $(SPACE),\ ,$(subst \,/,$1)) b1 := ( diff --git a/libretro/Makefile.common b/libretro/Makefile.common index 2c24b22b84..bb185a17a1 100644 --- a/libretro/Makefile.common +++ b/libretro/Makefile.common @@ -362,6 +362,7 @@ SOURCES_CXX += \ $(COREDIR)/Instance.cpp \ $(COREDIR)/Debugger/Breakpoints.cpp \ $(COREDIR)/Debugger/SymbolMap.cpp \ + $(COREDIR)/Debugger/MemBlockInfo.cpp \ $(COREDIR)/Dialog/PSPDialog.cpp \ $(COREDIR)/Dialog/PSPGamedataInstallDialog.cpp \ $(COREDIR)/Dialog/PSPMsgDialog.cpp \ diff --git a/libretro/README_WINDOWS.txt b/libretro/README_WINDOWS.txt index 755658dca4..5bdc37e135 100644 --- a/libretro/README_WINDOWS.txt +++ b/libretro/README_WINDOWS.txt @@ -13,7 +13,7 @@ pacman -S make Then use the following in msys: cd libretro -make platform=windows_msvc2019_desktop_x64 -j32 && cp ppsspp_libretro.* /d/retroarch/cores && rm nul +make platform=windows_msvc2019_desktop_x64 -j32 && cp ppsspp_libretro.* /d/retroarch/cores Note that the latter part copies the DLL/PDB into wherever retroarch reads it from. Might need to adjust the path, and adjust -j32 depending on your number of logical CPUs - might not need that many threads (or you might need more...). diff --git a/libretro/jni/Android.mk b/libretro/jni/Android.mk index f5703ba4c4..96f0b93b40 100644 --- a/libretro/jni/Android.mk +++ b/libretro/jni/Android.mk @@ -12,14 +12,14 @@ FFMPEGLIBS += libavformat libavcodec libavutil libswresample libswscale WITH_DYNAREC := 1 ifeq ($(TARGET_ARCH),arm64) - COREFLAGS += -DARM64 -D_ARCH_64 + COREFLAGS += -D_ARCH_64 HAVE_NEON := 1 FFMPEGLIBDIR := $(FFMPEGDIR)/android/arm64/lib FFMPEGINCFLAGS := -I$(FFMPEGDIR)/android/arm64/include endif ifeq ($(TARGET_ARCH),arm) - COREFLAGS += -DARM -DARMEABI_V7A -D__arm__ -DARM_ASM -D_ARCH_32 -mfpu=neon + COREFLAGS += -D__arm__ -DARM_ASM -D_ARCH_32 -mfpu=neon HAVE_NEON := 1 FFMPEGLIBDIR := $(FFMPEGDIR)/android/armv7/lib FFMPEGINCFLAGS := -I$(FFMPEGDIR)/android/armv7/include diff --git a/libretro/libretro.cpp b/libretro/libretro.cpp index 122cab72e2..9751924cb9 100644 --- a/libretro/libretro.cpp +++ b/libretro/libretro.cpp @@ -1,3 +1,4 @@ +#include "ppsspp_config.h" #include #include #include @@ -214,9 +215,9 @@ static RetroOption ppsspp_texture_deposterize("ppsspp_texture_deposterize" static RetroOption ppsspp_texture_replacement("ppsspp_texture_replacement", "Texture Replacement", false); static RetroOption ppsspp_gpu_hardware_transform("ppsspp_gpu_hardware_transform", "GPU Hardware T&L", true); static RetroOption ppsspp_vertex_cache("ppsspp_vertex_cache", "Vertex Cache (Speedhack)", true); -static RetroOption ppsspp_unsafe_func_replacements("ppsspp_unsafe_func_replacements", "Unsafe FuncReplacements", true); static RetroOption ppsspp_cheats("ppsspp_cheats", "Internal Cheats Support", false); static RetroOption ppsspp_io_timing_method("ppsspp_io_timing_method", "IO Timing Method", { { "Fast", IOTimingMethods::IOTIMING_FAST }, { "Host", IOTimingMethods::IOTIMING_HOST }, { "Simulate UMD delays", IOTimingMethods::IOTIMING_REALISTIC } }); +static RetroOption ppsspp_frame_duplication("ppsspp_frame_duplication", "Duplicate frames in 30hz games", false); void retro_set_environment(retro_environment_t cb) { @@ -240,10 +241,10 @@ void retro_set_environment(retro_environment_t cb) vars.push_back(ppsspp_texture_replacement.GetOptions()); vars.push_back(ppsspp_gpu_hardware_transform.GetOptions()); vars.push_back(ppsspp_vertex_cache.GetOptions()); - vars.push_back(ppsspp_unsafe_func_replacements.GetOptions()); vars.push_back(ppsspp_cheats.GetOptions()); vars.push_back(ppsspp_io_timing_method.GetOptions()); vars.push_back(ppsspp_lower_resolution_for_effects.GetOptions()); + vars.push_back(ppsspp_frame_duplication.GetOptions()); vars.push_back({}); environ_cb = cb; @@ -308,13 +309,13 @@ static void check_variables(CoreParameter &coreParam) ppsspp_texture_anisotropic_filtering.Update(&g_Config.iAnisotropyLevel); ppsspp_texture_deposterize.Update(&g_Config.bTexDeposterize); ppsspp_texture_replacement.Update(&g_Config.bReplaceTextures); - ppsspp_unsafe_func_replacements.Update(&g_Config.bFuncReplacements); ppsspp_cheats.Update(&g_Config.bEnableCheats); ppsspp_locked_cpu_speed.Update(&g_Config.iLockedCPUSpeed); ppsspp_rendering_mode.Update(&g_Config.iRenderingMode); ppsspp_cpu_core.Update((CPUCore *)&g_Config.iCpuCore); ppsspp_io_timing_method.Update((IOTimingMethods *)&g_Config.iIOTimingMethod); ppsspp_lower_resolution_for_effects.Update(&g_Config.iBloomHack); + ppsspp_frame_duplication.Update(&g_Config.bRenderDuplicateFrames); ppsspp_language.Update(&g_Config.iLanguage); if (g_Config.iLanguage < 0) @@ -354,6 +355,7 @@ void retro_init(void) #endif g_Config.bEnableLogging = true; + // libretro does its own timing, so this should stay CONTINUOUS. g_Config.iUnthrottleMode = (int)UnthrottleMode::CONTINUOUS; g_Config.bMemStickInserted = true; g_Config.iGlobalVolume = VOLUME_MAX - 1; @@ -361,6 +363,7 @@ void retro_init(void) g_Config.bEnableSound = true; g_Config.iCwCheatRefreshRate = 60; g_Config.iMemStickSizeGB = 16; + g_Config.bFuncReplacements = true; g_Config.iFirmwareVersion = PSP_DEFAULT_FIRMWARE; g_Config.iPSPModel = PSP_MODEL_SLIM; diff --git a/ppsspp_config.h b/ppsspp_config.h index e429e629cb..5725778144 100644 --- a/ppsspp_config.h +++ b/ppsspp_config.h @@ -48,10 +48,6 @@ #define PPSSPP_ARCH_ARMV7 1 #define PPSSPP_ARCH_ARM_NEON 1 #endif - //TODO: Remove this compat define - #ifndef ARM - #define ARM 1 - #endif #endif #if defined(__aarch64__) || defined(_M_ARM64) @@ -66,10 +62,6 @@ #elif defined(__mips__) #define PPSSPP_ARCH_MIPS 1 #define PPSSPP_ARCH_32BIT 1 - //TODO: Remove this compat define - #ifndef MIPS - #define MIPS 1 - #endif #endif @@ -91,16 +83,8 @@ #if TARGET_IPHONE_SIMULATOR #define PPSSPP_PLATFORM_IOS 1 #define PPSSPP_PLATFORM_IOS_SIMULATOR 1 - //TODO: Remove this compat define - #ifndef IOS - #define IOS 1 - #endif #elif TARGET_OS_IPHONE #define PPSSPP_PLATFORM_IOS 1 - //TODO: Remove this compat define - #ifndef IOS - #define IOS 1 - #endif #elif TARGET_OS_MAC #define PPSSPP_PLATFORM_MAC 1 #else diff --git a/pspautotests b/pspautotests index 7e4a1e7509..1047400eae 160000 --- a/pspautotests +++ b/pspautotests @@ -1 +1 @@ -Subproject commit 7e4a1e750997e95df8a1f04f9a6396801adf7113 +Subproject commit 1047400eaec6bcbdb2a64d326375ef6a6617c4ac diff --git a/test.py b/test.py index fddbda057c..b4595ede9d 100755 --- a/test.py +++ b/test.py @@ -14,6 +14,8 @@ PPSSPP_EXECUTABLES = [ "Windows\\Release\\PPSSPPHeadless.exe", "Windows\\x64\\Debug\\PPSSPPHeadless.exe", "Windows\\x64\\Release\\PPSSPPHeadless.exe", + "build*/PPSSPPHeadless.exe", + "./PPSSPPHeadless.exe", # Mac "build*/Debug/PPSSPPHeadless", "build*/Release/PPSSPPHeadless", diff --git a/unittest/TestArmEmitter.cpp b/unittest/TestArmEmitter.cpp index aa9db3f72a..94acdbdb1f 100644 --- a/unittest/TestArmEmitter.cpp +++ b/unittest/TestArmEmitter.cpp @@ -229,10 +229,10 @@ bool TestArmEmitter() { const u8 *codeStart = emitter.GetCodePointer(); - MIPSState mips; + MIPSState mipsState; MIPSComp::JitState js; MIPSComp::JitOptions jo; - ArmRegCacheFPU fpr(&mips, &js, &jo); + ArmRegCacheFPU fpr(&mipsState, &js, &jo); fpr.SetEmitter(&emitter); int C000 = GetColumnName(0, M_4x4, 0, 0); int C010 = GetColumnName(0, M_4x4, 1, 0); diff --git a/unittest/TestVertexJit.cpp b/unittest/TestVertexJit.cpp index 965fcee639..f1ea248b21 100644 --- a/unittest/TestVertexJit.cpp +++ b/unittest/TestVertexJit.cpp @@ -15,6 +15,8 @@ // Official git repository and contact information can be found at // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. +#include + #include "Common/Common.h" #include "Common/TimeUtil.h" #include "Core/Config.h" diff --git a/unittest/UnitTest.cpp b/unittest/UnitTest.cpp index 6547be7ea6..2733287e55 100644 --- a/unittest/UnitTest.cpp +++ b/unittest/UnitTest.cpp @@ -69,6 +69,10 @@ float System_GetPropertyFloat(SystemProperty prop) { return -1; } bool System_GetPropertyBool(SystemProperty prop) { + switch (prop) { + case SYSPROP_CAN_JIT: + return true; + } return false; } @@ -298,6 +302,8 @@ bool TestParsers() { bool TestVFPUSinCos() { float sine, cosine; + InitVFPUSinCos(false); + EXPECT_FALSE(vfpu_sincos == nullptr); vfpu_sincos(0.0f, sine, cosine); EXPECT_EQ_FLOAT(sine, 0.0f); EXPECT_EQ_FLOAT(cosine, 1.0f); diff --git a/unittest/UnitTests.vcxproj b/unittest/UnitTests.vcxproj index f585ee5feb..5a63519960 100644 --- a/unittest/UnitTests.vcxproj +++ b/unittest/UnitTests.vcxproj @@ -168,7 +168,7 @@ Level3 - _CRTDBG_MAP_ALLOC;USING_WIN_UI;USING_WIN_UI;GLEW_STATIC;_CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_ARCH_32=1;_WINDOWS;_UNICODE;UNICODE;%(PreprocessorDefinitions) + _CRTDBG_MAP_ALLOC;USING_WIN_UI;USING_WIN_UI;GLEW_STATIC;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_ARCH_32=1;_WINDOWS;_UNICODE;UNICODE;%(PreprocessorDefinitions) ../ffmpeg/Windows/x86/include;../ext;../common;..;../ext/glew;../ext/zlib true false @@ -190,7 +190,7 @@ Level3 - _CRTDBG_MAP_ALLOC;USING_WIN_UI;GLEW_STATIC;_CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_ARCH_64=1;_WINDOWS;_UNICODE;UNICODE;%(PreprocessorDefinitions) + _CRTDBG_MAP_ALLOC;USING_WIN_UI;GLEW_STATIC;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_ARCH_64=1;_WINDOWS;_UNICODE;UNICODE;%(PreprocessorDefinitions) ../ffmpeg/Windows/x86_64/include;../ext;../common;..;../ext/glew;../ext/zlib true false @@ -213,7 +213,7 @@ Level3 - _CRTDBG_MAP_ALLOC;USING_WIN_UI;GLEW_STATIC;_CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_ARCH_64=1;_WINDOWS;_UNICODE;UNICODE;%(PreprocessorDefinitions) + _CRTDBG_MAP_ALLOC;USING_WIN_UI;GLEW_STATIC;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_ARCH_64=1;_WINDOWS;_UNICODE;UNICODE;%(PreprocessorDefinitions) ../ffmpeg/Windows/aarch64/include;../ext;../common;..;../ext/glew;../ext/zlib true false @@ -236,7 +236,7 @@ Level3 - _CRTDBG_MAP_ALLOC;USING_WIN_UI;GLEW_STATIC;_CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_ARCH_32=1;_WINDOWS;_UNICODE;UNICODE;%(PreprocessorDefinitions) + _CRTDBG_MAP_ALLOC;USING_WIN_UI;GLEW_STATIC;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_ARCH_32=1;_WINDOWS;_UNICODE;UNICODE;%(PreprocessorDefinitions) ../ffmpeg/Windows/arm/include;../ext;../common;..;../ext/glew;../ext/zlib true false @@ -263,7 +263,7 @@ MaxSpeed true true - USING_WIN_UI;GLEW_STATIC;_CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_ARCH_32=1;_WINDOWS;_UNICODE;UNICODE;%(PreprocessorDefinitions) + USING_WIN_UI;GLEW_STATIC;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_ARCH_32=1;_WINDOWS;_UNICODE;UNICODE;%(PreprocessorDefinitions) ../ffmpeg/Windows/x86/include;../ext;../common;..;../ext/glew;../ext/zlib false Size @@ -290,7 +290,7 @@ MaxSpeed true true - USING_WIN_UI;GLEW_STATIC;_CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_ARCH_64=1;_WINDOWS;_UNICODE;UNICODE;%(PreprocessorDefinitions) + USING_WIN_UI;GLEW_STATIC;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_ARCH_64=1;_WINDOWS;_UNICODE;UNICODE;%(PreprocessorDefinitions) ../ffmpeg/Windows/x86_64/include;../ext;../common;..;../ext/glew;../ext/zlib false true @@ -319,7 +319,7 @@ MaxSpeed true true - USING_WIN_UI;GLEW_STATIC;_CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_ARCH_64=1;_WINDOWS;_UNICODE;UNICODE;%(PreprocessorDefinitions) + USING_WIN_UI;GLEW_STATIC;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_ARCH_64=1;_WINDOWS;_UNICODE;UNICODE;%(PreprocessorDefinitions) ../ffmpeg/Windows/aarch64/include;../ext;../common;..;../ext/glew;../ext/zlib false true @@ -348,7 +348,7 @@ MaxSpeed true true - USING_WIN_UI;GLEW_STATIC;_CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_ARCH_32=1;_WINDOWS;_UNICODE;UNICODE;%(PreprocessorDefinitions) + USING_WIN_UI;GLEW_STATIC;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_ARCH_32=1;_WINDOWS;_UNICODE;UNICODE;%(PreprocessorDefinitions) ../ffmpeg/Windows/arm/include;../ext;../common;..;../ext/glew;../ext/zlib false true