diff --git a/CMakeLists.txt b/CMakeLists.txt
index 20a0b97f95..4256bd2ab0 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 8f734cdd02..a53e904637 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 643e621821..74ade8dece 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 4e26d2c079..6a350b693c 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 596a9421b5..1b49e9273d 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 0e0999c0f9..5fe9411f67 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 6d13b6f1f2..0f01636405 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 0000000000..c300f84367
--- /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 0000000000..9f530a7389
--- /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 7e157036d9..3b8737f8c4 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 cb584faa47..14bb73aca9 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 36d26ae90c..77f9a0d604 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 78b4492835..0cd5ec1b48 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 0000000000..b2b0663271
--- /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 8ca3b9ce11..17e7cac416 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 abstract class NativeActivity extends Activity {
}
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 abstract class NativeActivity extends Activity {
} 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 abstract class NativeActivity extends Activity {
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 90c21ce9eb..169ac3c816 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 \