]> git.saurik.com Git - winterboard.git/blob - Library.mm
d1cf54ed37bcd9a271ec2e37a7b0c2c7ed2cec84
[winterboard.git] / Library.mm
1 /* WinterBoard - Theme Manager for the iPhone
2 * Copyright (C) 2008-2011 Jay Freeman (saurik)
3 */
4
5 /*
6 * Redistribution and use in source and binary
7 * forms, with or without modification, are permitted
8 * provided that the following conditions are met:
9 *
10 * 1. Redistributions of source code must retain the
11 * above copyright notice, this list of conditions
12 * and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the
14 * above copyright notice, this list of conditions
15 * and the following disclaimer in the documentation
16 * and/or other materials provided with the
17 * distribution.
18 * 3. The name of the author may not be used to endorse
19 * or promote products derived from this software
20 * without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS''
23 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
24 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
25 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
27 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
28 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
29 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
30 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
31 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
32 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
33 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
34 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
35 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36 */
37
38 #include <sys/time.h>
39
40 struct timeval _ltv;
41 bool _itv;
42
43 #define _trace() do { \
44 struct timeval _ctv; \
45 gettimeofday(&_ctv, NULL); \
46 if (!_itv) { \
47 _itv = true; \
48 _ltv = _ctv; \
49 } \
50 NSLog(@"%lu.%.6u[%f]:WB:_trace()@%s:%u[%s]\n", \
51 _ctv.tv_sec, _ctv.tv_usec, \
52 (_ctv.tv_sec - _ltv.tv_sec) + (_ctv.tv_usec - _ltv.tv_usec) / 1000000.0, \
53 __FILE__, __LINE__, __FUNCTION__\
54 ); \
55 _ltv = _ctv; \
56 } while (false)
57
58 #define _transient
59
60 #import <CoreFoundation/CoreFoundation.h>
61 #import <Foundation/Foundation.h>
62 #import <CoreGraphics/CoreGraphics.h>
63 #import <ImageIO/CGImageSource.h>
64
65 #import <Celestial/AVController.h>
66 #import <Celestial/AVItem.h>
67 #import <Celestial/AVQueue.h>
68
69 #include <substrate.h>
70
71 #import <UIKit/UIKit.h>
72
73 #import <SpringBoard/SBApplication.h>
74 #import <SpringBoard/SBApplicationIcon.h>
75 #import <SpringBoard/SBAppWindow.h>
76 #import <SpringBoard/SBAwayView.h>
77 #import <SpringBoard/SBBookmarkIcon.h>
78 #import <SpringBoard/SBButtonBar.h>
79 #import <SpringBoard/SBCalendarIconContentsView.h>
80 #import <SpringBoard/SBIconController.h>
81 #import <SpringBoard/SBIconLabel.h>
82 #import <SpringBoard/SBIconList.h>
83 #import <SpringBoard/SBIconModel.h>
84 #import <SpringBoard/SBImageCache.h>
85 // XXX: #import <SpringBoard/SBSearchView.h>
86 #import <SpringBoard/SBSearchTableViewCell.h>
87 #import <SpringBoard/SBStatusBarContentsView.h>
88 #import <SpringBoard/SBStatusBarController.h>
89 #import <SpringBoard/SBStatusBarOperatorNameView.h>
90 #import <SpringBoard/SBStatusBarTimeView.h>
91 #import <SpringBoard/SBUIController.h>
92 #import <SpringBoard/SBWidgetApplicationIcon.h>
93
94 #import <MobileSMS/mSMSMessageTranscriptController.h>
95
96 #import <MediaPlayer/MPMoviePlayerController.h>
97 #import <MediaPlayer/MPVideoView.h>
98 #import <MediaPlayer/MPVideoView-PlaybackControl.h>
99
100 #import <CoreGraphics/CGGeometry.h>
101
102 #import <ChatKit/CKMessageCell.h>
103
104 #include <sys/sysctl.h>
105
106 #include "WBMarkup.h"
107
108 extern "C" void __clear_cache (char *beg, char *end);
109
110 @protocol WinterBoard
111 - (void *) _node;
112 @end
113
114 Class $MPMoviePlayerController;
115 Class $MPVideoView;
116
117 MSClassHook(NSBundle)
118 MSClassHook(NSString)
119
120 MSClassHook(UIImage)
121 MSMetaClassHook(UIImage)
122 MSClassHook(UINavigationBar)
123 MSClassHook(UIToolbar)
124
125 MSClassHook(CKBalloonView)
126 MSClassHook(CKMessageCell)
127 MSClassHook(CKTimestampView)
128 MSClassHook(CKTranscriptCell)
129 MSClassHook(CKTranscriptController)
130 MSClassHook(CKTranscriptHeaderView)
131 MSClassHook(CKTranscriptTableView)
132
133 MSClassHook(SBApplication)
134 MSClassHook(SBApplicationIcon)
135 MSClassHook(SBAwayView)
136 MSClassHook(SBBookmarkIcon)
137 MSClassHook(SBButtonBar)
138 MSClassHook(SBCalendarApplicationIcon)
139 MSClassHook(SBCalendarIconContentsView)
140 MSClassHook(SBDockIconListView)
141 MSClassHook(SBIcon)
142 MSClassHook(SBIconBadge)
143 MSClassHook(SBIconBadgeFactory)
144 MSClassHook(SBIconController)
145 MSClassHook(SBIconLabel)
146 MSClassHook(SBIconList)
147 MSClassHook(SBIconModel)
148 //MSClassHook(SBImageCache)
149 MSClassHook(SBSearchView)
150 MSClassHook(SBSearchTableViewCell)
151 MSClassHook(SBSlidingAlertDisplay)
152 MSClassHook(SBStatusBarContentsView)
153 MSClassHook(SBStatusBarController)
154 MSClassHook(SBStatusBarOperatorNameView)
155 MSClassHook(SBStatusBarTimeView)
156 MSClassHook(SBUIController)
157 MSClassHook(SBWallpaperView)
158 MSClassHook(SBWidgetApplicationIcon)
159
160 extern "C" void WKSetCurrentGraphicsContext(CGContextRef);
161
162 __attribute__((__constructor__))
163 static void MSFixClass() {
164 if ($SBIcon == nil)
165 $SBIcon = objc_getClass("SBIconView");
166 if ($CKTranscriptController == nil)
167 $CKTranscriptController = objc_getClass("mSMSMessageTranscriptController");
168 }
169
170 static bool IsWild_;
171 static bool Four_($SBDockIconListView != nil);
172
173 @interface NSDictionary (WinterBoard)
174 - (UIColor *) wb$colorForKey:(NSString *)key;
175 - (BOOL) wb$boolForKey:(NSString *)key;
176 @end
177
178 @implementation NSDictionary (WinterBoard)
179
180 - (UIColor *) wb$colorForKey:(NSString *)key {
181 NSString *value = [self objectForKey:key];
182 if (value == nil)
183 return nil;
184 /* XXX: incorrect */
185 return nil;
186 }
187
188 - (BOOL) wb$boolForKey:(NSString *)key {
189 if (NSString *value = [self objectForKey:key])
190 return [value boolValue];
191 return false;
192 }
193
194 @end
195
196 static BOOL (*_GSFontGetUseLegacyFontMetrics)();
197 #define $GSFontGetUseLegacyFontMetrics() \
198 (_GSFontGetUseLegacyFontMetrics == NULL ? YES : _GSFontGetUseLegacyFontMetrics())
199
200 static bool Debug_ = false;
201 static bool UIDebug_ = false;
202 static bool Engineer_ = false;
203 static bool SummerBoard_ = true;
204 static bool SpringBoard_;
205
206 static UIImage *(*_UIApplicationImageWithName)(NSString *name);
207 static UIImage *(*_UIImageWithNameInDomain)(NSString *name, NSString *domain);
208 static NSBundle *(*_UIKitBundle)();
209 static bool (*_UIPackedImageTableGetIdentifierForName)(NSString *, int *);
210 static int (*_UISharedImageNameGetIdentifier)(NSString *);
211
212 static NSMutableDictionary *UIImages_ = [[NSMutableDictionary alloc] initWithCapacity:32];
213 static NSMutableDictionary *PathImages_ = [[NSMutableDictionary alloc] initWithCapacity:16];
214 static NSMutableDictionary *Cache_ = [[NSMutableDictionary alloc] initWithCapacity:64];
215 static NSMutableDictionary *Strings_ = [[NSMutableDictionary alloc] initWithCapacity:0];
216 static NSMutableDictionary *Bundles_ = [[NSMutableDictionary alloc] initWithCapacity:2];
217
218 static NSFileManager *Manager_;
219 static NSMutableArray *Themes_;
220
221 static NSDictionary *English_;
222 static NSMutableDictionary *Info_;
223
224 // $getTheme$() {{{
225 static NSMutableDictionary *Themed_ = [[NSMutableDictionary alloc] initWithCapacity:128];
226
227 static unsigned Scale_ = 0;
228
229 static unsigned $getScale$(NSString *path) {
230 NSString *name(path);
231
232 #define StripName(strip) \
233 if ([name hasSuffix:@ strip]) \
234 name = [name substringWithRange:NSMakeRange(0, [name length] - sizeof(strip) - 1)];
235
236 StripName(".png");
237 StripName(".jpg");
238 StripName("~iphone");
239 StripName("~ipad");
240
241 return [name hasSuffix:@"@2x"] ? 2 : 1;
242 }
243
244 static NSArray *$useScale$(NSArray *files, bool use = true) {
245 if (use && Scale_ == 0) {
246 UIScreen *screen([UIScreen mainScreen]);
247 if ([screen respondsToSelector:@selector(scale)])
248 Scale_ = [screen scale];
249 else
250 Scale_ = 1;
251 }
252
253 if (Scale_ == 1)
254 return files;
255
256 NSString *idiom(IsWild_ ? @"ipad" : @"iphone");
257
258 NSMutableArray *scaled([NSMutableArray arrayWithCapacity:([files count] * 4)]);
259
260 for (NSString *file in files) {
261 NSString *base([file stringByDeletingPathExtension]);
262 NSString *extension([file pathExtension]);
263
264 if (use) {
265 if (Scale_ == 2) {
266 [scaled addObject:[NSString stringWithFormat:@"%@@2x~%@.%@", base, idiom, extension]];
267 if (!IsWild_)
268 [scaled addObject:[NSString stringWithFormat:@"%@@2x.%@", base, extension]];
269 }
270
271 [scaled addObject:[NSString stringWithFormat:@"%@~%@.%@", base, idiom, extension]];
272
273 // if (!IsWild_) <- support old themes
274 [scaled addObject:file];
275 } else if ([base hasSuffix: @"@2x"]) {
276 [scaled addObject:[NSString stringWithFormat:@"%@~iphone.%@", base, extension]];
277 [scaled addObject:file];
278
279 // XXX: this actually can't be used, as the person loading the file doesn't realize that the @2x changed
280 /*NSString *rest([base substringWithRange:NSMakeRange(0, [base length] - 3)]);
281 [scaled addObject:[NSString stringWithFormat:@"%@~iphone.%@", rest, extension]];
282 [scaled addObject:[rest stringByAppendingPathExtension:extension]];*/
283 } else {
284 // XXX: this code isn't really complete
285
286 [scaled addObject:file];
287
288 if ([base hasSuffix:@"~iphone"])
289 [scaled addObject:[[base substringWithRange:NSMakeRange(0, [base length] - 7)] stringByAppendingPathExtension:extension]];
290 }
291 }
292
293 return scaled;
294 }
295
296 static NSString *$getTheme$(NSArray *files, NSArray *themes = Themes_) {
297 // XXX: this is not reasonable; OMG
298 id key(files);
299
300 @synchronized (Themed_) {
301 if (NSString *path = [Themed_ objectForKey:key])
302 return reinterpret_cast<id>(path) == [NSNull null] ? nil : path;
303 }
304
305 if (Debug_)
306 NSLog(@"WB:Debug: %@", [files description]);
307
308 NSString *path;
309
310 for (NSString *theme in Themes_)
311 for (NSString *file in files) {
312 path = [NSString stringWithFormat:@"%@/%@", theme, file];
313 if ([Manager_ fileExistsAtPath:path])
314 goto set;
315 }
316
317 path = nil;
318 set:
319
320 @synchronized (Themed_) {
321 [Themed_ setObject:(path == nil ? [NSNull null] : reinterpret_cast<id>(path)) forKey:key];
322 }
323
324 return path;
325 }
326 // }}}
327 // $pathForFile$inBundle$() {{{
328 static NSString *$pathForFile$inBundle$(NSString *file, NSBundle *bundle, bool ui) {
329 NSString *identifier = [bundle bundleIdentifier];
330 NSMutableArray *names = [NSMutableArray arrayWithCapacity:8];
331
332 if (identifier != nil)
333 [names addObject:[NSString stringWithFormat:@"Bundles/%@/%@", identifier, file]];
334 if (NSString *folder = [[bundle bundlePath] lastPathComponent]) {
335 [names addObject:[NSString stringWithFormat:@"Folders/%@/%@", folder, file]];
336 NSString *base([folder stringByDeletingPathExtension]);
337 if ([base hasSuffix:@"~iphone"])
338 [names addObject:[NSString stringWithFormat:@"Folders/%@.%@/%@", [base substringWithRange:NSMakeRange(0, [base length] - 7)], [folder pathExtension], file]];
339 }
340 if (ui)
341 [names addObject:[NSString stringWithFormat:@"UIImages/%@", file]];
342
343 #define remapResourceName(oldname, newname) \
344 else if ([file isEqualToString:(oldname)]) \
345 [names addObject:[NSString stringWithFormat:@"%@.png", newname]]; \
346
347 bool summer(SpringBoard_ && SummerBoard_);
348
349 if (identifier == nil);
350 else if ([identifier isEqualToString:@"com.apple.chatkit"])
351 [names addObject:[NSString stringWithFormat:@"Bundles/com.apple.MobileSMS/%@", file]];
352 else if ([identifier isEqualToString:@"com.apple.calculator"])
353 [names addObject:[NSString stringWithFormat:@"Files/Applications/Calculator.app/%@", file]];
354 else if (!summer);
355 remapResourceName(@"FSO_BG.png", @"StatusBar")
356 remapResourceName(Four_ ? @"SBDockBG-old.png" : @"SBDockBG.png", @"Dock")
357 remapResourceName(@"SBWeatherCelsius.png", @"Icons/Weather")
358
359 [names addObject:[NSString stringWithFormat:@"Fallback/%@", file]];
360
361 if (NSString *path = $getTheme$($useScale$(names, ui)))
362 return path;
363
364 return nil;
365 }
366 // }}}
367
368 static NSString *$pathForIcon$(SBApplication *self, NSString *suffix = @"") {
369 NSString *identifier = [self bundleIdentifier];
370 NSString *path = [self path];
371 NSString *folder = [path lastPathComponent];
372 NSString *dname = [self displayName];
373 NSString *didentifier = [self displayIdentifier];
374
375 if (Debug_)
376 NSLog(@"WB:Debug: [SBApplication(%@:%@:%@:%@) pathForIcon]", identifier, folder, dname, didentifier);
377
378 NSMutableArray *names = [NSMutableArray arrayWithCapacity:8];
379
380 /* XXX: I might need to keep this for backwards compatibility
381 if (identifier != nil)
382 [names addObject:[NSString stringWithFormat:@"Bundles/%@/icon.png", identifier]];
383 if (folder != nil)
384 [names addObject:[NSString stringWithFormat:@"Folders/%@/icon.png", folder]]; */
385
386 #define testForIcon(Name) \
387 if (NSString *name = Name) \
388 [names addObject:[NSString stringWithFormat:@"Icons%@/%@.png", suffix, name]];
389
390 if (![didentifier isEqualToString:identifier])
391 testForIcon(didentifier);
392
393 testForIcon(identifier);
394 testForIcon(dname);
395
396 if ([identifier isEqualToString:@"com.apple.MobileSMS"])
397 testForIcon(@"SMS");
398
399 if (didentifier != nil) {
400 testForIcon([English_ objectForKey:didentifier]);
401
402 NSArray *parts = [didentifier componentsSeparatedByString:@"-"];
403 if ([parts count] != 1)
404 if (NSDictionary *english = [[[NSDictionary alloc] initWithContentsOfFile:[path stringByAppendingString:@"/English.lproj/UIRoleDisplayNames.strings"]] autorelease])
405 testForIcon([english objectForKey:[parts lastObject]]);
406 }
407
408 if (NSString *path = $getTheme$(names))
409 return path;
410
411 return nil;
412 }
413
414 // -[NSBundle wb$bundleWithFile] {{{
415 @interface NSBundle (WinterBoard)
416 + (NSBundle *) wb$bundleWithFile:(NSString *)path;
417 @end
418
419 @implementation NSBundle (WinterBoard)
420
421 + (NSBundle *) wb$bundleWithFile:(NSString *)path {
422 path = [path stringByDeletingLastPathComponent];
423 if (path == nil || [path length] == 0 || [path isEqualToString:@"/"])
424 return nil;
425
426 NSBundle *bundle([Bundles_ objectForKey:path]);
427 if (reinterpret_cast<id>(bundle) == [NSNull null])
428 return nil;
429 else if (bundle == nil) {
430 if ([Manager_ fileExistsAtPath:[path stringByAppendingPathComponent:@"Info.plist"]])
431 bundle = [NSBundle bundleWithPath:path];
432 if (bundle == nil)
433 bundle = [NSBundle wb$bundleWithFile:path];
434 if (Debug_)
435 NSLog(@"WB:Debug:PathBundle(%@, %@)", path, bundle);
436 [Bundles_ setObject:(bundle == nil ? [NSNull null] : reinterpret_cast<id>(bundle)) forKey:path];
437 }
438
439 return bundle;
440 }
441
442 @end
443 // }}}
444 // -[NSString wb$themedPath] {{{
445 @interface NSString (WinterBoard)
446 - (NSString *) wb$themedPath;
447 @end
448
449 @implementation NSString (WinterBoard)
450
451 - (NSString *) wb$themedPath {
452 if ([self hasPrefix:@"/Library/Themes/"])
453 return self;
454
455 if (Debug_)
456 NSLog(@"WB:Debug:Bypass(\"%@\")", self);
457
458 if (NSBundle *bundle = [NSBundle wb$bundleWithFile:self]) {
459 NSString *file([self stringByResolvingSymlinksInPath]);
460 NSString *prefix([[bundle bundlePath] stringByResolvingSymlinksInPath]);
461 if ([file hasPrefix:prefix]) {
462 NSUInteger length([prefix length]);
463 if (length != [file length])
464 if (NSString *path = $pathForFile$inBundle$([file substringFromIndex:(length + 1)], bundle, false))
465 return path;
466 }
467 }
468
469 return self;
470 }
471
472 @end
473 // }}}
474
475 void WBLogRect(const char *tag, struct CGRect rect) {
476 NSLog(@"%s:{%f,%f+%f,%f}", tag, rect.origin.x, rect.origin.y, rect.size.width, rect.size.height);
477 }
478
479 void WBLogHierarchy(UIView *view, unsigned index = 0, unsigned indent = 0) {
480 CGRect frame([view frame]);
481 NSLog(@"%*s|%2d:%p:%s : {%f,%f+%f,%f} (%@)", indent * 3, "", index, view, class_getName([view class]), frame.origin.x, frame.origin.y, frame.size.width, frame.size.height, [view backgroundColor]);
482 index = 0;
483 for (UIView *child in [view subviews])
484 WBLogHierarchy(child, index++, indent + 1);
485 }
486
487 UIImage *$cacheForImage$(UIImage *image) {
488 CGColorSpaceRef space(CGColorSpaceCreateDeviceRGB());
489 CGRect rect = {CGPointMake(1, 1), [image size]};
490 CGSize size = {rect.size.width + 2, rect.size.height + 2};
491
492 CGContextRef context(CGBitmapContextCreate(NULL, size.width, size.height, 8, 4 * size.width, space, kCGBitmapByteOrder32Little | kCGImageAlphaPremultipliedFirst));
493 CGColorSpaceRelease(space);
494
495 CGContextDrawImage(context, rect, [image CGImage]);
496 CGImageRef ref(CGBitmapContextCreateImage(context));
497 CGContextRelease(context);
498
499 UIImage *cache([UIImage imageWithCGImage:ref]);
500 CGImageRelease(ref);
501
502 return cache;
503 }
504
505 /*MSHook(id, SBImageCache$initWithName$forImageWidth$imageHeight$initialCapacity$, SBImageCache *self, SEL sel, NSString *name, unsigned width, unsigned height, unsigned capacity) {
506 //if ([name isEqualToString:@"icons"]) return nil;
507 return _SBImageCache$initWithName$forImageWidth$imageHeight$initialCapacity$(self, sel, name, width, height, capacity);
508 }*/
509
510 MSHook(void, SBIconModel$cacheImageForIcon$, SBIconModel *self, SEL sel, SBIcon *icon) {
511 NSString *key([icon displayIdentifier]);
512
513 if (UIImage *image = [icon icon]) {
514 CGSize size = [image size];
515 if (size.width != 59 || size.height != 60) {
516 UIImage *cache($cacheForImage$(image));
517 [Cache_ setObject:cache forKey:key];
518 return;
519 }
520 }
521
522 _SBIconModel$cacheImageForIcon$(self, sel, icon);
523 }
524
525 MSHook(void, SBIconModel$cacheImagesForIcon$, SBIconModel *self, SEL sel, SBIcon *icon) {
526 /* XXX: do I /really/ have to do this? figure out how to cache the small icon! */
527 _SBIconModel$cacheImagesForIcon$(self, sel, icon);
528
529 NSString *key([icon displayIdentifier]);
530
531 if (UIImage *image = [icon icon]) {
532 CGSize size = [image size];
533 if (size.width != 59 || size.height != 60) {
534 UIImage *cache($cacheForImage$(image));
535 [Cache_ setObject:cache forKey:key];
536 return;
537 }
538 }
539 }
540
541 MSHook(UIImage *, SBIconModel$getCachedImagedForIcon$, SBIconModel *self, SEL sel, SBIcon *icon) {
542 NSString *key([icon displayIdentifier]);
543 if (UIImage *image = [Cache_ objectForKey:key])
544 return image;
545 else
546 return _SBIconModel$getCachedImagedForIcon$(self, sel, icon);
547 }
548
549 MSHook(UIImage *, SBIconModel$getCachedImagedForIcon$smallIcon$, SBIconModel *self, SEL sel, SBIcon *icon, BOOL small) {
550 if (small)
551 return _SBIconModel$getCachedImagedForIcon$smallIcon$(self, sel, icon, small);
552 NSString *key([icon displayIdentifier]);
553 if (UIImage *image = [Cache_ objectForKey:key])
554 return image;
555 else
556 return _SBIconModel$getCachedImagedForIcon$smallIcon$(self, sel, icon, small);
557 }
558
559 MSHook(id, SBSearchView$initWithFrame$, id /* XXX: SBSearchView */ self, SEL sel, struct CGRect frame) {
560 if ((self = _SBSearchView$initWithFrame$(self, sel, frame)) != nil) {
561 [self setBackgroundColor:[UIColor clearColor]];
562 for (UIView *child in [self subviews])
563 [child setBackgroundColor:[UIColor clearColor]];
564 } return self;
565 }
566
567 MSHook(id, SBSearchTableViewCell$initWithStyle$reuseIdentifier$, SBSearchTableViewCell *self, SEL sel, int style, NSString *reuse) {
568 if ((self = _SBSearchTableViewCell$initWithStyle$reuseIdentifier$(self, sel, style, reuse)) != nil) {
569 [self setBackgroundColor:[UIColor clearColor]];
570 } return self;
571 }
572
573 MSHook(void, SBSearchTableViewCell$drawRect$, SBSearchTableViewCell *self, SEL sel, struct CGRect rect, BOOL selected) {
574 _SBSearchTableViewCell$drawRect$(self, sel, rect, selected);
575 float inset([self edgeInset]);
576 [[UIColor clearColor] set];
577 UIRectFill(CGRectMake(0, 0, inset, rect.size.height));
578 UIRectFill(CGRectMake(rect.size.width - inset, 0, inset, rect.size.height));
579 }
580
581 MSHook(UIImage *, SBApplicationIcon$icon, SBApplicationIcon *self, SEL sel) {
582 if (![Info_ wb$boolForKey:@"ComposeStoreIcons"])
583 if (NSString *path = $pathForIcon$([self application]))
584 return [UIImage imageWithContentsOfFile:path];
585 return _SBApplicationIcon$icon(self, sel);
586 }
587
588 MSHook(UIImage *, SBApplicationIcon$generateIconImage$, SBApplicationIcon *self, SEL sel, int type) {
589 if (type == 2)
590 if (![Info_ wb$boolForKey:@"ComposeStoreIcons"]) {
591 if (IsWild_ && false) // XXX: delete this code, it should not be supported
592 if (NSString *path72 = $pathForIcon$([self application], @"-72"))
593 return [UIImage imageWithContentsOfFile:path72];
594 if (NSString *path = $pathForIcon$([self application]))
595 if (UIImage *image = [UIImage imageWithContentsOfFile:path]) {
596 float width;
597 if ([$SBIcon respondsToSelector:@selector(defaultIconImageSize)])
598 width = [$SBIcon defaultIconImageSize].width;
599 else
600 width = 59;
601 return width == 59 ? image : [image _imageScaledToProportion:(width / 59.0) interpolationQuality:5];
602 }
603 }
604 return _SBApplicationIcon$generateIconImage$(self, sel, type);
605 }
606
607 MSHook(UIImage *, SBWidgetApplicationIcon$icon, SBWidgetApplicationIcon *self, SEL sel) {
608 if (Debug_)
609 NSLog(@"WB:Debug:Widget(%@:%@)", [self displayIdentifier], [self displayName]);
610 if (NSString *path = $getTheme$([NSArray arrayWithObject:[NSString stringWithFormat:@"Icons/%@.png", [self displayName]]]))
611 return [UIImage imageWithContentsOfFile:path];
612 return _SBWidgetApplicationIcon$icon(self, sel);
613 }
614
615 MSHook(UIImage *, SBBookmarkIcon$icon, SBBookmarkIcon *self, SEL sel) {
616 if (Debug_)
617 NSLog(@"WB:Debug:Bookmark(%@:%@)", [self displayIdentifier], [self displayName]);
618 if (NSString *path = $getTheme$([NSArray arrayWithObject:[NSString stringWithFormat:@"Icons/%@.png", [self displayName]]]))
619 return [UIImage imageWithContentsOfFile:path];
620 return _SBBookmarkIcon$icon(self, sel);
621 }
622
623 MSHook(NSString *, SBApplication$pathForIcon, SBApplication *self, SEL sel) {
624 if (NSString *path = $pathForIcon$(self))
625 return path;
626 return _SBApplication$pathForIcon(self, sel);
627 }
628
629 static UIImage *CachedImageAtPath(NSString *path) {
630 path = [path stringByResolvingSymlinksInPath];
631 UIImage *image = [PathImages_ objectForKey:path];
632 if (image != nil)
633 return reinterpret_cast<id>(image) == [NSNull null] ? nil : image;
634 image = [[UIImage alloc] initWithContentsOfFile:path cache:true];
635 if (image != nil)
636 image = [image autorelease];
637 [PathImages_ setObject:(image == nil ? [NSNull null] : reinterpret_cast<id>(image)) forKey:path];
638 return image;
639 }
640
641 MSHook(UIImage *, _UIApplicationImageWithName, NSString *name) {
642 NSBundle *bundle = [NSBundle mainBundle];
643 if (Debug_)
644 NSLog(@"WB:Debug: _UIApplicationImageWithName(\"%@\", %@)", name, bundle);
645 if (NSString *path = $pathForFile$inBundle$(name, bundle, false))
646 return CachedImageAtPath(path);
647 return __UIApplicationImageWithName(name);
648 }
649
650 #define WBDelegate(delegate) \
651 - (NSMethodSignature*) methodSignatureForSelector:(SEL)sel { \
652 if (Engineer_) \
653 NSLog(@"WB:MS:%s:(%s)", class_getName([self class]), sel_getName(sel)); \
654 if (NSMethodSignature *sig = [delegate methodSignatureForSelector:sel]) \
655 return sig; \
656 NSLog(@"WB:Error: [%s methodSignatureForSelector:(%s)]", class_getName([self class]), sel_getName(sel)); \
657 return nil; \
658 } \
659 \
660 - (void) forwardInvocation:(NSInvocation*)inv { \
661 SEL sel = [inv selector]; \
662 if ([delegate respondsToSelector:sel]) \
663 [inv invokeWithTarget:delegate]; \
664 else \
665 NSLog(@"WB:Error: [%s forwardInvocation:(%s)]", class_getName([self class]), sel_getName(sel)); \
666 }
667
668 // %hook -[NSBundle pathForResource:ofType:] {{{
669 MSInstanceMessageHook2(NSString *, NSBundle, pathForResource,ofType, NSString *, resource, NSString *, type) {
670 NSString *file = type == nil ? resource : [NSString stringWithFormat:@"%@.%@", resource, type];
671 if (Debug_)
672 NSLog(@"WB:Debug: [NSBundle(%@) pathForResource:\"%@\"]", [self bundleIdentifier], file);
673 if (NSString *path = $pathForFile$inBundle$(file, self, false))
674 return path;
675 return MSOldCall(resource, type);
676 }
677 // }}}
678
679 static void $drawLabel$(NSString *label, CGRect rect, NSString *style, NSString *custom) {
680 bool ellipsis(false);
681 float max = rect.size.width - 11, width;
682 width:
683 width = [(ellipsis ? [label stringByAppendingString:@"..."] : label) sizeWithStyle:style forWidth:320].width;
684
685 if (width > max) {
686 size_t length([label length]);
687 float spacing((width - max) / (length - 1));
688
689 if (spacing > 1.25) {
690 ellipsis = true;
691 label = [label substringToIndex:(length - 1)];
692 goto width;
693 }
694
695 style = [style stringByAppendingString:[NSString stringWithFormat:@"letter-spacing: -%f; ", spacing]];
696 }
697
698 if (ellipsis)
699 label = [label stringByAppendingString:@"..."];
700
701 if (custom != nil)
702 style = [style stringByAppendingString:custom];
703
704 CGSize size = [label sizeWithStyle:style forWidth:rect.size.width];
705 [label drawAtPoint:CGPointMake((rect.size.width - size.width) / 2, 0) withStyle:style];
706 }
707
708 static struct WBStringDrawingState {
709 WBStringDrawingState *next_;
710 unsigned count_;
711 NSString *base_;
712 NSString *info_;
713 } *stringDrawingState_;
714
715 MSInstanceMessageHook6(CGSize, NSString, drawAtPoint,forWidth,withFont,lineBreakMode,letterSpacing,includeEmoji, CGPoint, point, float, width, UIFont *, font, UILineBreakMode, mode, float, spacing, BOOL, emoji) {
716 WBStringDrawingState *state(stringDrawingState_);
717 if (state == NULL)
718 return MSOldCall(point, width, font, mode, spacing, emoji);
719
720 if (--state->count_ == 0)
721 stringDrawingState_ = state->next_;
722 if (state->info_ == nil)
723 return MSOldCall(point, width, font, mode, spacing, emoji);
724
725 NSString *info([Info_ objectForKey:state->info_]);
726 if (info == nil)
727 return MSOldCall(point, width, font, mode, spacing, emoji);
728
729 NSString *base(state->base_ ?: @"");
730 NSString *extra([NSString stringWithFormat:@"letter-spacing: %gpx", spacing]);
731 [self drawAtPoint:point withStyle:[NSString stringWithFormat:@"%@;%@;%@;%@", [font markupDescription], extra, base, info]];
732 return CGSizeZero;
733 }
734
735 MSInstanceMessageHook7(CGSize, NSString, _drawInRect,withFont,lineBreakMode,alignment,lineSpacing,includeEmoji,truncationRect, CGRect, rect, UIFont *, font, UILineBreakMode, mode, UITextAlignment, alignment, float, spacing, BOOL, emoji, CGRect, truncation) {
736 WBStringDrawingState *state(stringDrawingState_);
737 if (state == NULL)
738 return MSOldCall(rect, font, mode, alignment, spacing, emoji, truncation);
739
740 if (--state->count_ == 0)
741 stringDrawingState_ = state->next_;
742 if (state->info_ == nil)
743 return MSOldCall(rect, font, mode, alignment, spacing, emoji, truncation);
744
745 NSString *info([Info_ objectForKey:state->info_]);
746 if (info == nil)
747 return MSOldCall(rect, font, mode, alignment, spacing, emoji, truncation);
748
749 NSString *textAlign;
750 switch (alignment) {
751 default:
752 case UITextAlignmentLeft:
753 textAlign = @"left";
754 break;
755 case UITextAlignmentCenter:
756 textAlign = @"center";
757 break;
758 case UITextAlignmentRight:
759 textAlign = @"right";
760 break;
761 }
762
763 NSString *base(state->base_ ?: @"");
764 NSString *extra([NSString stringWithFormat:@"text-align: %@", textAlign]);
765 [self drawInRect:rect withStyle:[NSString stringWithFormat:@"%@;%@;%@;%@", [font markupDescription], extra, base, info]];
766 return CGSizeZero;
767 }
768
769 MSInstanceMessageHook4(CGSize, NSString, sizeWithFont,forWidth,lineBreakMode,letterSpacing, UIFont *, font, float, width, UILineBreakMode, mode, float, spacing) {
770 WBStringDrawingState *state(stringDrawingState_);
771 if (state == NULL)
772 return MSOldCall(font, width, mode, spacing);
773
774 if (--state->count_ == 0)
775 stringDrawingState_ = state->next_;
776 if (state->info_ == nil)
777 return MSOldCall(font, width, mode, spacing);
778
779 NSString *info([Info_ objectForKey:state->info_]);
780 if (info == nil)
781 return MSOldCall(font, width, mode, spacing);
782
783 NSString *base(state->base_ ?: @"");
784 NSString *extra([NSString stringWithFormat:@"letter-spacing: %gpx", spacing]);
785 return [self sizeWithStyle:[NSString stringWithFormat:@"%@;%@;%@;%@", [font markupDescription], extra, base, info] forWidth:width];
786 }
787
788 MSInstanceMessageHook1(CGSize, NSString, sizeWithFont, UIFont *, font) {
789 WBStringDrawingState *state(stringDrawingState_);
790 if (state == NULL)
791 return MSOldCall(font);
792
793 if (--state->count_ == 0)
794 stringDrawingState_ = state->next_;
795 if (state->info_ == nil)
796 return MSOldCall(font);
797
798 NSString *info([Info_ objectForKey:state->info_]);
799 if (info == nil)
800 return MSOldCall(font);
801
802 NSString *base(state->base_ ?: @"");
803 return [self sizeWithStyle:[NSString stringWithFormat:@"%@;%@;%@", [font markupDescription], base, info] forWidth:65535];
804 }
805
806 MSInstanceMessageHook1(UIImage *, SBIconBadgeFactory, checkoutBadgeImageForText, NSString *, text) {
807 WBStringDrawingState badgeState = {NULL, 1, @""
808 "color: white;"
809 , @"BadgeStyle"};
810
811 stringDrawingState_ = &badgeState;
812
813 UIImage *image(MSOldCall(text));
814
815 stringDrawingState_ = NULL;
816 return image;
817 }
818
819 MSInstanceMessageHook1(UIImage *, SBCalendarApplicationIcon, generateIconImage, int, type) {
820 WBStringDrawingState dayState = {NULL, 2, @""
821 "color: white;"
822 // XXX: this is only correct on an iPod dock
823 "text-shadow: rgba(0, 0, 0, 0.2) -1px -1px 2px;"
824 , @"CalendarIconDayStyle"};
825
826 WBStringDrawingState sizeState = {&dayState, 7, nil, nil};
827
828 WBStringDrawingState dateState = {&sizeState, 2, @""
829 "color: #333333;"
830 , @"CalendarIconDateStyle"};
831
832 stringDrawingState_ = &dateState;
833
834 UIImage *image(MSOldCall(type));
835
836 stringDrawingState_ = NULL;
837 return image;
838 }
839
840 MSHook(void, SBCalendarIconContentsView$drawRect$, SBCalendarIconContentsView *self, SEL sel, CGRect rect) {
841 NSBundle *bundle([NSBundle mainBundle]);
842
843 CFLocaleRef locale(CFLocaleCopyCurrent());
844 CFDateFormatterRef formatter(CFDateFormatterCreate(NULL, locale, kCFDateFormatterNoStyle, kCFDateFormatterNoStyle));
845 CFRelease(locale);
846
847 CFDateRef now(CFDateCreate(NULL, CFAbsoluteTimeGetCurrent()));
848
849 CFDateFormatterSetFormat(formatter, (CFStringRef) [bundle localizedStringForKey:@"CALENDAR_ICON_DAY_NUMBER_FORMAT" value:@"d" table:@"SpringBoard"]);
850 CFStringRef date(CFDateFormatterCreateStringWithDate(NULL, formatter, now));
851 CFDateFormatterSetFormat(formatter, (CFStringRef) [bundle localizedStringForKey:@"CALENDAR_ICON_DAY_NAME_FORMAT" value:@"cccc" table:@"SpringBoard"]);
852 CFStringRef day(CFDateFormatterCreateStringWithDate(NULL, formatter, now));
853
854 CFRelease(now);
855
856 CFRelease(formatter);
857
858 NSString *datestyle([@""
859 "font-family: Helvetica; "
860 "font-weight: bold; "
861 "color: #333333; "
862 "alpha: 1.0; "
863 "" stringByAppendingString:(IsWild_
864 ? @"font-size: 54px; "
865 : @"font-size: 39px; "
866 )]);
867
868 NSString *daystyle([@""
869 "font-family: Helvetica; "
870 "font-weight: bold; "
871 "color: white; "
872 "text-shadow: rgba(0, 0, 0, 0.2) -1px -1px 2px; "
873 "" stringByAppendingString:(IsWild_
874 ? @"font-size: 11px; "
875 : @"font-size: 9px; "
876 )]);
877
878 if (NSString *style = [Info_ objectForKey:@"CalendarIconDateStyle"])
879 datestyle = [datestyle stringByAppendingString:style];
880 if (NSString *style = [Info_ objectForKey:@"CalendarIconDayStyle"])
881 daystyle = [daystyle stringByAppendingString:style];
882
883 float width([self bounds].size.width);
884 float leeway(10);
885 CGSize datesize = [(NSString *)date sizeWithStyle:datestyle forWidth:(width + leeway)];
886 CGSize daysize = [(NSString *)day sizeWithStyle:daystyle forWidth:(width + leeway)];
887
888 unsigned base0(IsWild_ ? 89 : 70);
889 if ($GSFontGetUseLegacyFontMetrics())
890 base0 = base0 + 1;
891 unsigned base1(IsWild_ ? 18 : 16);
892
893 if (Four_) {
894 ++base0;
895 ++base1;
896 }
897
898 [(NSString *)date drawAtPoint:CGPointMake(
899 (width + 1 - datesize.width) / 2, (base0 - datesize.height) / 2
900 ) withStyle:datestyle];
901
902 [(NSString *)day drawAtPoint:CGPointMake(
903 (width + 1 - daysize.width) / 2, (base1 - daysize.height) / 2
904 ) withStyle:daystyle];
905
906 CFRelease(date);
907 CFRelease(day);
908 }
909
910 // %hook -[{NavigationBar,Toolbar} setBarStyle:] {{{
911 void $setBarStyle$_(NSString *name, int &style) {
912 if (Debug_)
913 NSLog(@"WB:Debug:%@Style:%d", name, style);
914 NSNumber *number = nil;
915 if (number == nil)
916 number = [Info_ objectForKey:[NSString stringWithFormat:@"%@Style-%d", name, style]];
917 if (number == nil)
918 number = [Info_ objectForKey:[NSString stringWithFormat:@"%@Style", name]];
919 if (number != nil) {
920 style = [number intValue];
921 if (Debug_)
922 NSLog(@"WB:Debug:%@Style=%d", name, style);
923 }
924 }
925
926 MSInstanceMessageHook1(void, UIToolbar, setBarStyle, int, style) {
927 $setBarStyle$_(@"Toolbar", style);
928 return MSOldCall(style);
929 }
930
931 MSInstanceMessageHook1(void, UINavigationBar, setBarStyle, int, style) {
932 $setBarStyle$_(@"NavigationBar", style);
933 return MSOldCall(style);
934 }
935 // }}}
936
937 MSHook(void, SBButtonBar$didMoveToSuperview, UIView *self, SEL sel) {
938 [[self superview] setBackgroundColor:[UIColor clearColor]];
939 _SBButtonBar$didMoveToSuperview(self, sel);
940 }
941
942 MSHook(void, SBStatusBarContentsView$didMoveToSuperview, UIView *self, SEL sel) {
943 [[self superview] setBackgroundColor:[UIColor clearColor]];
944 _SBStatusBarContentsView$didMoveToSuperview(self, sel);
945 }
946
947 static NSArray *Wallpapers_;
948 static bool Papered_;
949 static bool Docked_;
950 static bool SMSBackgrounded_;
951 static NSString *WallpaperFile_;
952 static UIImageView *WallpaperImage_;
953 static UIWebDocumentView *WallpaperPage_;
954 static NSURL *WallpaperURL_;
955
956 #define _release(object) \
957 do if (object != nil) { \
958 [object release]; \
959 object = nil; \
960 } while (false)
961
962 static UIImage *$getImage$(NSString *path) {
963 UIImage *image([UIImage imageWithContentsOfFile:path]);
964
965 unsigned scale($getScale$(path));
966 if (scale != 1 && [image respondsToSelector:@selector(setScale)])
967 [image setScale:scale];
968
969 return image;
970 }
971
972 static UIImage *$getDefaultDesktopImage$() {
973 if (NSString *path = $getTheme$($useScale$([NSArray arrayWithObjects:@"LockBackground.png", @"LockBackground.jpg", nil])))
974 return $getImage$(path);
975 return nil;
976 }
977
978 MSClassMessageHook0(UIImage *, UIImage, defaultDesktopImage) {
979 return $getDefaultDesktopImage$() ?: MSOldCall();
980 }
981
982 MSInstanceMessageHook0(UIImage *, SBSlidingAlertDisplay, _defaultDesktopImage) {
983 return $getDefaultDesktopImage$() ?: MSOldCall();
984 }
985
986 MSInstanceMessageHook0(void, SBWallpaperView, resetCurrentImageToWallpaper) {
987 for (UIView *parent([self superview]); parent != nil; parent = [parent superview])
988 if ([parent isKindOfClass:$SBSlidingAlertDisplay]) {
989 if (UIImage *image = $getDefaultDesktopImage$()) {
990 [self setImage:image];
991 return;
992 }
993
994 break;
995 }
996
997 MSOldCall();
998 }
999
1000 // %hook -[SBUIController init] {{{
1001 MSInstanceMessageHook0(id, SBUIController, init) {
1002 self = MSOldCall();
1003 if (self == nil)
1004 return nil;
1005
1006 NSString *paper($getTheme$(Wallpapers_));
1007 if (paper != nil)
1008 paper = [paper stringByDeletingLastPathComponent];
1009
1010 {
1011 size_t size;
1012 sysctlbyname("hw.machine", NULL, &size, NULL, 0);
1013 char *machine = new char[size];
1014
1015 if (sysctlbyname("hw.machine", machine, &size, NULL, 0) == -1) {
1016 perror("sysctlbyname(\"hw.machine\", ?)");
1017 delete [] machine;
1018 machine = NULL;
1019 }
1020
1021 IsWild_ = machine != NULL && strncmp(machine, "iPad", 4) == 0;
1022 }
1023
1024 if (Debug_)
1025 NSLog(@"WB:Debug:Info = %@", [Info_ description]);
1026
1027 if (paper != nil) {
1028 UIImageView *&_wallpaperView(MSHookIvar<UIImageView *>(self, "_wallpaperView"));
1029 if (&_wallpaperView != NULL) {
1030 [_wallpaperView removeFromSuperview];
1031 [_wallpaperView release];
1032 _wallpaperView = nil;
1033 }
1034 }
1035
1036 UIView *&_contentLayer(MSHookIvar<UIView *>(self, "_contentLayer"));
1037 UIView *&_contentView(MSHookIvar<UIView *>(self, "_contentView"));
1038
1039 UIView **player;
1040 if (&_contentLayer != NULL)
1041 player = &_contentLayer;
1042 else if (&_contentView != NULL)
1043 player = &_contentView;
1044 else
1045 player = NULL;
1046 UIView *layer(player == NULL ? nil : *player);
1047
1048 UIWindow *window([[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]);
1049 UIView *content([[[UIView alloc] initWithFrame:[window frame]] autorelease]);
1050 [window setContentView:content];
1051
1052 UIWindow *&_window(MSHookIvar<UIWindow *>(self, "_window"));
1053 [window setBackgroundColor:[_window backgroundColor]];
1054 [_window setBackgroundColor:[UIColor clearColor]];
1055
1056 [window setLevel:-1000];
1057 [window setHidden:NO];
1058
1059 /*if (player != NULL)
1060 *player = content;*/
1061
1062 [content setBackgroundColor:[layer backgroundColor]];
1063 [layer setBackgroundColor:[UIColor clearColor]];
1064
1065 UIView *indirect;
1066 if (!SummerBoard_ || !IsWild_)
1067 indirect = content;
1068 else {
1069 CGRect bounds([content bounds]);
1070 bounds.origin.y = -30;
1071 indirect = [[[UIView alloc] initWithFrame:bounds] autorelease];
1072 [content addSubview:indirect];
1073 [indirect zoomToScale:2.4];
1074 }
1075
1076 _release(WallpaperFile_);
1077 _release(WallpaperImage_);
1078 _release(WallpaperPage_);
1079 _release(WallpaperURL_);
1080
1081 if (paper != nil) {
1082 NSArray *themes([NSArray arrayWithObject:paper]);
1083
1084 if (NSString *path = $getTheme$([NSArray arrayWithObject:@"Wallpaper.mp4"], themes)) {
1085 #if UseAVController
1086 NSError *error;
1087
1088 static AVController *controller_(nil);
1089 if (controller_ == nil) {
1090 AVQueue *queue([AVQueue avQueue]);
1091 controller_ = [[AVController avControllerWithQueue:queue error:&error] retain];
1092 }
1093
1094 AVQueue *queue([controller_ queue]);
1095
1096 UIView *video([[[UIView alloc] initWithFrame:[indirect bounds]] autorelease]);
1097 [controller_ setLayer:[video _layer]];
1098
1099 AVItem *item([[[AVItem alloc] initWithPath:path error:&error] autorelease]);
1100 [queue appendItem:item error:&error];
1101
1102 [controller_ play:&error];
1103 #elif UseMPMoviePlayerController
1104 NSURL *url([NSURL fileURLWithPath:path]);
1105 MPMoviePlayerController *controller = [[$MPMoviePlayerController alloc] initWithContentURL:url];
1106 controller.movieControlMode = MPMovieControlModeHidden;
1107 [controller play];
1108 #else
1109 MPVideoView *video = [[[$MPVideoView alloc] initWithFrame:[indirect bounds]] autorelease];
1110 [video setMovieWithPath:path];
1111 [video setRepeatMode:1];
1112 [video setRepeatGap:-1];
1113 [video playFromBeginning];;
1114 #endif
1115
1116 [indirect addSubview:video];
1117 }
1118
1119 if (NSString *path = $getTheme$($useScale$([NSArray arrayWithObjects:@"Wallpaper.png", @"Wallpaper.jpg", nil]), themes)) {
1120 if (UIImage *image = $getImage$(path)) {
1121 WallpaperFile_ = [path retain];
1122 WallpaperImage_ = [[UIImageView alloc] initWithImage:image];
1123 if (NSNumber *number = [Info_ objectForKey:@"WallpaperAlpha"])
1124 [WallpaperImage_ setAlpha:[number floatValue]];
1125 [indirect addSubview:WallpaperImage_];
1126 }
1127 }
1128
1129 if (NSString *path = $getTheme$([NSArray arrayWithObject:@"Wallpaper.html"], themes)) {
1130 CGRect bounds = [indirect bounds];
1131
1132 UIWebDocumentView *view([[[UIWebDocumentView alloc] initWithFrame:bounds] autorelease]);
1133 [view setAutoresizes:true];
1134
1135 WallpaperPage_ = [view retain];
1136 WallpaperURL_ = [[NSURL fileURLWithPath:path] retain];
1137
1138 [WallpaperPage_ loadRequest:[NSURLRequest requestWithURL:WallpaperURL_]];
1139
1140 [view setBackgroundColor:[UIColor clearColor]];
1141 if ([view respondsToSelector:@selector(setDrawsBackground:)])
1142 [view setDrawsBackground:NO];
1143 [[view webView] setDrawsBackground:NO];
1144
1145 [indirect addSubview:view];
1146 }
1147 }
1148
1149 for (size_t i(0), e([Themes_ count]); i != e; ++i) {
1150 NSString *theme = [Themes_ objectAtIndex:(e - i - 1)];
1151 NSString *html = [theme stringByAppendingPathComponent:@"Widget.html"];
1152 if ([Manager_ fileExistsAtPath:html]) {
1153 CGRect bounds = [indirect bounds];
1154
1155 UIWebDocumentView *view([[[UIWebDocumentView alloc] initWithFrame:bounds] autorelease]);
1156 [view setAutoresizes:true];
1157
1158 NSURL *url = [NSURL fileURLWithPath:html];
1159 [view loadRequest:[NSURLRequest requestWithURL:url]];
1160
1161 [view setBackgroundColor:[UIColor clearColor]];
1162 if ([view respondsToSelector:@selector(setDrawsBackground:)])
1163 [view setDrawsBackground:NO];
1164 [[view webView] setDrawsBackground:NO];
1165
1166 [indirect addSubview:view];
1167 }
1168 }
1169
1170 return self;
1171 }
1172 // }}}
1173
1174 MSHook(void, SBAwayView$updateDesktopImage$, SBAwayView *self, SEL sel, UIImage *image) {
1175 NSString *path = $getTheme$([NSArray arrayWithObject:@"LockBackground.html"]);
1176 UIView *&_backgroundView(MSHookIvar<UIView *>(self, "_backgroundView"));
1177
1178 if (path != nil && _backgroundView != nil)
1179 path = nil;
1180
1181 _SBAwayView$updateDesktopImage$(self, sel, image);
1182
1183 if (path != nil) {
1184 CGRect bounds = [self bounds];
1185
1186 UIWebDocumentView *view([[[UIWebDocumentView alloc] initWithFrame:bounds] autorelease]);
1187 [view setAutoresizes:true];
1188
1189 if (WallpaperPage_ != nil)
1190 [WallpaperPage_ release];
1191 WallpaperPage_ = [view retain];
1192
1193 if (WallpaperURL_ != nil)
1194 [WallpaperURL_ release];
1195 WallpaperURL_ = [[NSURL fileURLWithPath:path] retain];
1196
1197 [WallpaperPage_ loadRequest:[NSURLRequest requestWithURL:WallpaperURL_]];
1198
1199 [[view webView] setDrawsBackground:false];
1200 [view setBackgroundColor:[UIColor clearColor]];
1201
1202 [self insertSubview:view aboveSubview:_backgroundView];
1203 }
1204 }
1205
1206 /*extern "C" CGColorRef CGGStateGetSystemColor(void *);
1207 extern "C" CGColorRef CGGStateGetFillColor(void *);
1208 extern "C" CGColorRef CGGStateGetStrokeColor(void *);
1209 extern "C" NSString *UIStyleStringFromColor(CGColorRef);*/
1210
1211 /* WBTimeLabel {{{ */
1212 @interface WBTimeLabel : NSProxy {
1213 NSString *time_;
1214 _transient SBStatusBarTimeView *view_;
1215 }
1216
1217 - (id) initWithTime:(NSString *)time view:(SBStatusBarTimeView *)view;
1218
1219 @end
1220
1221 @implementation WBTimeLabel
1222
1223 - (void) dealloc {
1224 [time_ release];
1225 [super dealloc];
1226 }
1227
1228 - (id) initWithTime:(NSString *)time view:(SBStatusBarTimeView *)view {
1229 time_ = [time retain];
1230 view_ = view;
1231 return self;
1232 }
1233
1234 - (NSString *) description {
1235 return time_;
1236 }
1237
1238 WBDelegate(time_)
1239
1240 - (CGSize) drawAtPoint:(CGPoint)point forWidth:(float)width withFont:(UIFont *)font lineBreakMode:(UILineBreakMode)mode {
1241 if (NSString *custom = [Info_ objectForKey:@"TimeStyle"]) {
1242 BOOL &_mode(MSHookIvar<BOOL>(view_, "_mode"));;
1243
1244 [time_ drawAtPoint:point withStyle:[NSString stringWithFormat:@""
1245 "font-family: Helvetica; "
1246 "font-weight: bold; "
1247 "font-size: 14px; "
1248 "color: %@; "
1249 "%@", _mode ? @"white" : @"black", custom]];
1250
1251 return CGSizeZero;
1252 }
1253
1254 return [time_ drawAtPoint:point forWidth:width withFont:font lineBreakMode:mode];
1255 }
1256
1257 @end
1258 /* }}} */
1259 /* WBBadgeLabel {{{ */
1260 @interface WBBadgeLabel : NSProxy {
1261 NSString *badge_;
1262 }
1263
1264 - (id) initWithBadge:(NSString *)badge;
1265 - (NSString *) description;
1266
1267 @end
1268
1269 @implementation WBBadgeLabel
1270
1271 - (void) dealloc {
1272 [badge_ release];
1273 [super dealloc];
1274 }
1275
1276 - (id) initWithBadge:(NSString *)badge {
1277 badge_ = [badge retain];
1278 return self;
1279 }
1280
1281 - (NSString *) description {
1282 return [badge_ description];
1283 }
1284
1285 WBDelegate(badge_)
1286
1287 - (CGSize) drawAtPoint:(CGPoint)point forWidth:(float)width withFont:(UIFont *)font lineBreakMode:(UILineBreakMode)mode {
1288 if (NSString *custom = [Info_ objectForKey:@"BadgeStyle"]) {
1289 [badge_ drawAtPoint:point withStyle:[NSString stringWithFormat:@""
1290 "font-family: Helvetica; "
1291 "font-weight: bold; "
1292 "font-size: 17px; "
1293 "color: white; "
1294 "%@", custom]];
1295
1296 return CGSizeZero;
1297 }
1298
1299 return [badge_ drawAtPoint:point forWidth:width withFont:font lineBreakMode:mode];
1300 }
1301
1302 @end
1303 /* }}} */
1304
1305 // IconAlpha {{{
1306 MSInstanceMessageHook1(void, SBIcon, setIconImageAlpha, float, alpha) {
1307 if (NSNumber *number = [Info_ objectForKey:@"IconAlpha"])
1308 alpha = [number floatValue];
1309 return MSOldCall(alpha);
1310 }
1311
1312 MSInstanceMessageHook1(void, SBIcon, setIconLabelAlpha, float, alpha) {
1313 if (NSNumber *number = [Info_ objectForKey:@"IconAlpha"])
1314 alpha = [number floatValue];
1315 return MSOldCall(alpha);
1316 }
1317
1318 MSInstanceMessageHook0(id, SBIcon, initWithDefaultSize) {
1319 if ((self = MSOldCall()) != nil) {
1320 if (NSNumber *number = [Info_ objectForKey:@"IconAlpha"]) {
1321 // XXX: note: this is overridden above, which is silly
1322 float alpha([number floatValue]);
1323 [self setIconImageAlpha:alpha];
1324 [self setIconLabelAlpha:alpha];
1325 }
1326 } return self;
1327 }
1328
1329 MSInstanceMessageHook1(void, SBIcon, setAlpha, float, alpha) {
1330 if (NSNumber *number = [Info_ objectForKey:@"IconAlpha"])
1331 alpha = [number floatValue];
1332 return MSOldCall(alpha);
1333 }
1334 // }}}
1335
1336 MSHook(id, SBIconBadge$initWithBadge$, SBIconBadge *self, SEL sel, NSString *badge) {
1337 if ((self = _SBIconBadge$initWithBadge$(self, sel, badge)) != nil) {
1338 id &_badge(MSHookIvar<id>(self, "_badge"));
1339 if (_badge != nil)
1340 if (id label = [[WBBadgeLabel alloc] initWithBadge:[_badge autorelease]])
1341 _badge = label;
1342 } return self;
1343 }
1344
1345 void SBStatusBarController$setStatusBarMode(int &mode) {
1346 if (Debug_)
1347 NSLog(@"WB:Debug:setStatusBarMode:%d", mode);
1348 if (mode < 100) // 104:hidden 105:glowing
1349 if (NSNumber *number = [Info_ objectForKey:@"StatusBarMode"])
1350 mode = [number intValue];
1351 }
1352
1353 /*MSHook(void, SBStatusBarController$setStatusBarMode$orientation$duration$animation$, SBStatusBarController *self, SEL sel, int mode, int orientation, double duration, int animation) {
1354 NSLog(@"mode:%d orientation:%d duration:%f animation:%d", mode, orientation, duration, animation);
1355 SBStatusBarController$setStatusBarMode(mode);
1356 return _SBStatusBarController$setStatusBarMode$orientation$duration$animation$(self, sel, mode, orientation, duration, animation);
1357 }*/
1358
1359 MSHook(void, SBStatusBarController$setStatusBarMode$orientation$duration$fenceID$animation$, SBStatusBarController *self, SEL sel, int mode, int orientation, float duration, int fenceID, int animation) {
1360 //NSLog(@"mode:%d orientation:%d duration:%f fenceID:%d animation:%d", mode, orientation, duration, fenceID, animation);
1361 SBStatusBarController$setStatusBarMode(mode);
1362 return _SBStatusBarController$setStatusBarMode$orientation$duration$fenceID$animation$(self, sel, mode, orientation, duration, fenceID, animation);
1363 }
1364
1365 MSHook(void, SBStatusBarController$setStatusBarMode$orientation$duration$fenceID$animation$startTime$, SBStatusBarController *self, SEL sel, int mode, int orientation, double duration, int fenceID, int animation, double startTime) {
1366 //NSLog(@"mode:%d orientation:%d duration:%f fenceID:%d animation:%d startTime:%f", mode, orientation, duration, fenceID, animation, startTime);
1367 SBStatusBarController$setStatusBarMode(mode);
1368 //NSLog(@"mode=%u", mode);
1369 return _SBStatusBarController$setStatusBarMode$orientation$duration$fenceID$animation$startTime$(self, sel, mode, orientation, duration, fenceID, animation, startTime);
1370 }
1371
1372 /*MSHook(id, SBStatusBarContentsView$initWithStatusBar$mode$, SBStatusBarContentsView *self, SEL sel, id bar, int mode) {
1373 if (NSNumber *number = [Info_ objectForKey:@"StatusBarContentsMode"])
1374 mode = [number intValue];
1375 return _SBStatusBarContentsView$initWithStatusBar$mode$(self, sel, bar, mode);
1376 }*/
1377
1378 MSHook(NSString *, SBStatusBarOperatorNameView$operatorNameStyle, SBStatusBarOperatorNameView *self, SEL sel) {
1379 NSString *style(_SBStatusBarOperatorNameView$operatorNameStyle(self, sel));
1380 if (Debug_)
1381 NSLog(@"operatorNameStyle= %@", style);
1382 if (NSString *custom = [Info_ objectForKey:@"OperatorNameStyle"])
1383 style = [NSString stringWithFormat:@"%@; %@", style, custom];
1384 return style;
1385 }
1386
1387 MSHook(void, SBStatusBarOperatorNameView$setOperatorName$fullSize$, SBStatusBarOperatorNameView *self, SEL sel, NSString *name, BOOL full) {
1388 if (Debug_)
1389 NSLog(@"setOperatorName:\"%@\" fullSize:%u", name, full);
1390 return _SBStatusBarOperatorNameView$setOperatorName$fullSize$(self, sel, name, NO);
1391 }
1392
1393 // XXX: replace this with [SBStatusBarTimeView tile]
1394 MSHook(void, SBStatusBarTimeView$drawRect$, SBStatusBarTimeView *self, SEL sel, CGRect rect) {
1395 id &_time(MSHookIvar<id>(self, "_time"));
1396 if (_time != nil && [_time class] != [WBTimeLabel class])
1397 object_setInstanceVariable(self, "_time", reinterpret_cast<void *>([[WBTimeLabel alloc] initWithTime:[_time autorelease] view:self]));
1398 return _SBStatusBarTimeView$drawRect$(self, sel, rect);
1399 }
1400
1401 @interface UIView (WinterBoard)
1402 - (bool) wb$isWBImageView;
1403 - (void) wb$logHierarchy;
1404 - (void) wb$setBackgroundColor:(UIColor *)color;
1405 @end
1406
1407 @implementation UIView (WinterBoard)
1408
1409 - (bool) wb$isWBImageView {
1410 return false;
1411 }
1412
1413 - (void) wb$logHierarchy {
1414 WBLogHierarchy(self);
1415 }
1416
1417 - (void) wb$setBackgroundColor:(UIColor *)color {
1418 [self setBackgroundColor:color];
1419 for (UIView *child in [self subviews])
1420 [child wb$setBackgroundColor:color];
1421 }
1422
1423 @end
1424
1425 @interface WBImageView : UIImageView {
1426 }
1427
1428 - (bool) wb$isWBImageView;
1429 - (void) wb$updateFrame;
1430 @end
1431
1432 @implementation WBImageView
1433
1434 - (bool) wb$isWBImageView {
1435 return true;
1436 }
1437
1438 - (void) wb$updateFrame {
1439 CGRect frame([self frame]);
1440 frame.origin.y = 0;
1441
1442 for (UIView *view(self); ; ) {
1443 view = [view superview];
1444 if (view == nil)
1445 break;
1446 frame.origin.y -= [view frame].origin.y;
1447 }
1448
1449 [self setFrame:frame];
1450 }
1451
1452 @end
1453
1454 MSHook(void, SBIconList$setFrame$, SBIconList *self, SEL sel, CGRect frame) {
1455 NSArray *subviews([self subviews]);
1456 WBImageView *view([subviews count] == 0 ? nil : [subviews objectAtIndex:0]);
1457 if (view != nil && [view wb$isWBImageView])
1458 [view wb$updateFrame];
1459 _SBIconList$setFrame$(self, sel, frame);
1460 }
1461
1462 MSHook(void, SBIconController$noteNumberOfIconListsChanged, SBIconController *self, SEL sel) {
1463 SBIconModel *&_iconModel(MSHookIvar<SBIconModel *>(self, "_iconModel"));
1464 NSArray *lists([_iconModel iconLists]);
1465
1466 for (unsigned i(0), e([lists count]); i != e; ++i)
1467 if (NSString *path = $getTheme$([NSArray arrayWithObject:[NSString stringWithFormat:@"Page%u.png", i]])) {
1468 SBIconList *list([lists objectAtIndex:i]);
1469 NSArray *subviews([list subviews]);
1470
1471 WBImageView *view([subviews count] == 0 ? nil : [subviews objectAtIndex:0]);
1472 if (view == nil || ![view wb$isWBImageView]) {
1473 view = [[[WBImageView alloc] init] autorelease];
1474 [list insertSubview:view atIndex:0];
1475 }
1476
1477 UIImage *image([UIImage imageWithContentsOfFile:path]);
1478
1479 CGRect frame([view frame]);
1480 frame.size = [image size];
1481 [view setFrame:frame];
1482
1483 [view setImage:image];
1484 [view wb$updateFrame];
1485 }
1486
1487 return _SBIconController$noteNumberOfIconListsChanged(self, sel);
1488 }
1489
1490 MSHook(id, SBIconLabel$initWithSize$label$, SBIconLabel *self, SEL sel, CGSize size, NSString *label) {
1491 self = _SBIconLabel$initWithSize$label$(self, sel, size, label);
1492 if (self != nil)
1493 [self setClipsToBounds:NO];
1494 return self;
1495 }
1496
1497 MSHook(void, SBIconLabel$setInDock$, SBIconLabel *self, SEL sel, BOOL docked) {
1498 static bool gssc(false);
1499 if (!gssc) {
1500 BOOL (*GSSystemHasCapability)(CFStringRef) = reinterpret_cast<BOOL (*)(CFStringRef)>(dlsym(RTLD_DEFAULT, "GSSystemHasCapability"));
1501 Papered_ |= GSSystemHasCapability != NULL && GSSystemHasCapability(CFSTR("homescreen-wallpaper"));
1502 gssc = true;
1503
1504 if ([Info_ objectForKey:@"UndockedIconLabels"] == nil)
1505 [Info_ setObject:[NSNumber numberWithBool:(
1506 !Papered_ ||
1507 [Info_ objectForKey:@"DockedIconLabelStyle"] != nil ||
1508 [Info_ objectForKey:@"UndockedIconLabelStyle"] != nil
1509 )] forKey:@"UndockedIconLabels"];
1510 }
1511
1512 id &_label(MSHookIvar<id>(self, "_label"));
1513 if (![Info_ wb$boolForKey:@"UndockedIconLabels"])
1514 docked = true;
1515
1516 if (_label != nil && [_label respondsToSelector:@selector(setInDock:)])
1517 [_label setInDock:docked];
1518
1519 _SBIconLabel$setInDock$(self, sel, docked);
1520 [self setNeedsDisplay];
1521 }
1522
1523 MSHook(BOOL, SBDockIconListView$shouldShowNewDock, id self, SEL sel) {
1524 return SummerBoard_ && Docked_ ? NO : _SBDockIconListView$shouldShowNewDock(self, sel);
1525 }
1526
1527 MSHook(void, SBDockIconListView$setFrame$, id self, SEL sel, CGRect frame) {
1528 _SBDockIconListView$setFrame$(self, sel, frame);
1529 }
1530
1531 // %hook -[NSBundle localizedStringForKey:value:table:] {{{
1532 MSInstanceMessageHook3(NSString *, NSBundle, localizedStringForKey,value,table, NSString *, key, NSString *, value, NSString *, table) {
1533 NSString *identifier = [self bundleIdentifier];
1534 NSLocale *locale = [NSLocale currentLocale];
1535 NSString *language = [locale objectForKey:NSLocaleLanguageCode];
1536 if (Debug_)
1537 NSLog(@"WB:Debug:[NSBundle(%@) localizedStringForKey:\"%@\" value:\"%@\" table:\"%@\"] (%@)", identifier, key, value, table, language);
1538 NSString *file = table == nil ? @"Localizable" : table;
1539 NSString *name = [NSString stringWithFormat:@"%@:%@", identifier, file];
1540 NSDictionary *strings;
1541 if ((strings = [Strings_ objectForKey:name]) != nil) {
1542 if (static_cast<id>(strings) != [NSNull null]) strings:
1543 if (NSString *value = [strings objectForKey:key])
1544 return value;
1545 } else if (NSString *path = $pathForFile$inBundle$([NSString stringWithFormat:@"%@.lproj/%@.strings",
1546 language, file
1547 ], self, false)) {
1548 if ((strings = [[NSDictionary alloc] initWithContentsOfFile:path]) != nil) {
1549 [Strings_ setObject:[strings autorelease] forKey:name];
1550 goto strings;
1551 } else goto null;
1552 } else null:
1553 [Strings_ setObject:[NSNull null] forKey:name];
1554 return MSOldCall(key, value, table);
1555 }
1556 // }}}
1557 // %hook -[WebCoreFrameBridge renderedSizeOfNode:constrainedToWidth:] {{{
1558 MSClassHook(WebCoreFrameBridge)
1559
1560 MSInstanceMessageHook2(CGSize, WebCoreFrameBridge, renderedSizeOfNode,constrainedToWidth, id, node, float, width) {
1561 if (node == nil)
1562 return CGSizeZero;
1563 void **core(reinterpret_cast<void **>([node _node]));
1564 if (core == NULL || core[6] == NULL)
1565 return CGSizeZero;
1566 return MSOldCall(node, width);
1567 }
1568 // }}}
1569
1570 MSInstanceMessage1(void, SBIconLabel, drawRect, CGRect, rect) {
1571 static Ivar drawMoreLegibly = object_getInstanceVariable(self, "_drawMoreLegibly", NULL);
1572
1573 int docked;
1574 Ivar ivar = object_getInstanceVariable(self, "_inDock", reinterpret_cast<void **>(&docked));
1575 docked = (docked & (ivar_getOffset(ivar) == ivar_getOffset(drawMoreLegibly) ? 0x2 : 0x1)) != 0;
1576
1577 NSString *label(MSHookIvar<NSString *>(self, "_label"));
1578
1579 NSString *style = [NSString stringWithFormat:@""
1580 "font-family: Helvetica; "
1581 "font-weight: bold; "
1582 "color: %@; %@"
1583 "", (docked || !SummerBoard_ ? @"white" : @"#b3b3b3"), (IsWild_
1584 ? @"font-size: 12px; "
1585 : @"font-size: 11px; "
1586 )];
1587
1588 if (IsWild_)
1589 style = [style stringByAppendingString:@"text-shadow: rgba(0, 0, 0, 0.5) 0px 1px 0px; "];
1590 else if (docked)
1591 style = [style stringByAppendingString:@"text-shadow: rgba(0, 0, 0, 0.5) 0px -1px 0px; "];
1592
1593 NSString *custom([Info_ objectForKey:(docked ? @"DockedIconLabelStyle" : @"UndockedIconLabelStyle")]);
1594
1595 $drawLabel$(label, [self bounds], style, custom);
1596 }
1597
1598 MSInstanceMessage0(CGImageRef, SBIconLabel, buildLabelImage) {
1599 bool docked((MSHookIvar<unsigned>(self, "_inDock") & 0x2) != 0);
1600
1601 WBStringDrawingState labelState = {NULL, 0, [NSString stringWithFormat:@""
1602 "color: %@;"
1603 ,
1604 (docked || !SummerBoard_ ? @"white" : @"#b3b3b3")
1605 ], docked ? @"DockedIconLabelStyle" : @"UndockedIconLabelStyle"};
1606
1607 stringDrawingState_ = &labelState;
1608
1609 CGImageRef image(MSOldCall());
1610
1611 stringDrawingState_ = NULL;
1612 return image;
1613 }
1614
1615 // ChatKit {{{
1616 MSInstanceMessageHook2(id, CKBalloonView, initWithFrame,delegate, CGRect, frame, id, delegate) {
1617 if ((self = MSOldCall(frame, delegate)) != nil) {
1618 [self setBackgroundColor:[UIColor clearColor]];
1619 } return self;
1620 }
1621
1622 MSInstanceMessageHook0(BOOL, CKBalloonView, _canUseLayerBackedBalloon) {
1623 return SMSBackgrounded_ ? NO : MSOldCall();
1624 }
1625
1626 MSInstanceMessageHook0(void, CKTranscriptHeaderView, layoutSubviews) {
1627 [self wb$setBackgroundColor:[UIColor clearColor]];
1628 return MSOldCall();
1629 }
1630
1631 MSInstanceMessageHook1(void, CKMessageCell, addBalloonView, CKBalloonView *, balloon) {
1632 MSOldCall(balloon);
1633 [balloon setBackgroundColor:[UIColor clearColor]];
1634 }
1635
1636 MSInstanceMessageHook1(void, CKTranscriptCell, setBackgroundColor, UIColor *, color) {
1637 MSOldCall([UIColor clearColor]);
1638 [[self contentView] wb$setBackgroundColor:[UIColor clearColor]];
1639 }
1640
1641 // iOS >= 5.0
1642 MSInstanceMessageHook2(id, CKTranscriptCell, initWithStyle,reuseIdentifier, int, style, NSString *, reuse) {
1643 if ((self = MSOldCall(style, reuse)) != nil) {
1644 [self setBackgroundColor:[UIColor clearColor]];
1645 [[self contentView] wb$setBackgroundColor:[UIColor clearColor]];
1646 } return self;
1647 }
1648
1649 // iOS << 5.0
1650 MSInstanceMessageHook2(id, CKMessageCell, initWithStyle,reuseIdentifier, int, style, NSString *, reuse) {
1651 if ((self = MSOldCall(style, reuse)) != nil) {
1652 [self setBackgroundColor:[UIColor clearColor]];
1653 [[self contentView] setBackgroundColor:[UIColor clearColor]];
1654 } return self;
1655 }
1656
1657 MSInstanceMessageHook2(id, CKTimestampView, initWithStyle,reuseIdentifier, int, style, NSString *, reuse) {
1658 if ((self = MSOldCall(style, reuse)) != nil) {
1659 UILabel *&_label(MSHookIvar<UILabel *>(self, "_label"));
1660 [_label setBackgroundColor:[UIColor clearColor]];
1661 } return self;
1662 }
1663
1664 MSInstanceMessageHook1(void, CKTranscriptTableView, setSeparatorStyle, int, style) {
1665 MSOldCall(UITableViewCellSeparatorStyleNone);
1666 }
1667
1668 MSInstanceMessageHook2(id, CKTranscriptTableView, initWithFrame,style, CGRect, frame, int, style) {
1669 if ((self = MSOldCall(frame, style)) != nil) {
1670 [self setSeparatorStyle:UITableViewCellSeparatorStyleNone];
1671 } return self;
1672 }
1673
1674 MSInstanceMessageHook0(void, CKTranscriptController, loadView) {
1675 MSOldCall();
1676
1677 if (NSString *path = $getTheme$($useScale$([NSArray arrayWithObjects:@"SMSBackground.png", @"SMSBackground.jpg", nil])))
1678 if (UIImage *image = $getImage$(path)) {
1679 SMSBackgrounded_ = true;
1680
1681 UIView *&_transcriptTable(MSHookIvar<UIView *>(self, "_transcriptTable"));
1682 UIView *&_transcriptLayer(MSHookIvar<UIView *>(self, "_transcriptLayer"));
1683 UIView *table;
1684 if (&_transcriptTable != NULL)
1685 table = _transcriptTable;
1686 else if (&_transcriptLayer != NULL)
1687 table = _transcriptLayer;
1688 else
1689 table = nil;
1690
1691 UIView *placard(table != nil ? [table superview] : MSHookIvar<UIView *>(self, "_backPlacard"));
1692 UIImageView *background([[[UIImageView alloc] initWithImage:image] autorelease]);
1693
1694 if (table == nil)
1695 [placard insertSubview:background atIndex:0];
1696 else {
1697 [table setBackgroundColor:[UIColor clearColor]];
1698 [placard insertSubview:background belowSubview:table];
1699 }
1700 }
1701 }
1702 // }}}
1703
1704 // %hook _UIImageWithName() {{{
1705 MSHook(UIImage *, _UIImageWithName, NSString *name) {
1706 if (Debug_)
1707 NSLog(@"WB:Debug: _UIImageWithName(\"%@\")", name);
1708 if (name == nil)
1709 return nil;
1710
1711 int identifier;
1712 bool packed;
1713
1714 if (_UIPackedImageTableGetIdentifierForName != NULL)
1715 packed = _UIPackedImageTableGetIdentifierForName(name, &identifier);
1716 else if (_UISharedImageNameGetIdentifier != NULL) {
1717 identifier = _UISharedImageNameGetIdentifier(name);
1718 packed = identifier != -1;
1719 } else {
1720 identifier = -1;
1721 packed = false;
1722 }
1723
1724 if (Debug_)
1725 NSLog(@"WB:Debug: _UISharedImageNameGetIdentifier(\"%@\") = %d", name, identifier);
1726
1727 if (!packed)
1728 return __UIImageWithName(name);
1729 else {
1730 NSNumber *key([NSNumber numberWithInt:identifier]);
1731 UIImage *image([UIImages_ objectForKey:key]);
1732 if (image != nil)
1733 return reinterpret_cast<id>(image) == [NSNull null] ? __UIImageWithName(name) : image;
1734 if (NSString *path = $pathForFile$inBundle$(name, _UIKitBundle(), true))
1735 image = $getImage$(path);
1736 [UIImages_ setObject:(image == nil ? [NSNull null] : reinterpret_cast<id>(image)) forKey:key];
1737 if (image != nil)
1738 return image;
1739
1740 image = __UIImageWithName(name);
1741
1742 if (UIDebug_) {
1743 NSString *path([@"/tmp/UIImages/" stringByAppendingString:name]);
1744 if (![Manager_ fileExistsAtPath:path])
1745 [UIImagePNGRepresentation(image) writeToFile:path atomically:YES];
1746 }
1747
1748 return image;
1749 }
1750 }
1751 // }}}
1752 // %hook _UIImageWithNameInDomain() {{{
1753 MSHook(UIImage *, _UIImageWithNameInDomain, NSString *name, NSString *domain) {
1754 NSString *key([NSString stringWithFormat:@"D:%zu%@%@", [domain length], domain, name]);
1755 UIImage *image([PathImages_ objectForKey:key]);
1756 if (image != nil)
1757 return reinterpret_cast<id>(image) == [NSNull null] ? __UIImageWithNameInDomain(name, domain) : image;
1758 if (Debug_)
1759 NSLog(@"WB:Debug: UIImageWithNameInDomain(\"%@\", \"%@\")", name, domain);
1760 if (NSString *path = $getTheme$($useScale$([NSArray arrayWithObject:[NSString stringWithFormat:@"Domains/%@/%@", domain, name]])))
1761 image = $getImage$(path);
1762 [PathImages_ setObject:(image == nil ? [NSNull null] : reinterpret_cast<id>(image)) forKey:key];
1763 return image == nil ? __UIImageWithNameInDomain(name, domain) : image;
1764 }
1765 // }}}
1766
1767 // %hook GSFontCreateWithName() {{{
1768 MSHook(GSFontRef, GSFontCreateWithName, const char *name, GSFontSymbolicTraits traits, float size) {
1769 if (Debug_)
1770 NSLog(@"WB:Debug: GSFontCreateWithName(\"%s\", %f)", name, size);
1771 if (NSString *font = [Info_ objectForKey:[NSString stringWithFormat:@"FontName-%s", name]])
1772 name = [font UTF8String];
1773 //if (NSString *scale = [Info_ objectForKey:[NSString stringWithFormat:@"FontScale-%s", name]])
1774 // size *= [scale floatValue];
1775 return _GSFontCreateWithName(name, traits, size);
1776 }
1777 // }}}
1778
1779 #define AudioToolbox "/System/Library/Frameworks/AudioToolbox.framework/AudioToolbox"
1780 #define UIKit "/System/Library/Frameworks/UIKit.framework/UIKit"
1781
1782 bool (*_Z24GetFileNameForThisActionmPcRb)(unsigned long a0, char *a1, bool &a2);
1783
1784 MSHook(bool, _Z24GetFileNameForThisActionmPcRb, unsigned long a0, char *a1, bool &a2) {
1785 if (Debug_)
1786 NSLog(@"WB:Debug:GetFileNameForThisAction(%u, %p, %u)", a0, a1, a2);
1787 bool value = __Z24GetFileNameForThisActionmPcRb(a0, a1, a2);
1788 if (Debug_)
1789 NSLog(@"WB:Debug:GetFileNameForThisAction(%u, %s, %u) = %u", a0, value ? a1 : NULL, a2, value);
1790
1791 if (value) {
1792 NSString *path = [NSString stringWithUTF8String:a1];
1793 if ([path hasPrefix:@"/System/Library/Audio/UISounds/"]) {
1794 NSString *file = [path substringFromIndex:31];
1795 for (NSString *theme in Themes_) {
1796 NSString *path([NSString stringWithFormat:@"%@/UISounds/%@", theme, file]);
1797 if ([Manager_ fileExistsAtPath:path]) {
1798 strcpy(a1, [path UTF8String]);
1799 continue;
1800 }
1801 }
1802 }
1803 }
1804 return value;
1805 }
1806
1807 static void ChangeWallpaper(
1808 CFNotificationCenterRef center,
1809 void *observer,
1810 CFStringRef name,
1811 const void *object,
1812 CFDictionaryRef info
1813 ) {
1814 if (Debug_)
1815 NSLog(@"WB:Debug:ChangeWallpaper!");
1816
1817 UIImage *image;
1818 if (WallpaperFile_ != nil) {
1819 image = [[UIImage alloc] initWithContentsOfFile:WallpaperFile_];
1820 if (image != nil)
1821 image = [image autorelease];
1822 } else image = nil;
1823
1824 if (WallpaperImage_ != nil)
1825 [WallpaperImage_ setImage:image];
1826 if (WallpaperPage_ != nil)
1827 [WallpaperPage_ loadRequest:[NSURLRequest requestWithURL:WallpaperURL_]];
1828
1829 }
1830
1831 #define WBRename(name, sel, imp) \
1832 _ ## name ## $ ## imp = MSHookMessage($ ## name, @selector(sel), &$ ## name ## $ ## imp)
1833
1834 template <typename Type_>
1835 static void msset(Type_ &function, MSImageRef image, const char *name) {
1836 function = reinterpret_cast<Type_>(MSFindSymbol(image, name));
1837 }
1838
1839 template <typename Type_>
1840 static void nlset(Type_ &function, struct nlist *nl, size_t index) {
1841 struct nlist &name(nl[index]);
1842 uintptr_t value(name.n_value);
1843 if ((name.n_desc & N_ARM_THUMB_DEF) != 0)
1844 value |= 0x00000001;
1845 function = reinterpret_cast<Type_>(value);
1846 }
1847
1848 template <typename Type_>
1849 static void dlset(Type_ &function, const char *name) {
1850 function = reinterpret_cast<Type_>(dlsym(RTLD_DEFAULT, name));
1851 }
1852
1853 // %hook CGImageReadCreateWithFile() {{{
1854 MSHook(void *, CGImageReadCreateWithFile, NSString *path, int flag) {
1855 if (Debug_)
1856 NSLog(@"WB:Debug: CGImageReadCreateWithFile(%@, %d)", path, flag);
1857 NSAutoreleasePool *pool([[NSAutoreleasePool alloc] init]);
1858 void *value(_CGImageReadCreateWithFile([path wb$themedPath], flag));
1859 [pool release];
1860 return value;
1861 }
1862 // }}}
1863
1864 static void NSString$drawAtPoint$withStyle$(NSString *self, SEL _cmd, CGPoint point, NSString *style) {
1865 WKSetCurrentGraphicsContext(UIGraphicsGetCurrentContext());
1866 if (style == nil || [style length] == 0)
1867 style = @"font-family: Helvetica; font-size: 12px";
1868 return [[WBMarkup sharedMarkup] drawString:self atPoint:point withStyle:style];
1869 }
1870
1871 static void NSString$drawInRect$withStyle$(NSString *self, SEL _cmd, CGRect rect, NSString *style) {
1872 WKSetCurrentGraphicsContext(UIGraphicsGetCurrentContext());
1873 if (style == nil || [style length] == 0)
1874 style = @"font-family: Helvetica; font-size: 12px";
1875 return [[WBMarkup sharedMarkup] drawString:self inRect:rect withStyle:style];
1876 }
1877
1878 static CGSize NSString$sizeWithStyle$forWidth$(NSString *self, SEL _cmd, NSString *style, float width) {
1879 if (style == nil || [style length] == 0)
1880 style = @"font-family: Helvetica; font-size: 12px";
1881 return [[WBMarkup sharedMarkup] sizeOfString:self withStyle:style forWidth:width];
1882 }
1883
1884 static void SBInitialize() {
1885 class_addMethod($NSString, @selector(drawAtPoint:withStyle:), (IMP) &NSString$drawAtPoint$withStyle$, "v20@0:4{CGPoint=ff}8@16");
1886 class_addMethod($NSString, @selector(drawInRect:withStyle:), (IMP) &NSString$drawInRect$withStyle$, "v28@0:4{CGRect={CGSize=ff}{CGSize=ff}}8@24");
1887 class_addMethod($NSString, @selector(sizeWithStyle:forWidth:), (IMP) &NSString$sizeWithStyle$forWidth$, "{CGSize=ff}16@0:4@8f12");
1888
1889 if (SummerBoard_) {
1890 WBRename(SBApplication, pathForIcon, pathForIcon);
1891 WBRename(SBApplicationIcon, icon, icon);
1892 WBRename(SBApplicationIcon, generateIconImage:, generateIconImage$);
1893 }
1894
1895 WBRename(SBBookmarkIcon, icon, icon);
1896 WBRename(SBButtonBar, didMoveToSuperview, didMoveToSuperview);
1897 WBRename(SBCalendarIconContentsView, drawRect:, drawRect$);
1898 WBRename(SBIconBadge, initWithBadge:, initWithBadge$);
1899 WBRename(SBIconController, noteNumberOfIconListsChanged, noteNumberOfIconListsChanged);
1900
1901 WBRename(SBWidgetApplicationIcon, icon, icon);
1902
1903 WBRename(SBDockIconListView, setFrame:, setFrame$);
1904 MSHookMessage(object_getClass($SBDockIconListView), @selector(shouldShowNewDock), &$SBDockIconListView$shouldShowNewDock, &_SBDockIconListView$shouldShowNewDock);
1905
1906 if (kCFCoreFoundationVersionNumber < 600)
1907 WBRename(SBIconLabel, drawRect:, drawRect$);
1908 else
1909 WBRename(SBIconLabel, buildLabelImage, buildLabelImage);
1910
1911 WBRename(SBIconLabel, initWithSize:label:, initWithSize$label$);
1912 WBRename(SBIconLabel, setInDock:, setInDock$);
1913
1914 WBRename(SBIconList, setFrame:, setFrame$);
1915
1916 WBRename(SBIconModel, cacheImageForIcon:, cacheImageForIcon$);
1917 WBRename(SBIconModel, cacheImagesForIcon:, cacheImagesForIcon$);
1918 WBRename(SBIconModel, getCachedImagedForIcon:, getCachedImagedForIcon$);
1919 WBRename(SBIconModel, getCachedImagedForIcon:smallIcon:, getCachedImagedForIcon$smallIcon$);
1920
1921 WBRename(SBSearchView, initWithFrame:, initWithFrame$);
1922 WBRename(SBSearchTableViewCell, drawRect:, drawRect$);
1923 WBRename(SBSearchTableViewCell, initWithStyle:reuseIdentifier:, initWithStyle$reuseIdentifier$);
1924
1925 //WBRename(SBImageCache, initWithName:forImageWidth:imageHeight:initialCapacity:, initWithName$forImageWidth$imageHeight$initialCapacity$);
1926
1927 WBRename(SBAwayView, updateDesktopImage:, updateDesktopImage$);
1928 WBRename(SBStatusBarContentsView, didMoveToSuperview, didMoveToSuperview);
1929 //WBRename(SBStatusBarContentsView, initWithStatusBar:mode:, initWithStatusBar$mode$);
1930 //WBRename(SBStatusBarController, setStatusBarMode:orientation:duration:animation:, setStatusBarMode$orientation$duration$animation$);
1931 WBRename(SBStatusBarController, setStatusBarMode:orientation:duration:fenceID:animation:, setStatusBarMode$orientation$duration$fenceID$animation$);
1932 WBRename(SBStatusBarController, setStatusBarMode:orientation:duration:fenceID:animation:startTime:, setStatusBarMode$orientation$duration$fenceID$animation$startTime$);
1933 WBRename(SBStatusBarOperatorNameView, operatorNameStyle, operatorNameStyle);
1934 WBRename(SBStatusBarOperatorNameView, setOperatorName:fullSize:, setOperatorName$fullSize$);
1935 WBRename(SBStatusBarTimeView, drawRect:, drawRect$);
1936
1937 if (SummerBoard_)
1938 English_ = [[NSDictionary alloc] initWithContentsOfFile:@"/System/Library/CoreServices/SpringBoard.app/English.lproj/LocalizedApplicationNames.strings"];
1939 }
1940
1941 MSInitialize {
1942 NSAutoreleasePool *pool([[NSAutoreleasePool alloc] init]);
1943
1944 NSString *identifier([[NSBundle mainBundle] bundleIdentifier]);
1945 SpringBoard_ = [identifier isEqualToString:@"com.apple.springboard"];
1946
1947 Manager_ = [[NSFileManager defaultManager] retain];
1948 Themes_ = [[NSMutableArray alloc] initWithCapacity:8];
1949
1950 dlset(_GSFontGetUseLegacyFontMetrics, "GSFontGetUseLegacyFontMetrics");
1951
1952 // Load Settings.plist {{{
1953 if (NSDictionary *settings = [NSDictionary dictionaryWithContentsOfFile:[NSString stringWithFormat:@"/User/Library/Preferences/com.saurik.WinterBoard.plist"]]) {
1954 if (NSNumber *value = [settings objectForKey:@"SummerBoard"])
1955 SummerBoard_ = [value boolValue];
1956 if (NSNumber *value = [settings objectForKey:@"Debug"])
1957 Debug_ = [value boolValue];
1958 if (NSNumber *value = [settings objectForKey:@"RecordUI"])
1959 UIDebug_ = [value boolValue];
1960
1961 NSArray *themes([settings objectForKey:@"Themes"]);
1962 if (themes == nil)
1963 if (NSString *theme = [settings objectForKey:@"Theme"])
1964 themes = [NSArray arrayWithObject:[NSDictionary dictionaryWithObjectsAndKeys:
1965 theme, @"Name",
1966 [NSNumber numberWithBool:true], @"Active",
1967 nil]];
1968
1969 if (themes != nil)
1970 for (NSDictionary *theme in themes) {
1971 NSNumber *active([theme objectForKey:@"Active"]);
1972 if (![active boolValue])
1973 continue;
1974
1975 NSString *name([theme objectForKey:@"Name"]);
1976 if (name == nil)
1977 continue;
1978
1979 NSString *theme(nil);
1980
1981 #define testForTheme(format...) \
1982 if (theme == nil) { \
1983 NSString *path = [NSString stringWithFormat:format]; \
1984 if ([Manager_ fileExistsAtPath:path]) { \
1985 [Themes_ addObject:path]; \
1986 continue; \
1987 } \
1988 }
1989
1990 testForTheme(@"/Library/Themes/%@.theme", name)
1991 testForTheme(@"/Library/Themes/%@", name)
1992 testForTheme(@"%@/Library/SummerBoard/Themes/%@", NSHomeDirectory(), name)
1993
1994 }
1995 }
1996 // }}}
1997 // Merge Info.plist {{{
1998 Info_ = [[NSMutableDictionary dictionaryWithCapacity:16] retain];
1999
2000 for (NSString *theme in Themes_)
2001 if (NSDictionary *info = [NSDictionary dictionaryWithContentsOfFile:[NSString stringWithFormat:@"%@/Info.plist", theme]])
2002 for (NSString *key in [info allKeys])
2003 if ([Info_ objectForKey:key] == nil)
2004 [Info_ setObject:[info objectForKey:key] forKey:key];
2005 // }}}
2006
2007 // AudioToolbox {{{
2008 if (MSImageRef image = MSGetImageByName(AudioToolbox)) {
2009 msset(_Z24GetFileNameForThisActionmPcRb, image, "__Z24GetFileNameForThisActionmPcRb");
2010 MSHookFunction(_Z24GetFileNameForThisActionmPcRb, &$_Z24GetFileNameForThisActionmPcRb, &__Z24GetFileNameForThisActionmPcRb);
2011 }
2012 // }}}
2013 // GraphicsServices {{{
2014 if (true) {
2015 MSHookFunction(&GSFontCreateWithName, &$GSFontCreateWithName, &_GSFontCreateWithName);
2016 }
2017 // }}}
2018 // ImageIO {{{
2019 if (MSImageRef image = MSGetImageByName("/System/Library/Frameworks/ImageIO.framework/ImageIO")) {
2020 void *(*CGImageReadCreateWithFile)(NSString *, int);
2021 msset(CGImageReadCreateWithFile, image, "_CGImageReadCreateWithFile");
2022 MSHookFunction(CGImageReadCreateWithFile, MSHake(CGImageReadCreateWithFile));
2023 }
2024 // }}}
2025 // SpringBoard {{{
2026 if (SpringBoard_) {
2027 Wallpapers_ = [[NSArray arrayWithObjects:@"Wallpaper.mp4", @"Wallpaper@2x.png", @"Wallpaper@2x.jpg", @"Wallpaper.png", @"Wallpaper.jpg", @"Wallpaper.html", nil] retain];
2028 Papered_ = $getTheme$(Wallpapers_) != nil;
2029 Docked_ = $getTheme$([NSArray arrayWithObjects:@"Dock.png", nil]) != nil;
2030
2031 CFNotificationCenterAddObserver(
2032 CFNotificationCenterGetDarwinNotifyCenter(),
2033 NULL, &ChangeWallpaper, (CFStringRef) @"com.saurik.winterboard.lockbackground", NULL, 0
2034 );
2035
2036 if ($getTheme$([NSArray arrayWithObject:@"Wallpaper.mp4"]) != nil) {
2037 NSBundle *MediaPlayer([NSBundle bundleWithPath:@"/System/Library/Frameworks/MediaPlayer.framework"]);
2038 if (MediaPlayer != nil)
2039 [MediaPlayer load];
2040
2041 $MPMoviePlayerController = objc_getClass("MPMoviePlayerController");
2042 $MPVideoView = objc_getClass("MPVideoView");
2043 }
2044
2045 SBInitialize();
2046 }
2047 // }}}
2048 // UIKit {{{
2049 if ([NSBundle bundleWithIdentifier:@"com.apple.UIKit"] != nil) {
2050 struct nlist nl[6];
2051 memset(nl, 0, sizeof(nl));
2052 nl[0].n_un.n_name = (char *) "__UIApplicationImageWithName";
2053 nl[1].n_un.n_name = (char *) "__UIImageWithNameInDomain";
2054 nl[2].n_un.n_name = (char *) "__UIKitBundle";
2055 nl[3].n_un.n_name = (char *) "__UIPackedImageTableGetIdentifierForName";
2056 nl[4].n_un.n_name = (char *) "__UISharedImageNameGetIdentifier";
2057 nlist(UIKit, nl);
2058
2059 nlset(_UIApplicationImageWithName, nl, 0);
2060 nlset(_UIImageWithNameInDomain, nl, 1);
2061 nlset(_UIKitBundle, nl, 2);
2062 nlset(_UIPackedImageTableGetIdentifierForName, nl, 3);
2063 nlset(_UISharedImageNameGetIdentifier, nl, 4);
2064
2065 MSHookFunction(_UIApplicationImageWithName, &$_UIApplicationImageWithName, &__UIApplicationImageWithName);
2066 MSHookFunction(_UIImageWithName, &$_UIImageWithName, &__UIImageWithName);
2067 MSHookFunction(_UIImageWithNameInDomain, &$_UIImageWithNameInDomain, &__UIImageWithNameInDomain);
2068 }
2069 // }}}
2070
2071 if (UIDebug_ && ![Manager_ fileExistsAtPath:@"/tmp/UIImages"]) {
2072 NSError *error(nil);
2073 if (![Manager_ createDirectoryAtPath:@"/tmp/UIImages" withIntermediateDirectories:NO attributes:[NSDictionary dictionaryWithObjectsAndKeys:
2074 [NSNumber numberWithShort:0777], NSFilePosixPermissions,
2075 nil] error:&error])
2076 NSLog(@"WB:Error: cannot create /tmp/UIImages (%@)", error);
2077 }
2078
2079 [pool release];
2080 }