From cad232e60e367889453440f53fb73928e7a6ea8e Mon Sep 17 00:00:00 2001 From: Florin9doi Date: Fri, 12 Jan 2024 17:34:50 +0200 Subject: [PATCH] sceSircs/Infrared support on Android --- CMakeLists.txt | 2 + Common/System/Request.h | 4 + Common/System/System.h | 1 + Core/Core.vcxproj | 2 + Core/Core.vcxproj.filters | 6 ++ Core/HLE/HLETables.cpp | 2 + Core/HLE/sceHttp.cpp | 5 -- Core/HLE/sceSircs.cpp | 59 +++++++++++++++ Core/HLE/sceSircs.h | 28 +++++++ UWP/CoreUWP/CoreUWP.vcxproj | 2 + android/AndroidManifest.xml | 1 + android/jni/Android.mk | 1 + android/jni/app-android.cpp | 3 + .../src/org/ppsspp/ppsspp/InfraredHelper.java | 75 +++++++++++++++++++ .../src/org/ppsspp/ppsspp/NativeActivity.java | 29 ++++++- libretro/Makefile.common | 1 + 16 files changed, 215 insertions(+), 6 deletions(-) create mode 100644 Core/HLE/sceSircs.cpp create mode 100644 Core/HLE/sceSircs.h create mode 100644 android/src/org/ppsspp/ppsspp/InfraredHelper.java diff --git a/CMakeLists.txt b/CMakeLists.txt index 20a0b97f950f..4256bd2ab0bf 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2160,6 +2160,8 @@ add_library(${CoreLibName} ${CoreLinkType} Core/HLE/sceSfmt19937.h Core/HLE/sceSha256.cpp Core/HLE/sceSha256.h + Core/HLE/sceSircs.cpp + Core/HLE/sceSircs.h Core/HLE/sceSsl.cpp Core/HLE/sceSsl.h Core/HLE/sceUmd.cpp diff --git a/Common/System/Request.h b/Common/System/Request.h index 8f734cdd02ad..a53e90463731 100644 --- a/Common/System/Request.h +++ b/Common/System/Request.h @@ -140,6 +140,10 @@ inline void System_GPSCommand(const std::string &command) { g_requestManager.MakeSystemRequest(SystemRequestType::GPS_COMMAND, nullptr, nullptr, command, "", 0); } +inline void System_InfraredCommand(const std::string &command) { + g_requestManager.MakeSystemRequest(SystemRequestType::INFRARED_COMMAND, nullptr, nullptr, command, "", 0); +} + inline void System_MicrophoneCommand(const std::string &command) { g_requestManager.MakeSystemRequest(SystemRequestType::MICROPHONE_COMMAND, nullptr, nullptr, command, "", 0); } diff --git a/Common/System/System.h b/Common/System/System.h index 643e6218213f..74ade8dece2c 100644 --- a/Common/System/System.h +++ b/Common/System/System.h @@ -83,6 +83,7 @@ enum class SystemRequestType { // High-level hardware control CAMERA_COMMAND, GPS_COMMAND, + INFRARED_COMMAND, MICROPHONE_COMMAND, }; diff --git a/Core/Core.vcxproj b/Core/Core.vcxproj index 4e26d2c0790d..6a350b693c10 100644 --- a/Core/Core.vcxproj +++ b/Core/Core.vcxproj @@ -736,6 +736,7 @@ + @@ -1301,6 +1302,7 @@ + diff --git a/Core/Core.vcxproj.filters b/Core/Core.vcxproj.filters index 596a9421b518..1b49e9273d66 100644 --- a/Core/Core.vcxproj.filters +++ b/Core/Core.vcxproj.filters @@ -580,6 +580,9 @@ HLE\Libraries + + HLE\Libraries + Util @@ -1773,6 +1776,9 @@ HLE\Libraries + + HLE\Libraries + Util diff --git a/Core/HLE/HLETables.cpp b/Core/HLE/HLETables.cpp index 0e0999c0f91c..5fe9411f6780 100644 --- a/Core/HLE/HLETables.cpp +++ b/Core/HLE/HLETables.cpp @@ -64,6 +64,7 @@ #include "scePsmf.h" #include "sceRtc.h" #include "sceSas.h" +#include "sceSircs.h" #include "sceSsl.h" #include "sceUmd.h" #include "sceUsb.h" @@ -307,6 +308,7 @@ void RegisterAllModules() { Register_sceDdrdb(); Register_mp4msv(); Register_InterruptManagerForKernel(); + Register_sceSircs(); // add new modules here. } diff --git a/Core/HLE/sceHttp.cpp b/Core/HLE/sceHttp.cpp index 6d13b6f1f2f9..0f0163640574 100644 --- a/Core/HLE/sceHttp.cpp +++ b/Core/HLE/sceHttp.cpp @@ -272,11 +272,6 @@ static int sceHttpGetContentLength(int requestID, u64 contentLengthPtr) { return 0; } -/* -* 0x62411801 sceSircsInit -0x19155a2f sceSircsEnd -0x71eef62d sceSircsSend - */ const HLEFunction sceHttp[] = { {0XAB1ABE07, &WrapI_I, "sceHttpInit", 'i', "i" }, {0XD1C8945E, &WrapI_V, "sceHttpEnd", 'i', "" }, diff --git a/Core/HLE/sceSircs.cpp b/Core/HLE/sceSircs.cpp new file mode 100644 index 000000000000..c300f8436710 --- /dev/null +++ b/Core/HLE/sceSircs.cpp @@ -0,0 +1,59 @@ +// Copyright (c) 2012- 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/. + +#ifdef __MINGW32__ +#include +#endif +#include + +#include "Common/System/System.h" +#include "Common/System/Request.h" +#include "Common/Serialize/Serializer.h" +#include "Common/Serialize/SerializeFuncs.h" +#include "Core/HLE/HLE.h" +#include "Core/HLE/sceSircs.h" +#include "Core/HLE/FunctionWrappers.h" +#include "Core/MemMapHelpers.h" + +int sceSircsSend(u32 dataAddr, int count) { + auto data = PSPPointer::Create(dataAddr); + if (data.IsValid()) { + INFO_LOG(HLE, "%s (version=0x%x, command=0x%x, address=0x%x, count=%d)", + __FUNCTION__, data->version, data->command, data->address, count); + #if PPSSPP_PLATFORM(ANDROID) + char command[40] = {0}; + snprintf(command, sizeof(command), "sircs_%d_%d_%d_%d", + data->version, data->command, data->address, count); + System_InfraredCommand(command); + #endif + data.NotifyRead("sceSircsSend"); + } + return 0; +} + +const HLEFunction sceSircs[] = +{ + {0X62411801, nullptr, "sceSircsInit", '?', "" }, + {0X19155A2F, nullptr, "sceSircsEnd", '?', "" }, + {0X71EEF62D, &WrapI_UI, "sceSircsSend", 'i', "xi" }, + {0x83381633, nullptr, "sceSircsReceive", '?', "" }, +}; + +void Register_sceSircs() +{ + RegisterModule("sceSircs", ARRAY_SIZE(sceSircs), sceSircs); +} diff --git a/Core/HLE/sceSircs.h b/Core/HLE/sceSircs.h new file mode 100644 index 000000000000..9f530a73897f --- /dev/null +++ b/Core/HLE/sceSircs.h @@ -0,0 +1,28 @@ +// Copyright (c) 2012- PPSSPP Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0 or later versions. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#pragma once + +#include "Core/HLE/FunctionWrappers.h" + +void Register_sceSircs(); + +typedef struct { + u8 version; + u8 command; + u16 address; +} SircsData; diff --git a/UWP/CoreUWP/CoreUWP.vcxproj b/UWP/CoreUWP/CoreUWP.vcxproj index 7e157036d928..3b8737f8c45b 100644 --- a/UWP/CoreUWP/CoreUWP.vcxproj +++ b/UWP/CoreUWP/CoreUWP.vcxproj @@ -240,6 +240,7 @@ + @@ -486,6 +487,7 @@ + diff --git a/android/AndroidManifest.xml b/android/AndroidManifest.xml index cb584faa475c..14bb73aca91c 100644 --- a/android/AndroidManifest.xml +++ b/android/AndroidManifest.xml @@ -27,6 +27,7 @@ + diff --git a/android/jni/Android.mk b/android/jni/Android.mk index 36d26ae90c97..77f9a0d60444 100644 --- a/android/jni/Android.mk +++ b/android/jni/Android.mk @@ -652,6 +652,7 @@ EXEC_AND_LIB_FILES := \ $(SRC)/Core/HLE/sceSas.cpp \ $(SRC)/Core/HLE/sceSfmt19937.cpp \ $(SRC)/Core/HLE/sceSha256.cpp \ + $(SRC)/Core/HLE/sceSircs.cpp \ $(SRC)/Core/HLE/sceSsl.cpp \ $(SRC)/Core/HLE/sceUmd.cpp \ $(SRC)/Core/HLE/sceUsb.cpp \ diff --git a/android/jni/app-android.cpp b/android/jni/app-android.cpp index 78b449283532..0cd5ec1b4802 100644 --- a/android/jni/app-android.cpp +++ b/android/jni/app-android.cpp @@ -1128,6 +1128,9 @@ bool System_MakeRequest(SystemRequestType type, int requestId, const std::string case SystemRequestType::GPS_COMMAND: PushCommand("gps_command", param1); return true; + case SystemRequestType::INFRARED_COMMAND: + PushCommand("infrared_command", param1); + return true; case SystemRequestType::MICROPHONE_COMMAND: PushCommand("microphone_command", param1); return true; diff --git a/android/src/org/ppsspp/ppsspp/InfraredHelper.java b/android/src/org/ppsspp/ppsspp/InfraredHelper.java new file mode 100644 index 000000000000..b2b0663271a8 --- /dev/null +++ b/android/src/org/ppsspp/ppsspp/InfraredHelper.java @@ -0,0 +1,75 @@ +package org.ppsspp.ppsspp; + +import android.content.Context; +import android.hardware.ConsumerIrManager; +import android.hardware.ConsumerIrManager.CarrierFrequencyRange; +import android.os.Build; +import android.util.Log; + +import androidx.annotation.RequiresApi; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +@RequiresApi(api = Build.VERSION_CODES.KITKAT) +class InfraredHelper { + private static final String TAG = InfraredHelper.class.getSimpleName(); + private static final int SIRC_FREQ = 40000; + private ConsumerIrManager mConsumerIrManager; + + InfraredHelper(Context context) throws Exception { + mConsumerIrManager = (ConsumerIrManager) context.getSystemService(Context.CONSUMER_IR_SERVICE); + Log.d(TAG, "HasIrEmitter: " + mConsumerIrManager.hasIrEmitter()); + if (!mConsumerIrManager.hasIrEmitter()) { + throw new Exception("No Ir Emitter"); + } + boolean sirc_freq_supported = false; + CarrierFrequencyRange[] carrierFrequencies = mConsumerIrManager.getCarrierFrequencies(); + for (CarrierFrequencyRange freq : carrierFrequencies) { + Log.d(TAG, "CarrierFrequencies: " + freq.getMinFrequency() + " -> " + freq.getMaxFrequency()); + if (freq.getMinFrequency() <= SIRC_FREQ && SIRC_FREQ <= freq.getMaxFrequency()) { + sirc_freq_supported = true; + } + } + if (!sirc_freq_supported) { + throw new Exception("Sirc Frequency unsupported"); + } + } + + void sendSircCommand(int version, int command, int address, int count) { + final List start = Arrays.asList(2400, 600); + final List one = Arrays.asList(1200, 600); + final List zero = Arrays.asList( 600, 600); + + List iterList = new ArrayList<>(); + iterList.addAll(start); + + for (int i = 0; i < version; i++) { + List val = i < 7 + ? ((command >> i ) & 1) == 1 ? one : zero + : ((address >> i - 7) & 1) == 1 ? one : zero; + iterList.addAll(val); + } + + int iterSum = 0; + for (int i = 0; i < iterList.size() - 1; i++) { + iterSum += iterList.get(i); + } + int lastVal = 52000 - iterSum; // SIRC cicle = 52ms + iterList.set(iterList.size() - 1, lastVal); + + List patternList = new ArrayList<>(); + // Android is limited to 2 seconds => max 38 loops of 52ms each + // Limit even further to 4 loops for now + for (int i = 0; i < count && i < 4; i++) { + patternList.addAll(iterList); + } + + int[] pattern = new int[patternList.size()]; + for (int i = 0; i < patternList.size(); i++) { + pattern[i] = patternList.get(i); + } + mConsumerIrManager.transmit(SIRC_FREQ, pattern); + } +} diff --git a/android/src/org/ppsspp/ppsspp/NativeActivity.java b/android/src/org/ppsspp/ppsspp/NativeActivity.java index 8ca3b9ce11db..17e7cac416a3 100644 --- a/android/src/org/ppsspp/ppsspp/NativeActivity.java +++ b/android/src/org/ppsspp/ppsspp/NativeActivity.java @@ -111,6 +111,7 @@ public abstract class NativeActivity extends Activity { private PowerSaveModeReceiver mPowerSaveModeReceiver = null; private SizeManager sizeManager = null; private static LocationHelper mLocationHelper; + private static InfraredHelper mInfraredHelper; private static CameraHelper mCameraHelper; private static final String[] permissionsForStorage = { @@ -472,6 +473,14 @@ public void Initialize() { } mLocationHelper = new LocationHelper(this); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + try { + mInfraredHelper = new InfraredHelper(this); + } catch (Exception e) { + mInfraredHelper = null; + Log.i(TAG, "InfraredHelper exception: " + e); + } + } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { // android.graphics.SurfaceTexture is not available before version 11. mCameraHelper = new CameraHelper(this); @@ -1560,7 +1569,25 @@ public boolean processCommand(String command, String params) { } else if (params.equals("close")) { mLocationHelper.stopLocationUpdates(); } + } else if (command.equals("infrared_command")) { + if (mInfraredHelper == null) { + return false; + } + if (params.startsWith("sircs")) { + Pattern pattern = Pattern.compile("sircs_(\\d+)_(\\d+)_(\\d+)_(\\d+)"); + Matcher matcher = pattern.matcher(params); + if (!matcher.matches()) + return false; + int ir_version = Integer.parseInt(matcher.group(1)); + int ir_command = Integer.parseInt(matcher.group(2)); + int ir_address = Integer.parseInt(matcher.group(3)); + int ir_count = Integer.parseInt(matcher.group(4)); + mInfraredHelper.sendSircCommand(ir_version, ir_command, ir_address, ir_count); + } } else if (command.equals("camera_command")) { + if (mCameraHelper == null) { + return false; + } if (params.startsWith("startVideo")) { Pattern pattern = Pattern.compile("startVideo_(\\d+)x(\\d+)"); Matcher matcher = pattern.matcher(params); @@ -1569,7 +1596,7 @@ public boolean processCommand(String command, String params) { int width = Integer.parseInt(matcher.group(1)); int height = Integer.parseInt(matcher.group(2)); mCameraHelper.setCameraSize(width, height); - if (mCameraHelper != null && !askForPermissions(permissionsForCamera, REQUEST_CODE_CAMERA_PERMISSION)) { + if (!askForPermissions(permissionsForCamera, REQUEST_CODE_CAMERA_PERMISSION)) { mCameraHelper.startCamera(); } } else if (mCameraHelper != null && params.equals("stopVideo")) { diff --git a/libretro/Makefile.common b/libretro/Makefile.common index 90c21ce9eb4c..169ac3c8169c 100644 --- a/libretro/Makefile.common +++ b/libretro/Makefile.common @@ -617,6 +617,7 @@ SOURCES_CXX += \ $(COREDIR)/HLE/KUBridge.cpp \ $(COREDIR)/HLE/Plugins.cpp \ $(COREDIR)/HLE/sceSha256.cpp \ + $(COREDIR)/HLE/sceSircs.cpp \ $(COREDIR)/HLE/sceG729.cpp \ $(COREDIR)/HLE/sceSfmt19937.cpp \ $(COREDIR)/HLE/ReplaceTables.cpp \