Wednesday, October 28, 2009

Viewing PNG files inside .ipa files

This is mainly for the curious people out there wanting to play around with iPhone apps downloaded from the App Store.

As many now know, and iPhone app consists of a zip file renamed with a the ‘ipa’ extension. On Macs all iPhone apps are downloaded to the users Music/iTuenes/iTunes Media/Mobile Applications folder.


To open the file, go to terminal and cd to that directory.

> cd “Music/iTunes/iTunes Media/Mobile Applications”

> mkdir ~/tmp

> cp [some iphone app].ipa ~/tmp/tmp.zip

> cd ~/tmp

> unzip tmp.zip

> cd Payload/[name of app].app

> cp *.png ~/tmp

> cp *.jpg ~/tmp

... and so on.


If you try to open the PNG files you will find that they are not normal PNG files, but rather “iPhone optimized” versions.


A good explanation can be found here:


http://modmyi.com/wiki/index.php/Iphone_PNG_images


and here:


http://iphonedevelopment.blogspot.com/2008/10/iphone-optimized-pngs.html



A quick check for each of the PNG files can be done using the ‘strings’ command.


> strings [someImage].png


If this then returns with a line starting with ‘CgBI’ you know this has been optimized. A normal PNG image should always start with ‘IHDR’


Also you can check an image using the ‘file’ command:


> file testImage.png

testImage.png: PNG image data, 805314566 x 396263525, 0-bit grayscale,



To fix these images you can use the tool provided here (source code only):


http://www.cyberhq.nl/2007/07/05/iphone-png-fixer-upper.html


To compile this project you will also need to install libpng


http://ethan.tira-thompson.org/Mac_OS_X_Ports.html


Once installed copy over the png.h file from /usr/local/include to some place in your home folder. Then open the iPhonePNG project and fix the path to png.h and build.

Then copy the binary found in the build/Release folder to a location in your path, say ~/bin

(or add it to your path with > export PATH = $PATH:~/bin )


A simpler approach is to use the download from http://www.newsfirerss.com/blog/?p=176

Both source code and a binary are provided.


But an even more elegant approach is provided by atPurpose, a Canadian developer. See

atPeek


This app enables viewing the PNG resources directly, without having to convert them.


I haven’t tried the paid-for app as yet as it requires OS 10.6 but it looks very slick. At the bottom of the page there are 2 QL plug-ins and are free to download. They can be just placed into the /Library/QuickLook folder and should work immediately (check this).



Testing iPhonePNG


Usage for this app is simple adding the name of the PNG file to it and it will then append “-Decoded” into the name when completed. IF the image is not optimized it will give the following error:


ZLib error! -3

libpng error: Extra compressed data

[read_png_file] Error during read_image


However, the output file will be created anyway and will only contain garbled data.


Once the image has been decoded you can verify this with the ‘file’ command:


> file testImage-Decoded.png

testImage-Decoded.png: PNG image data, 50 x 33, 8-bit/color RGBA, non-interlaced



You can use the Quick Look plugin directly from the command line also if you so choose.


> qlmanage -p image.png >& /dev/null


For more info on qlmanage see:

http://www.macworld.com/article/131923/2008/02/qlterminal.html


This means that you could run a shell script to check each PNG image for "0-bit" using the file command and then if found call iPhonePNG to convert them.



Here is a small Perl script that with convert all the images in the current directory or a single image if it is specified as a parameter:




#!/usr/bin/perl -w

my $file = shift;
my @files = `ls -1 | grep png`;
my $iphonepng = "~/bin/iPhonePNG";
my $pattern = "0-bit";

if($file ne '') {
&fixPng($file);
} else {
# convert all files that are optimized.
foreach my $imageFile (@files) {
&fixPng($imageFile);
}
}

sub fixPng
{
my $thisFile = shift;
my $file_check = `file $thisFile`;
if($file_check =~ m/$pattern/) {
chomp($thisFile);
print "Convert: $iphonepng $thisFile\n";


`$iphonepng $thisFile`;
# move the decoded file to the original file.
my $decodedFile = $thisFile;
$decodedFile =~ s/.png/-Decoded.png/;
`rm $thisFile`;
`mv $decodedFile $thisFile`;
}
}




Place this in a file 'convert.pl' and then

> chmod +x convert.pl

> ./convert.pl [optional image file]


This will have now converted either a single image passed as a parameter, or all the images in the current directory.