-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathTweak.xm
192 lines (169 loc) · 11.1 KB
/
Tweak.xm
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
#import <HBLog.h>
#import <PSHeader/Misc.h>
#import "Header.h"
#import <dlfcn.h>
#import <mach/port.h>
#import <mach/kern_return.h>
// Tweak
// 0 - white only (default)
// 1 - both
// 2 - amber only
/**
This tweak can tell the camera to use only the amber LED for torch.
In normal situations, the camera decides whether to turn on the amber LED if the scene temperature matches.
Amber tricks the camera into thinking that the scene always matches amber lighting condition.
When the scene is determined to be the warmest (percentile >= 100), only amber LED will turn on.
However, the concept differs for iPhone 7 (H9ISP) and newer which include quad-LEDs (two white's and two amber's).
Faking the scene condition is no longer relevant, as SetTorchLevel() calls a different function that does no longer
rely on the scene condition, but surprisingly presents another neat solution to enabling the amber light.
The function is SetIndividualTorchLEDLevels() that can literally be used to manipulate the brightness level of each individual LED.
The levels are represented as a single 32-bit integer. This integer is separated into 8-bit chunks.
From left to right, the 1st and the 3rd chunks specify the brightness level of the white LEDs (0x00 as min and 0xFF as max).
Similarly, the 2nd and the 4th chunks specify the brightness level of the amber LEDs (0x00 as min and 0xFF as max).
Easy enough, having only amber light requires us to set the integer level to be 0x00hh00hh.
By default, H6ISP and H9ISP cameras ensure that the brightness format is in 0xhh00hh00, locking down any non-jailbroken attempts.
**/
typedef struct HXISPCaptureStream *HXISPCaptureStreamRef;
typedef struct HXISPCaptureDevice *HXISPCaptureDeviceRef;
typedef struct HXISPCaptureGroup *HXISPCaptureGroupRef;
int (*SetTorchLevel)(CFNumberRef, HXISPCaptureStreamRef, HXISPCaptureDeviceRef) = NULL;
int (*SetTorchLevelWithGroup)(CFNumberRef, HXISPCaptureStreamRef, HXISPCaptureGroupRef, HXISPCaptureDeviceRef) = NULL;
int (*SetTorchColor)(CFMutableDictionaryRef, HXISPCaptureStreamRef, HXISPCaptureDeviceRef) = NULL;
int (*SetTorchColorWithGroup)(CFMutableDictionaryRef, HXISPCaptureStreamRef, HXISPCaptureGroupRef, HXISPCaptureDeviceRef) = NULL;
int (*SetTorchColorMode)(void *, unsigned int, unsigned short, unsigned short) = NULL;
SInt32 (*GetCFPreferenceNumber)(CFStringRef const, CFStringRef const, SInt32) = NULL;
int (*SetIndividualTorchLEDLevels)(void *, unsigned int, unsigned int) = NULL;
static SInt32 amberMode() {
return GetCFPreferenceNumber(amberModeKey, kDomain, PSAmberModeDefault);
}
static void SetTorchLevelHook(int result, CFNumberRef level, HXISPCaptureStreamRef stream, HXISPCaptureGroupRef group, HXISPCaptureDeviceRef device) {
if (!result && level && amberMode()) {
// If torch level setting is successful, we can override the torch color
CFMutableDictionaryRef dict = CFDictionaryCreateMutable(NULL, 0, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
int val = 100; // from 0 (coolest) to 100 (warmest)
CFNumberRef threshold = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &val);
CFDictionaryAddValue(dict, CFSTR("WarmLEDPercentile"), threshold);
// Now tell the camera the "fake" scene condition
if (SetTorchColorWithGroup)
SetTorchColorWithGroup(dict, stream, group, device);
else
SetTorchColor(dict, stream, device);
CFRelease(threshold);
CFRelease(dict);
}
}
%group SetTorchLevelHook
%hookf(int, SetTorchLevel, CFNumberRef level, HXISPCaptureStreamRef stream, HXISPCaptureDeviceRef device) {
int result = %orig(level, stream, device);
SetTorchLevelHook(result, level, stream, NULL, device);
return result;
}
%end
%group SetTorchLevelWithGroupHook
%hookf(int, SetTorchLevelWithGroup, CFNumberRef level, HXISPCaptureStreamRef stream, HXISPCaptureGroupRef group, HXISPCaptureDeviceRef device) {
int result = %orig(level, stream, group, device);
SetTorchLevelHook(result, level, stream, group, device);
return result;
}
%end
%group Dual
%hookf(int, SetTorchColorMode, void *arg0, unsigned int arg1, unsigned short mode, unsigned short level) {
return %orig(arg0, arg1, amberMode() == PSAmberModeBoth ? 1 : mode, level);
}
%end
%group Quad
%hookf(int, SetIndividualTorchLEDLevels, void *arg0, unsigned int arg1, unsigned int levels) {
// both: 0xhh00hh00 -> 0xhhhhhhhh
// amber only: 0xhh00hh00 -> 0x00hh00hh
PSAmberMode mode = amberMode();
unsigned int finalLevels = levels && mode ? (mode == PSAmberModeBoth ? (levels | (levels >> 8)) : (levels >> 8)) : levels;
return %orig(arg0, arg1, finalLevels);
}
%end
%ctor {
int HVer = 0;
void *IOKit = dlopen("/System/Library/Frameworks/IOKit.framework/IOKit", RTLD_NOW);
if (IOKit) {
mach_port_t *kIOMasterPortDefault = (mach_port_t *)dlsym(IOKit, "kIOMasterPortDefault");
CFMutableDictionaryRef (*IOServiceMatching)(const char *name) = (CFMutableDictionaryRef (*)(const char *))dlsym(IOKit, "IOServiceMatching");
mach_port_t (*IOServiceGetMatchingService)(mach_port_t masterPort, CFDictionaryRef matching) = (mach_port_t (*)(mach_port_t, CFDictionaryRef))dlsym(IOKit, "IOServiceGetMatchingService");
kern_return_t (*IOObjectRelease)(mach_port_t object) = (kern_return_t (*)(mach_port_t))dlsym(IOKit, "IOObjectRelease");
if (kIOMasterPortDefault && IOServiceGetMatchingService && IOObjectRelease) {
int hvers[] = { 13, 10, 9, 6 };
char AppleHXCamIn[14];
for (int i = 0; i < sizeof(hvers) / sizeof(hvers[0]); ++i) {
snprintf(AppleHXCamIn, sizeof(AppleHXCamIn), "AppleH%dCamIn", hvers[i]);
mach_port_t hx = IOServiceGetMatchingService(*kIOMasterPortDefault, IOServiceMatching(AppleHXCamIn));
if (hx) {
IOObjectRelease(hx);
HVer = hvers[i];
break;
}
}
}
dlclose(IOKit);
HBLogDebug(@"Detected ISP version: %d", HVer);
}
if (HVer == 0) return;
char imagePath[49];
snprintf(imagePath, sizeof(imagePath), "/System/Library/MediaCapture/H%dISP.mediacapture", HVer);
dlopen(imagePath, RTLD_NOW);
MSImageRef hxRef = MSGetImageByName(imagePath);
switch (HVer) {
case 9: {
SetTorchLevel = (int (*)(CFNumberRef, HXISPCaptureStreamRef, HXISPCaptureDeviceRef))MSFindSymbol(hxRef, "__ZL13SetTorchLevelPKvP18H9ISPCaptureStreamP18H9ISPCaptureDevice");
if (SetTorchLevel == NULL)
SetTorchLevelWithGroup = (int (*)(CFNumberRef, HXISPCaptureStreamRef, HXISPCaptureGroupRef, HXISPCaptureDeviceRef))MSFindSymbol(hxRef, "__ZL13SetTorchLevelPKvP18H9ISPCaptureStreamP17H9ISPCaptureGroupP18H9ISPCaptureDevice");
GetCFPreferenceNumber = (SInt32 (*)(CFStringRef const, CFStringRef const, SInt32))_PSFindSymbolCallable(hxRef, "__ZN5H9ISP26H9ISPGetCFPreferenceNumberEPK10__CFStringS2_i");
SetIndividualTorchLEDLevels = (int (*)(void *, unsigned int, unsigned int))MSFindSymbol(hxRef, "__ZN5H9ISP11H9ISPDevice27SetIndividualTorchLEDLevelsEjj");
break;
}
case 6: {
SetTorchLevel = (int (*)(CFNumberRef, HXISPCaptureStreamRef, HXISPCaptureDeviceRef))MSFindSymbol(hxRef, "__ZL13SetTorchLevelPKvP18H6ISPCaptureStreamP18H6ISPCaptureDevice");
GetCFPreferenceNumber = (SInt32 (*)(CFStringRef const, CFStringRef const, SInt32))_PSFindSymbolCallable(hxRef, "__ZN5H6ISP26H6ISPGetCFPreferenceNumberEPK10__CFStringS2_i");
SetTorchColor = (int (*)(CFMutableDictionaryRef, HXISPCaptureStreamRef, HXISPCaptureDeviceRef))_PSFindSymbolCallable(hxRef, "__ZL13SetTorchColorPKvP18H6ISPCaptureStreamP18H6ISPCaptureDevice");
SetTorchColorMode = (int (*)(void *, unsigned int, unsigned short, unsigned short))MSFindSymbol(hxRef, "__ZN5H6ISP11H6ISPDevice17SetTorchColorModeEjtt");
break;
}
default: {
char SetTorchLevelWithGroupSymbol[88];
snprintf(SetTorchLevelWithGroupSymbol, sizeof(SetTorchLevelWithGroupSymbol), "__ZL13SetTorchLevelPKvP19H%dISPCaptureStreamP18H%dISPCaptureGroupP19H%dISPCaptureDevice", HVer, HVer, HVer);
SetTorchLevelWithGroup = (int (*)(CFNumberRef, HXISPCaptureStreamRef, HXISPCaptureGroupRef, HXISPCaptureDeviceRef))MSFindSymbol(hxRef, SetTorchLevelWithGroupSymbol);
if (SetTorchLevelWithGroup == NULL) {
char SetTorchLevelSymbol[67];
snprintf(SetTorchLevelSymbol, sizeof(SetTorchLevelSymbol), "__ZL13SetTorchLevelPKvP19H%dISPCaptureStreamP19H%dISPCaptureDevice", HVer, HVer);
SetTorchLevel = (int (*)(CFNumberRef, HXISPCaptureStreamRef, HXISPCaptureDeviceRef))MSFindSymbol(hxRef, SetTorchLevelSymbol);
}
char GetCFPreferenceNumberSymbol[60];
snprintf(GetCFPreferenceNumberSymbol, sizeof(GetCFPreferenceNumberSymbol), "__ZN6H%dISP27H%dISPGetCFPreferenceNumberEPK10__CFStringS2_i", HVer, HVer);
GetCFPreferenceNumber = (SInt32 (*)(CFStringRef const, CFStringRef const, SInt32))_PSFindSymbolCallable(hxRef, GetCFPreferenceNumberSymbol);
char SetIndividualTorchLEDLevelsSymbol[58];
snprintf(SetIndividualTorchLEDLevelsSymbol, sizeof(SetIndividualTorchLEDLevelsSymbol), "__ZN6H%dISP12H%dISPDevice27SetIndividualTorchLEDLevelsEjj", HVer, HVer);
SetIndividualTorchLEDLevels = (int (*)(void *, unsigned int, unsigned int))MSFindSymbol(hxRef, SetIndividualTorchLEDLevelsSymbol);
char SetTorchColorWithGroupSymbol[88];
snprintf(SetTorchColorWithGroupSymbol, sizeof(SetTorchColorWithGroupSymbol), "__ZL13SetTorchColorPKvP19H%dISPCaptureStreamP18H%dISPCaptureGroupP19H%dISPCaptureDevice", HVer, HVer, HVer);
SetTorchColorWithGroup = (int (*)(CFMutableDictionaryRef, HXISPCaptureStreamRef, HXISPCaptureGroupRef, HXISPCaptureDeviceRef))_PSFindSymbolCallable(hxRef, SetTorchColorWithGroupSymbol);
char SetTorchColorModeSymbol[49];
snprintf(SetTorchColorModeSymbol, sizeof(SetTorchColorModeSymbol), "__ZN6H%dISP12H%dISPDevice17SetTorchColorModeEjtt", HVer, HVer);
SetTorchColorMode = (int (*)(void *, unsigned int, unsigned short, unsigned short))MSFindSymbol(hxRef, SetTorchColorModeSymbol);
break;
}
}
HBLogDebug(@"SetTorchLevel found: %d", SetTorchLevel != NULL);
HBLogDebug(@"SetTorchLevelWithGroup found: %d", SetTorchLevelWithGroup != NULL);
HBLogDebug(@"GetCFPreferenceNumber found: %d", GetCFPreferenceNumber != NULL);
HBLogDebug(@"SetIndividualTorchLEDLevels found: %d", SetIndividualTorchLEDLevels != NULL);
if (SetIndividualTorchLEDLevels != NULL) {
%init(Quad);
} else {
if (SetTorchLevelWithGroup) {
%init(SetTorchLevelWithGroupHook);
} else {
%init(SetTorchLevelHook);
}
HBLogDebug(@"SetTorchColor found: %d", SetTorchColor != NULL);
HBLogDebug(@"SetTorchColorWithGroup found: %d", SetTorchColorWithGroup != NULL);
HBLogDebug(@"SetTorchColorMode found: %d", SetTorchColorMode != NULL);
%init(Dual);
}
}