Showing posts with label Hack. Show all posts
Showing posts with label Hack. Show all posts

Wednesday, August 08, 2012

Prepare your apps for the iPhone 5

Earlier today, my friend Peter Steinberger asked:

Does anyone know what does “special iPhone Simulator tweaks” are? I’d love to test my stuff with different resolutions. http://9to5mac.com/2012/08/07/upcoming-ios-6-is-scalable-to-taller-640-x-1136-iphone-display-shows-possible-next-generation-device-user-interface/

So I investigated, and found a pretty elegant solution. Without further ado, here is how to change the size of the iOS simulator in order to test your apps in resolutions never seen before.

  1. Download File.txt into ~/Library/Application Support/iPhone Simulator (don't change the name of File.txt)
  2. Edit /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/Applications/iPhone Simulator.app/Contents/Resources/Devices/iPhone (Retina).deviceinfo/Info.plist and add the following keys:
    <key>eagle</key>
    <string>640</string>
    <key>giraffe</key>
    <string>1136</string>
  3. Add setenv("CLASSIC", "0", 1); just before UIApplicationMain in your main.m file in order to support any simulator version.
  4. Quit the iOS Simulator app

You are now ready to test your apps on the rumored 640×1136 new iPhone.

This hack works on Xcode 4.4.1+ and the iPhone 5.1+ Simulator with the iPhone (Retina) device.

Wednesday, June 02, 2010

Fixing -[NSMutableURLRequest setValue:forHTTPHeaderField:]

UPDATE

This problem is fixed as of Mac OS X 10.8.3+ and iOS 5.1+. I have no idea when it was actually fixed since radar sucks so much.

The problem

From NSMutableURLRequest setValue:forHTTPHeaderField: documentation:
In keeping with the HTTP RFC, HTTP header field names are case-insensitive.

I see three problems with this.
  1. Assuming all HTTP implementations are RFC compliant is foolish to say the least.
  2. Enforcing case-insensitivity is nonsense.
  3. This bit of documentation is accurate, the case of header fields is actually changed.
Trying this
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"http://www.example.com"]];
[request setValue:@"MyValue" forHTTPHeaderField:@"MyField"];
NSLog(@"result: %@", [request allHTTPHeaderFields]);
outputs
    result: {
Myfield = MyValue;
}
Notice how the 'F' of MyField was lowercased, that sucks! If the HTTP server you are trying to communicate with is case-sensitive, you are screwed. I filed radar #8029516, which is a duplicate of radar #3131623, which means there is almost no chance to have this fixed anytime soon.

When this happens, you should contact the server administrator asking for a case-insensitive implementation. But in the meantime, if you badly need to preserve the case of your headers, read on.

The investigation

First, let's fire otx to disassemble the Foundation framework in order to have a look at setValue:forHTTPHeaderField: implementation. I strongly suggest you to use otx version from subversion trunk, as it is better at resolving symbols in tail call optimizations (fixed in r553).

Excerpt from otx 0.16b:
   +18  [...]  movl     %eax,0x08(%ebp)
+21 [...] leave
+22 [...] jmpl 0x002c52bb

Excerpt from otx trunk:
   +18  [...]  movl     %eax,0x08(%ebp)
+21 [...] leave
+22 [...] jmpl 0x002c52bb _CFURLRequestSetHTTPHeaderFieldValue

Much better, isn't it?

So let's go through the function calls of setValue:forHTTPHeaderField:. With a basic static analysis of the Foundation and CFNetwork disassemblies, we can draw the following call path:
-[NSMutableURLRequest(NSMutableHTTPURLRequest) setValue:forHTTPHeaderField:]
CFURLRequestSetHTTPHeaderFieldValue()
URLRequest::setHTTPHeaderFieldValue()
HTTPRequest::setHeaderFieldValue()
CFHTTPMessageSetHeaderFieldValue()
HTTPMessage::setHeaderFieldValue()
_CFCapitalizeHeader()

_CFCapitalizeHeader looks like a very good candidate for being the bastard changing the case of our headers. A quick search reveals the source code of CFNetwork that was open source long time ago. Although the open source implementation does not exactly match what we see in the disassembly (it is now using toupper instead of ch + 'A' - 'a' for example), we are now absolutely sure that _CFCapitalizeHeader is the function responsible for messing with our headers.

Now, let's check is if there is a path that will not call _CFCapitalizeHeader and if we can somehow influence the condition that would avoid the call to _CFCapitalizeHeader. This is quickly checked, especially if you enabled the Separate logical blocks option of otx (-b option for cli).
HTTPMessage::setHeaderFieldValue(__CFString const*, __CFString const*)
+0 000515a4 55 pushl %ebp
+1 000515a5 89e5 movl %esp,%ebp
+3 000515a7 83ec28 subl $0x28,%esp
+6 000515aa 8b4508 movl 0x08(%ebp),%eax
+9 000515ad 8975f8 movl %esi,0xf8(%ebp)
+12 000515b0 8b7510 movl 0x10(%ebp),%esi
+15 000515b3 897dfc movl %edi,0xfc(%ebp)
+18 000515b6 8945f4 movl %eax,0xf4(%ebp)
+21 000515b9 8b450c movl 0x0c(%ebp),%eax
+24 000515bc 890424 movl %eax,(%esp)
+27 000515bf e88a26fbff calll __CFCapitalizeHeader
+32 000515c4 c744240cffffffff movl $0xffffffff,0x0c(%esp)
+40 000515cc 89742408 movl %esi,0x08(%esp)
+44 000515d0 89c7 movl %eax,%edi

We see that there is no path that avoid the call to _CFCapitalizeHeader. So we are left with the last resort solution: patching _CFCapitalizeHeader. With APE Lite, function patching is very easy. You first use APEFindSymbol() to find the address of a non-exported symbol (i.e. __CFCapitalizeHeader), then APEPatchCreate() to replace a function implementation with your own, while still keeping a reference to the original implementation. On iPhone OS, you can use APE Lite+arm (my implementation of the APE Lite API using MobileSubstrate).

The solution

NSMutableURLRequest+CaseSensitive is a category on NSMutableURLRequest that adds these three methods:
   - (void) setAllHTTPHeaderFields:(NSDictionary *)headerFields caseSensitive:(BOOL)caseSensitive;
- (void) setValue:(NSString *)value forHTTPHeaderField:(NSString *)field caseSensitive:(BOOL)caseSensitive;
- (void) addValue:(NSString *)value forHTTPHeaderField:(NSString *)field caseSensitive:(BOOL)caseSensitive;

Just pass caseSensitive:YES for preserving the case of your header fields.

Warning: you SHOULD NOT use this in production code. But hey, HTTP implementations SHOULD be case-insensitive ;-)

Thursday, April 08, 2010

ABGetMe with the iPhone SDK

Unlike the Mac Address Book API, the iPhone Address Book API does not come with the ABGetMe function. That's a pity, but we can do something about it.

Recently, I used MFMailComposeViewController for the first time and I realized that it was displaying my e-mail address. Nicolas Seriot already demonstrated how to retrieve all your e-mail account information in SpyPhone, but I felt there was a more lightweight method for retrieving the e-mail addresses by taking advantage of the MessageUI framework. A class-dump and five minutes later, here is my solution:

Wednesday, April 08, 2009

Extract UIKit artwork

While writing an iPhone application (more about it on this blog soon), I wanted to tint my UIAlertView with the same color I tinted my other controls. Maybe this is not a good idea, but I wanted to try anyway. Unfortunately, only UISearchBar, UISegmentedControl, UINavigationBar and UIToolbar have the tintColor property. So I started digging into UIKit. The first place to look was obviously the -(void)[UIAlertView(Private) drawRect:] method. To draw its background, a UIAlertView calls _popupAlertBackground which in turn call _UIImageWithName(@"UIPopupAlertSheetBackground.png"). This file probably lies inside the UIKit.framework would think a Mac developer. In fact it is not there. Only two pngs are there: DefaultWallpaper-iPhone.png and DefaultWallpaper-iPod.png. So, where is it?

It is in fact somehow embedded in the Other.artwork file which contains UIKit artwork. This file is memory mapped at application startup, probably for performance reasons and the content is easily accessed with the private _UIImageWithName() function. _UIImageWithName looks up in a dictionary that associates a file name to some memory location that contains the actual image data.

Now, it would be cool to extract all this content to see what artwork UIKit contains. It turns out to be quite easy. The only trick is to find a reference to the dictionary containing all the file names. Check my UIKit Artwork Extractor project on github to see how it works.

In an attempt to encourage comments, I'm offering a beer at NSConference to the first person who will clearly explain how to find the magic address of the images dictionary (except for Nicolas who knows the answer already) and I'm offering two beers to the one who provides a cleaner way to obtain a reference to the dictionary that is not UIKit version specific ;-)

Edit: I have updated the code so that it is not UIKit version specific, so I won the beers myself that I'm going to drink right away.

Monday, February 19, 2007

Front Row Trailers

English version below

Front Row Trailers est le nouveau nom de Bandes-annonces Front Row depuis la version 2.1.

Front Row Trailers n'est plus disponible. Veuillez vous référer aux instructions pour restaurer les bandes-annonces originale d'Apple.

Foire Aux Questions

Q: Comment faire pour désinstaller Front Row Trailers ?
R: Il n'y a pas besoin de désinstaller Front Row Trailers, il suffit de sélectionner la source Apple (US) et les bandes-annonces d'origine seront de retour.

Q: Est-ce que Front Row Trailers est compatible avec Leopard (Mac OS X 10.5) ?
R: Oui

Q: Sur Leopard, je dois entrer mon mot de passe à chaque fois que je change de source, que puis-je faire pour éviter cela ?
R: Il suffit de taper la commande suivante dans le Terminal (une fois la commande entrée, il faut taper son mon de passe):
sudo chmod o+w /System/Library/PrivateFrameworks/BackRow.framework/Versions/A/Resources/Trailers.plist

Q: Est-il possible de désactiver les bandes-annonces ?
R: Depuis la version 2.1.1, Front Row Trailers le permet en choisissant la source spéciale Deny.
Sur Leopard, il est aussi possible de désactiver les menus iTunes Top Movies, iTunes Top Songs et iTunes Top Music Videos en entrant la commande suivante dans le Terminal:
defaults write com.apple.frontrow EnableITMS -bool NO
Attention: cette commande doit être entrée avant de choisir la source Deny dans Front Row Trailers.

Q: Sur Leopard, certaines bandes-annonces ne se lisent pas aussi bien que sur Tiger ou ne fonctionnent même pas du tout. Prévoyez-vous de distribuer une mise à jour ?
R: Front Row pour Leopard utilise une architecture très différente de celle de Front Row pour Tiger. Tandis que Front Row pour Tiger utilise QuickTime pour lire les bandes-annonces, Front Row pour Leopard semble utiliser son propre méchanisme passant outre QuickTime. Alors que les bandes-annonces se lisent bien dans QuickTime player, celles-ci peuvent ne pas fonctionner dans Front Row. C'est notamment le cas avec les bandes-annonces allemandes utilisant le codec Sorenson 3. Malheureusement, il n'y a rien que je puisse faire. Néanmoins, une future mise à jour de Front Row ou de QuickTime pourrait résoudre ces problèmes.

Historique des versions

Version 2.1.2 (2008-01-24)
* Ajouté Ciné.ch (source suisse romande)
* Détection correcte de la version de QuickTime
Version 2.1.1 (2007-12-18)
* Ajouté Lycos (source espagnole)
* Enlevé Cinefacts (l'accès à Front Row Trailers a été bloqué)
* Possibilité d'interdire les bandes-annonces
Version 2.1 (2007-10-15)
* Changement de nom
* Bandes-annonces haute définition
* 20 sources
* Traduction italienne
* Traduction danoise
* Options de tri
* Nouveau système de mise à jour
Version 2.0.2 (2007-08-20)
* Corrige un bogue dans la détection de Perian
Version 2.0.1 (2007-06-22)
* Corrige un bogue dans la détection des composants QuickTime
Version 2.0 (2007-06-21)
* Nouvelle interface
* 9 nouvelles sources de bandes-annonces à choix
Version 1.1 (2007-05-03)
* Suppresion des saccades
Version 1.0 (2007-02-19)
* Version initiale




Bandes-annonces Front Row is know as Front Row Trailers since version 2.1.

Front Row Trailers isn't available anymore. Please refer to instructions to restore factory Apple trailers.

Frequently Asked Questions

Q: How do I uninstall Front Row Trailers ?
A: There is no need to uninstall Front Row Trailers, just select the Apple (US) source and you will get the original trailers from Apple.

Q: Is Front Row Trailers compatible with Leopard (Mac OS X 10.5) ?
A: Yes

Q: In Leopard, I am prompted for my password every time I change the source, is there anything I can do about that ?
A: Just type the following command in the Terminal (you will have to type your password):
sudo chmod o+w /System/Library/PrivateFrameworks/BackRow.framework/Versions/A/Resources/Trailers.plist

Q: Is it possible to disable the theatrical trailers ?
A: Since version 2.1.1, Front Row Trailers is able to deny trailers access by choosing the special Deny source.
On Leopard, it is also possible to disable the iTunes Top Movies, iTunes Top Songs and iTunes Top Music Videos menus by typing the following command in the Terminal:
defaults write com.apple.frontrow EnableITMS -bool NO
Warning: this command must be typed before choosing the Deny source.

Q: In Leopard, some trailers won't play as smoothly as in Tiger or won't even play at all. Are you planing to release an update ?
A: Front Row for Leopard uses a very different architecture than Front Row for Tiger. Whereas Front Row for Tiger uses QuickTime for playing the trailers, Front Row for Leopard seems to uses its own mechanism ignoring QuickTime. While the trailers play fine in QuickTime Player, they may not play in Front Row. This is notably the case with german trailers using the Sorenson Video 3 codec. Unfortunately, I can not do anything about that. Nevertheless, a future update of Front Row or QuickTime may fix these issues.

Version history

Version 2.1.2 (2008-01-24)
* Added Ciné.ch (swiss french source)
* Correctly detects QuickTime version
Version 2.1.1 (2007-12-18)
* Added Lycos (spanish source)
* Removed Cinefacts (Front Row Trailers access has been blocked)
* Possibility to deny trailers access
Version 2.1 (2007-10-15)
* Name change
* High-definition trailers
* 20 sources
* Italian localization
* Danish localization
* Sorting options
* New update system
Version 2.0.2 (2007-08-20)
* Fixes a bug in Perian detection
Version 2.0.1 (2007-06-22)
* Fixes a bug in QuickTime components detection
Version 2.0 (2007-06-21)
* New interface
* 9 new trailers sources
Version 1.1 (2007-05-03)
* Trailers play more smoothly
Version 1.0 (2007-02-19)
* First version

Sunday, September 24, 2006

Xcode and subversion 1.4 fix

I was unsatisfied to have to revert back to subversion 1.3.2 because Xcode (v2.4) does not yet support the new format of subversion 1.4 .svn/entries file (see my previous blog entry: Xcode and subversion 1.4 incompatibility).

So I wrote a SIMBL plugin that adresses this problem.


  1. Download and install SIMBL (Smart InputManager Bundle Loader) if not already installed

  2. Download Xcode+svn-1.4 and decompress it

  3. Move Xcode+svn-1.4.bundle into ~/Library/Application Support/SIMBL/Plugins

  4. Relaunch Xcode, it is now compatible with svn 1.4 :-)

  5. Update: Xcode 2.4.1 addresses this problem so my plugin is not needed anymore.
keywords: Xcode, SCM, subversion, svn, 1.4

Digg!