revenge-tweak/Sources/Utils.m
Ally Founder dba11af542 fix: restore compilability and add Bridgeless-mode injection hook
- Fix BunnyLog compile error in Sideloading.x (leftover from Bunny->Revenge
  rename; only RevengeLog is defined)
- Remove duplicated -loadConfig/+defaultConfig methods in LoaderConfig.m
- Repoint CI's ipa-patcher download at a live mirror with a pinned checksum
  (Cristiandis/ipa-patcher was deleted upstream)
- Add a second injection hook on RCTInstance/-_loadScriptFromSource: for
  React Native's Bridgeless mode, alongside the existing RCTCxxBridge hook.
  Discord's Android build already runs Bridgeless (confirmed via
  revenge-xposed's hook targets) and revenge-bundle now pins RN 0.81, which
  defaults to it - so the legacy bridge hook this tweak relied on was very
  likely dead. Unverified against a real device; see AUDIT.md.

See AUDIT.md for the full iOS-vs-Android comparison and remaining gaps.
2026-08-03 09:46:46 -04:00

101 lines
No EOL
3.9 KiB
Objective-C

#import "Utils.h"
#import <objc/runtime.h>
id makeRCTSource(NSURL *url, NSData *data) {
Class RCTSourceClass = NSClassFromString(@"RCTSource");
if (!RCTSourceClass) return nil;
id source = [RCTSourceClass new];
// RCTSource has no public initializer (see RCTJavaScriptLoader.mm's private
// RCTSourceCreate()), so its private ivars are poked directly instead.
Ivar urlIvar = class_getInstanceVariable(RCTSourceClass, "_url");
Ivar dataIvar = class_getInstanceVariable(RCTSourceClass, "_data");
Ivar lengthIvar = class_getInstanceVariable(RCTSourceClass, "_length");
Ivar filesChangedCountIvar = class_getInstanceVariable(RCTSourceClass, "_filesChangedCount");
if (urlIvar) object_setIvar(source, urlIvar, url);
if (dataIvar) object_setIvar(source, dataIvar, data);
// _length and _filesChangedCount are scalars (NSUInteger / NSInteger), not objects,
// so they need direct memory writes at the ivar's offset instead of object_setIvar.
if (lengthIvar) {
ptrdiff_t offset = ivar_getOffset(lengthIvar);
*(NSUInteger *)((uint8_t *)(__bridge void *)source + offset) = data.length;
}
if (filesChangedCountIvar) {
ptrdiff_t offset = ivar_getOffset(filesChangedCountIvar);
// -2 == RCTSourceFilesChangedCountNotBuiltByBundler, matching sources not built by Metro.
*(NSInteger *)((uint8_t *)(__bridge void *)source + offset) = -2;
}
return source;
}
NSURL *getPyoncordDirectory(void) {
NSFileManager *fileManager = [NSFileManager defaultManager];
NSURL *documentDirectoryURL = [[fileManager URLsForDirectory:NSDocumentDirectory
inDomains:NSUserDomainMask] lastObject];
NSURL *pyoncordFolderURL = [documentDirectoryURL URLByAppendingPathComponent:@"pyoncord"];
if (![fileManager fileExistsAtPath:pyoncordFolderURL.path]) {
[fileManager createDirectoryAtURL:pyoncordFolderURL
withIntermediateDirectories:YES
attributes:nil
error:nil];
}
return pyoncordFolderURL;
}
UIColor *hexToUIColor(NSString *hex) {
if (![hex hasPrefix:@"#"]) {
return nil;
}
NSString *hexColor = [hex substringFromIndex:1];
if (hexColor.length == 6) {
hexColor = [hexColor stringByAppendingString:@"ff"];
}
if (hexColor.length == 8) {
unsigned int hexNumber;
NSScanner *scanner = [NSScanner scannerWithString:hexColor];
if ([scanner scanHexInt:&hexNumber]) {
CGFloat r = ((hexNumber & 0xFF000000) >> 24) / 255.0;
CGFloat g = ((hexNumber & 0x00FF0000) >> 16) / 255.0;
CGFloat b = ((hexNumber & 0x0000FF00) >> 8) / 255.0;
CGFloat a = (hexNumber & 0x000000FF) / 255.0;
return [UIColor colorWithRed:r green:g blue:b alpha:a];
}
}
return nil;
}
void showErrorAlert(NSString *title, NSString *message) {
dispatch_async(dispatch_get_main_queue(), ^{
UIAlertController *alert = [UIAlertController alertControllerWithTitle:title
message:message
preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *okAction = [UIAlertAction actionWithTitle:@"OK"
style:UIAlertActionStyleDefault
handler:nil];
[alert addAction:okAction];
UIWindow *window = nil;
NSArray *windows = [[UIApplication sharedApplication] windows];
for (UIWindow *w in windows) {
if (w.isKeyWindow) {
window = w;
break;
}
}
[window.rootViewController presentViewController:alert animated:YES completion:nil];
});
}