]> git.saurik.com Git - winterboard.git/blob - Library.mm
Finally remembering to commit stuff.
[winterboard.git] / Library.mm
1 /* WinterBoard - Theme Manager for the iPhone
2 * Copyright (C) 2008 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 #define _trace() NSLog(@"WB:_trace(%u)", __LINE__);
39
40 #include <objc/runtime.h>
41 #include <objc/message.h>
42
43 extern "C" {
44 #include <mach-o/nlist.h>
45 }
46
47 #import <Foundation/Foundation.h>
48 #import <CoreGraphics/CoreGraphics.h>
49
50 #import <UIKit/UIColor.h>
51 #import <UIKit/UIFont.h>
52 #import <UIKit/UIImage.h>
53 #import <UIKit/UIImageView.h>
54 #import <UIKit/UINavigationBarBackground.h>
55
56 #import <UIKit/NSString-UIStringDrawing.h>
57 #import <UIKit/NSString-UIStringDrawingDeprecated.h>
58
59 #import <UIKit/UIImage-UIImageDeprecated.h>
60
61 #import <UIKit/UIView-Geometry.h>
62 #import <UIKit/UIView-Hierarchy.h>
63 #import <UIKit/UIView-Rendering.h>
64
65 #import <SpringBoard/SBApplication.h>
66 #import <SpringBoard/SBAppWindow.h>
67 #import <SpringBoard/SBButtonBar.h>
68 #import <SpringBoard/SBContentLayer.h>
69 #import <SpringBoard/SBIconLabel.h>
70 #import <SpringBoard/SBStatusBarContentsView.h>
71 #import <SpringBoard/SBStatusBarTimeView.h>
72 #import <SpringBoard/SBUIController.h>
73
74 #import <CoreGraphics/CGGeometry.h>
75
76 @interface NSDictionary (WinterBoard)
77 - (UIColor *) colorForKey:(NSString *)key;
78 - (BOOL) boolForKey:(NSString *)key;
79 @end
80
81 @implementation NSDictionary (WinterBoard)
82
83 - (UIColor *) colorForKey:(NSString *)key {
84 NSString *value = [self objectForKey:key];
85 if (value == nil)
86 return nil;
87 /* XXX: incorrect */
88 return nil;
89 }
90
91 - (BOOL) boolForKey:(NSString *)key {
92 if (NSString *value = [self objectForKey:key])
93 return [value boolValue];
94 return NO;
95 }
96
97 @end
98
99 bool Debug_ = false;
100
101 /* WinterBoard Backend {{{ */
102 #define WBPrefix "wb_"
103
104 void WBInject(const char *classname, const char *oldname, IMP newimp, const char *type) {
105 Class _class = objc_getClass(classname);
106 if (_class == nil)
107 return;
108 if (!class_addMethod(_class, sel_registerName(oldname), newimp, type))
109 NSLog(@"WB:Error: failed to inject [%s %s]", classname, oldname);
110 }
111
112 void WBRename(bool instance, const char *classname, const char *oldname, IMP newimp) {
113 Class _class = objc_getClass(classname);
114 if (_class == nil) {
115 if (Debug_)
116 NSLog(@"WB:Warning: cannot find class [%s]", classname);
117 return;
118 }
119 if (!instance)
120 _class = object_getClass(_class);
121 Method method = class_getInstanceMethod(_class, sel_getUid(oldname));
122 if (method == nil) {
123 if (Debug_)
124 NSLog(@"WB:Warning: cannot find method [%s %s]", classname, oldname);
125 return;
126 }
127 size_t namelen = strlen(oldname);
128 char newname[sizeof(WBPrefix) + namelen];
129 memcpy(newname, WBPrefix, sizeof(WBPrefix) - 1);
130 memcpy(newname + sizeof(WBPrefix) - 1, oldname, namelen + 1);
131 const char *type = method_getTypeEncoding(method);
132 if (!class_addMethod(_class, sel_registerName(newname), method_getImplementation(method), type))
133 NSLog(@"WB:Error: failed to rename [%s %s]", classname, oldname);
134 unsigned int count;
135 Method *methods = class_copyMethodList(_class, &count);
136 for (unsigned int index(0); index != count; ++index)
137 if (methods[index] == method)
138 goto found;
139 if (newimp != NULL)
140 if (!class_addMethod(_class, sel_getUid(oldname), newimp, type))
141 NSLog(@"WB:Error: failed to rename [%s %s]", classname, oldname);
142 goto done;
143 found:
144 if (newimp != NULL)
145 method_setImplementation(method, newimp);
146 done:
147 free(methods);
148 }
149 /* }}} */
150
151 @protocol WinterBoard
152 - (NSString *) wb_pathForIcon;
153 - (NSString *) wb_pathForResource:(NSString *)resource ofType:(NSString *)type;
154 - (id) wb_init;
155 - (id) wb_layer;
156 - (id) wb_initWithSize:(CGSize)size;
157 - (id) wb_initWithSize:(CGSize)size label:(NSString *)label;
158 - (id) wb_initWithFrame:(CGRect)frame;
159 - (id) wb_initWithCoder:(NSCoder *)coder;
160 - (void) wb_setFrame:(CGRect)frame;
161 - (void) wb_setBackgroundColor:(id)color;
162 - (void) wb_setAlpha:(float)value;
163 - (void) wb_setBarStyle:(int)style;
164 - (id) wb_initWithFrame:(CGRect)frame withBarStyle:(int)style withTintColor:(UIColor *)color;
165 - (void) wb_setOpaque:(BOOL)opaque;
166 - (void) wb_setInDock:(BOOL)docked;
167 - (void) wb_didMoveToSuperview;
168 + (UIImage *) wb_imageNamed:(NSString *)name inBundle:(NSBundle *)bundle;
169 @end
170
171 NSMutableDictionary **ImageMap_;
172
173 NSFileManager *Manager_;
174 NSDictionary *English_;
175 NSDictionary *Info_;
176 NSString *theme_;
177 NSString *Wallpaper_;
178
179 NSString *SBApplication$pathForIcon(SBApplication<WinterBoard> *self, SEL sel) {
180 if (theme_ != nil) {
181 NSString *identifier = [self bundleIdentifier];
182
183 #define testForIcon(Name) \
184 if (NSString *name = Name) { \
185 NSString *path = [NSString stringWithFormat:@"%@/Icons/%@.png", theme_, name]; \
186 if ([Manager_ fileExistsAtPath:path]) \
187 return path; \
188 }
189
190 if (identifier != nil) {
191 NSString *path = [NSString stringWithFormat:@"%@/Bundles/%@/icon.png", theme_, identifier];
192 if ([Manager_ fileExistsAtPath:path])
193 return path;
194 }
195
196 testForIcon(identifier);
197 testForIcon([self displayName]);
198
199 if (NSString *display = [self displayIdentifier])
200 testForIcon([English_ objectForKey:display]);
201
202 /*if (NSDictionary *strings = [[NSDictionary alloc] initWithContentsOfFile:[NSString stringWithFormat:@"%@/English.lproj/InfoPlist.strings", [self path]]]) {
203 testForIcon([strings objectForKey:@"UISettingsDisplayName"]);
204
205 _trace();
206 if (NSString *bundle = [strings objectForKey:@"CFBundleName"]) {
207 if ([bundle hasPrefix:@"Mobile"]) {
208 NSLog(@"bd:%@:%@", bundle, [bundle substringFromIndex:6]);
209 testForIcon([bundle substringFromIndex:6]);
210 }
211 testForIcon(bundle);
212 }
213 }*/
214 }
215
216 return [self wb_pathForIcon];
217 }
218
219 NSString *$pathForFileInBundle$(NSString *file) {
220 if (theme_ != nil) {
221 NSString *path = [NSString stringWithFormat:@"%@/Bundles/%@", theme_, file];
222 if ([Manager_ fileExistsAtPath:path])
223 return path;
224
225 #define remapResourceName(oldname, newname) \
226 else if ([file isEqualToString:oldname]) { \
227 NSString *path = [NSString stringWithFormat:@"%@/%@.png", theme_, newname]; \
228 if ([Manager_ fileExistsAtPath:path]) \
229 return path; \
230 }
231
232 if (false);
233 remapResourceName(@"com.apple.springboard/FSO_BG.png", @"StatusBar")
234 remapResourceName(@"com.apple.springboard/SBDockBG.png", @"Dock")
235 remapResourceName(@"com.apple.springboard/SBWeatherCelsius.png", @"Icons/Weather")
236 }
237
238 return nil;
239 }
240
241 UIImage *UIImage$imageNamed$inBundle$(Class<WinterBoard> self, SEL sel, NSString *name, NSBundle *bundle) {
242 if (Debug_)
243 NSLog(@"WB:Debug: [UIImage(%@) imageNamed:\"%@\"]", [bundle bundleIdentifier], name);
244 if (NSString *identifier = [bundle bundleIdentifier])
245 if (NSString *path = $pathForFileInBundle$([NSString stringWithFormat:@"%@/%@", identifier, name]))
246 return [UIImage imageWithContentsOfFile:path];
247 return [self wb_imageNamed:name inBundle:bundle];
248 }
249
250 UIImage *UIImage$imageNamed$(Class<WinterBoard> self, SEL sel, NSString *name) {
251 return UIImage$imageNamed$inBundle$(self, sel, name, [NSBundle mainBundle]);
252 }
253
254 NSString *NSBundle$pathForResource$ofType$(NSBundle<WinterBoard> *self, SEL sel, NSString *resource, NSString *type) {
255 NSString *file = type == nil ? resource : [NSString stringWithFormat:@"%@.%@", resource, type];
256 if (Debug_)
257 NSLog(@"WB:Debug: [NSBundle(%@) pathForResource:\"%@\"]", [self bundleIdentifier], file);
258 if (NSString *identifier = [self bundleIdentifier])
259 if (NSString *path = $pathForFileInBundle$([NSString stringWithFormat:@"%@/%@", identifier, file]))
260 return path;
261 return [self wb_pathForResource:resource ofType:type];
262 }
263
264 void $setBackgroundColor$(id<WinterBoard> self, SEL sel, UIColor *color) {
265 if (Wallpaper_ != nil)
266 return [self wb_setBackgroundColor:[UIColor clearColor]];
267 return [self wb_setBackgroundColor:color];
268 }
269
270 /*id SBStatusBarContentsView$initWithFrame$(SBStatusBarContentsView<WinterBoard> *self, SEL sel, CGRect frame) {
271 self = [self wb_initWithFrame:frame];
272 if (self == nil)
273 return nil;
274
275 NSString *path = [NSString stringWithFormat:@"%@/StatusBar.png", theme_];
276 if ([Manager_ fileExistsAtPath:path])
277 [self addSubview:[[[UIImageView alloc] initWithImage:[UIImage imageWithContentsOfFile:path]] autorelease]];
278 //[self setBackgroundColor:[UIColor clearColor]];
279
280 return self;
281 }*/
282
283 bool UINavigationBar$setBarStyle$_(SBAppWindow<WinterBoard> *self) {
284 if (Info_ != nil) {
285 NSNumber *number = [Info_ objectForKey:@"NavigationBarStyle"];
286 if (number != nil) {
287 [self wb_setBarStyle:[number intValue]];
288 return true;
289 }
290 }
291
292 return false;
293 }
294
295 /*id UINavigationBarBackground$initWithFrame$withBarStyle$withTintColor$(UINavigationBarBackground<WinterBoard> *self, SEL sel, CGRect frame, int style, UIColor *tint) {
296 _trace();
297
298 if (Info_ != nil) {
299 NSNumber *number = [Info_ objectForKey:@"NavigationBarStyle"];
300 if (number != nil)
301 style = [number intValue];
302
303 UIColor *color = [Info_ colorForKey:@"NavigationBarTint"];
304 if (color != nil)
305 tint = color;
306 }
307
308 return [self wb_initWithFrame:frame withBarStyle:style withTintColor:tint];
309 }*/
310
311 /*id UINavigationBar$initWithCoder$(SBAppWindow<WinterBoard> *self, SEL sel, CGRect frame, NSCoder *coder) {
312 self = [self wb_initWithCoder:coder];
313 if (self == nil)
314 return nil;
315 UINavigationBar$setBarStyle$_(self);
316 return self;
317 }
318
319 id UINavigationBar$initWithFrame$(SBAppWindow<WinterBoard> *self, SEL sel, CGRect frame) {
320 self = [self wb_initWithFrame:frame];
321 if (self == nil)
322 return nil;
323 UINavigationBar$setBarStyle$_(self);
324 return self;
325 }*/
326
327 void UINavigationBar$setBarStyle$(SBAppWindow<WinterBoard> *self, SEL sel, int style) {
328 if (UINavigationBar$setBarStyle$_(self))
329 return;
330 return [self wb_setBarStyle:style];
331 }
332
333 void $didMoveToSuperview(SBButtonBar<WinterBoard> *self, SEL sel) {
334 [[self superview] setBackgroundColor:[UIColor clearColor]];
335 [self wb_didMoveToSuperview];
336 }
337
338 id SBContentLayer$initWithSize$(SBContentLayer<WinterBoard> *self, SEL sel, CGSize size) {
339 self = [self wb_initWithSize:size];
340 if (self == nil)
341 return nil;
342
343 if (Wallpaper_ != nil) {
344 if (UIImage *image = [[UIImage alloc] initWithContentsOfFile:Wallpaper_])
345 [self addSubview:[[[UIImageView alloc] initWithImage:image] autorelease]];
346 [self setBackgroundColor:[UIColor redColor]];
347 }
348
349 return self;
350 }
351
352 @interface WBIconLabel : NSProxy {
353 NSString *label_;
354 BOOL docked_;
355 }
356
357 - (id) initWithLabel:(NSString *)label;
358 - (void) setInDock:(BOOL)docked;
359
360 @end
361
362 @implementation WBIconLabel
363
364 - (void) dealloc {
365 [label_ release];
366 [super dealloc];
367 }
368
369 - (id) initWithLabel:(NSString *)label {
370 label_ = [label retain];
371 return self;
372 }
373
374 - (BOOL) respondsToSelector:(SEL)sel {
375 return
376 sel == @selector(setInDock:)
377 ? YES : [super respondsToSelector:sel];
378 }
379
380 - (NSMethodSignature*) methodSignatureForSelector:(SEL)sel {
381 if (NSMethodSignature *sig = [label_ methodSignatureForSelector:sel])
382 return sig;
383 NSLog(@"WB:Error: [WBIconLabel methodSignatureForSelector:(%s)]", sel_getName(sel));
384 return nil;
385 }
386
387 - (void) forwardInvocation:(NSInvocation*)inv {
388 SEL sel = [inv selector];
389 if ([label_ respondsToSelector:sel])
390 [inv invokeWithTarget:label_];
391 else
392 NSLog(@"WB:Error: [WBIconLabel forwardInvocation:(%s)]", sel_getName(sel));
393 }
394
395 - (NSString *) _iconLabelStyle {
396 return Info_ == nil ? nil : [Info_ objectForKey:(docked_ ? @"DockIconLabelStyle" : @"IconLabelStyle")];
397 }
398
399 - (CGSize) drawInRect:(CGRect)rect withFont:(UIFont *)font lineBreakMode:(int)mode alignment:(int)alignment {
400 if (NSString *custom = [self _iconLabelStyle]) {
401 [label_ drawInRect:rect withStyle:[NSString stringWithFormat:@"font-family: Helvetica; font-weight: bold; font-size: 11px; text-align: center; %@", custom]];
402 return CGSizeZero;
403 }
404
405 return [label_ drawInRect:rect withFont:font lineBreakMode:mode alignment:alignment];
406 }
407
408 - (void) drawInRect:(CGRect)rect withStyle:(NSString *)style {
409 if (NSString *custom = [self _iconLabelStyle])
410 return [label_ drawInRect:rect withStyle:[NSString stringWithFormat:@"%@; %@", style, custom]];
411 return [label_ drawInRect:rect withStyle:style];
412 }
413
414 - (void) setInDock:(BOOL)docked {
415 docked_ = docked;
416 }
417
418 @end
419
420 void SBIconLabel$setInDock$(SBIconLabel<WinterBoard> *self, SEL sel, BOOL docked) {
421 id label;
422 object_getInstanceVariable(self, "_label", (void **) &label);
423 if (Info_ == nil || [Info_ boolForKey:@"IconLabelInDock"])
424 docked = YES;
425 if (label != nil && [label respondsToSelector:@selector(setInDock:)])
426 [label setInDock:docked];
427 return [self wb_setInDock:docked];
428 }
429
430 id SBIconLabel$initWithSize$label$(SBIconLabel<WinterBoard> *self, SEL sel, CGSize size, NSString *label) {
431 return [self wb_initWithSize:size label:[[[WBIconLabel alloc] initWithLabel:label] autorelease]];
432 }
433
434 extern "C" void FindMappedImages(void);
435 extern "C" NSData *UIImagePNGRepresentation(UIImage *);
436
437 extern "C" void WBInitialize() {
438 NSLog(@"WB:Notice: Installing WinterBoard...");
439
440 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
441
442 struct nlist nl[3];
443 memset(nl, 0, sizeof(nl));
444 nl[0].n_un.n_name = (char *) "___mappedImages";
445 nl[1].n_un.n_name = (char *) "__UISharedImageInitialize";
446 nlist("/System/Library/Frameworks/UIKit.framework/UIKit", nl);
447 ImageMap_ = (id *) nl[0].n_value;
448 void (*__UISharedImageInitialize)(bool) = (void (*)(bool)) nl[1].n_value;
449
450 __UISharedImageInitialize(false);
451
452 /*NSArray *keys = [*ImageMap_ allKeys];
453 for (int i(0), e([keys count]); i != e; ++i) {
454 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
455 NSString *key = [keys objectAtIndex:i];
456 CGImageRef ref = (CGImageRef) [*ImageMap_ objectForKey:key];
457 UIImage *image = [UIImage imageWithCGImage:ref];
458 NSData *data = UIImagePNGRepresentation(image);
459 [data writeToFile:[NSString stringWithFormat:@"/tmp/pwnr/%@", key] atomically:YES];
460 [pool release];
461 }*/
462
463 English_ = [[NSDictionary alloc] initWithContentsOfFile:@"/System/Library/CoreServices/SpringBoard.app/English.lproj/LocalizedApplicationNames.strings"];
464 if (English_ != nil)
465 English_ = [English_ retain];
466
467 Manager_ = [[NSFileManager defaultManager] retain];
468
469 //WBRename("SBStatusBarContentsView", "setBackgroundColor:", (IMP) &$setBackgroundColor$);
470 //WBRename("UINavigationBar", "initWithFrame:", (IMP) &UINavigationBar$initWithFrame$);
471 //WBRename("UINavigationBar", "initWithCoder:", (IMP) &UINavigationBar$initWithCoder$);
472 WBRename(true, "UINavigationBar", "setBarStyle:", (IMP) &UINavigationBar$setBarStyle$);
473 //WBRename("UINavigationBarBackground", "initWithFrame:withBarStyle:withTintColor:", (IMP) &UINavigationBarBackground$initWithFrame$withBarStyle$withTintColor$);
474 //WBRename("SBStatusBarContentsView", "initWithFrame:", (IMP) &SBStatusBarContentsView$initWithFrame$);
475
476 WBRename(false, "UIImage", "imageNamed:inBundle:", (IMP) &UIImage$imageNamed$inBundle$);
477 WBRename(false, "UIImage", "imageNamed:", (IMP) &UIImage$imageNamed$);
478 WBRename(true, "SBApplication", "pathForIcon", (IMP) &SBApplication$pathForIcon);
479 WBRename(true, "NSBundle", "pathForResource:ofType:", (IMP) &NSBundle$pathForResource$ofType$);
480 WBRename(true, "SBContentLayer", "initWithSize:", (IMP) &SBContentLayer$initWithSize$);
481 WBRename(true, "SBStatusBarContentsView", "didMoveToSuperview", (IMP) &$didMoveToSuperview);
482 WBRename(true, "SBButtonBar", "didMoveToSuperview", (IMP) &$didMoveToSuperview);
483 WBRename(true, "SBIconLabel", "setInDock:", (IMP) &SBIconLabel$setInDock$);
484 WBRename(true, "SBIconLabel", "initWithSize:label:", (IMP) &SBIconLabel$initWithSize$label$);
485
486 if (NSDictionary *settings = [[NSDictionary alloc] initWithContentsOfFile:[NSString stringWithFormat:@"%@/Library/Preferences/com.saurik.WinterBoard.plist", NSHomeDirectory()]]) {
487 [settings autorelease];
488 NSString *name = [settings objectForKey:@"Theme"];
489 NSString *path;
490
491 if (theme_ == nil) {
492 path = [NSString stringWithFormat:@"%@/Library/SummerBoard/Themes/%@", NSHomeDirectory(), name];
493 if ([Manager_ fileExistsAtPath:path])
494 theme_ = [path retain];
495 }
496
497 if (theme_ == nil) {
498 path = [NSString stringWithFormat:@"/Library/Themes/%@", name];
499 if ([Manager_ fileExistsAtPath:path])
500 theme_ = [path retain];
501 }
502 }
503
504 if (theme_ != nil) {
505 NSString *path = [NSString stringWithFormat:@"%@/Wallpaper.png", theme_];
506 if ([Manager_ fileExistsAtPath:path])
507 Wallpaper_ = [path retain];
508
509 NSString *folder = [NSString stringWithFormat:@"%@/UIImages", theme_];
510 if (NSArray *images = [Manager_ contentsOfDirectoryAtPath:folder error:NULL])
511 for (int i(0), e = [images count]; i != e; ++i) {
512 NSString *name = [images objectAtIndex:i];
513 if (![name hasSuffix:@".png"])
514 continue;
515 NSString *path = [NSString stringWithFormat:@"%@/%@", folder, name];
516 UIImage *image = [UIImage imageWithContentsOfFile:path];
517 [*ImageMap_ setObject:(id)[image imageRef] forKey:name];
518 }
519
520 Info_ = [[NSDictionary alloc] initWithContentsOfFile:[NSString stringWithFormat:@"%@/Info.plist", theme_]];
521 if (Info_ == nil) {
522 //LabelColor_ = [UIColor whiteColor];
523 } else {
524 //LabelColor_ = [Info_ colorForKey:@"LabelColor"];
525 }
526 }
527
528 [pool release];
529 }