xpwnのdmgでDecrypt

以前書いたvfdecryptを使うとiOS 6.0からはバグったdmgファイルしか生成できなくなりました。代替としてxpwnのdmgを使う事で正常にDecryptできます。

Xpwnからdmgをビルドする

xpwnのコードからビルドします。

git clone git://github.com/planetbeing/xpwn.git
cd xpwn
mkdir build
cd build
cmake ..
make dmg-bin

warningが出ますが、dmgはコンパイルできてます。dmg/dmgがそれです。/usr/local/bin/にでも入れときましょう。

dmgでDecrypt

dmg extract XXX-XXXX-XXX.dmg output.dmg -k <key>

iPadかどうか判別する

[[UIDevice currentDevice] isWildcat]
でもいいんですが、無駄にPrivate API使うのも危険なので(とは言えAppleのコードではかなり使われているのでこれの仕様が変わったり無くなったりする事はほぼ無いんじゃないかとは思いますが)、最近良く見かける下記のやり方の方が良さそう。
static BOOL IsPad(void)
{
    return UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad;
}

これも安心のiOS 3.2+

画面が縦なのか横なのかを判別する

これまで色々なメソッド使ってみたりしましたが、これ以上ない正解に辿りついたのでメモ。

static BOOL IsLandscape(void)
{
    return UIInterfaceOrientationIsLandscape([UIApplication sharedApplication].statusBarOrientation);
}

ちなみにステータスバーが非表示でも大丈夫みたいです。
Available in iOS 2.0 or higherの安心設計。

resizableImageWithCapInsets:(UIEdgeInsets)capInsets でちょっとハマった話

Capの左右(上下)の合計を画像の幅(高さ)と同じにした時のバグ(iOS 5)

ボタン用の下のような画像を背景に使った時にこんなコードを書いてハマったケース。

[button setBackgroundImage:[bgImage resizableImageWithCapInsets:UIEdgeInsetsMake(20.0f, 22.0f, 20.0f, 22.0f)] forState:UIControlStateNormal];
こう書くとiOS 6ではうまくいくのだけど、iOS 5では通常時は普通の表示だけどボタンを押下すると何か欠ける。

これは元画像の幅44pxに対してUIEdgeInsetsMakeのleftとrightの合計が44pxで同一になっているのが原因だ。左右端が表示されているのは画像の高さが47pxでtop+bottomは40だからだろう。ちなみに同一になるように書いてもiOS 6では丁度境界の1pxを引き伸ばしているのか、いい具合に表示された。丁度にする必要はないので若干数値を減らせばOKだ。
[button setBackgroundImage:[bgImage resizableImageWithCapInsets:UIEdgeInsetsMake(20.0f, 20.0f, 20.0f, 20.0f)] forState:UIControlStateNormal];

Capの左右(上下)で片方だけ0指定した時のバグ(iOS 5)

先ほどの画像のUIActivityViewController呼び出し時みたいなViewの背景に指定している画像は右のような1x44の画像で、これをリサイズさせて背景に指定しているが、上の方にしか適応されていない。コードはこんな感じだ。
_backgroundImageView.image = [bgImage resizableImageWithCapInsets:UIEdgeInsetsMake(43.0f, 0, 0, 0)];
これはCap-topを43にして残りの下1pxを伸ばしてくれると思って書いたコードだ。これの原因はtopに1以上の値を指定したがbottomは0を指定しているためだ。どうやら左右(上下)の数値の組み合わせは0,0の組み合わせでなければ1以上を指定して引き伸ばす範囲を明確にしないとダメらしい。ちなみにこれもiOS 6ではイイカンジに引き伸ばして表示された。解決の為には1以上を指定すればいいので、引き伸ばすピクセルが一番下の1pxではなく下から2px目になってしまうが、以下のようにすれば良い。
_backgroundImageView.image = [bgImage resizableImageWithCapInsets:UIEdgeInsetsMake(42.0f, 0, 1.0f, 0)];

これでiOS 5でもちゃんと引き伸ばされる。

BundleIdentifierSearcher

あのアプリを買っていないのだけれど、tweak作成にあたってBundleIdentifierを知りたい・・・。そんな貴兄におくるBundleIdentifierSearcher!



検索してダブルクリックするとBundleIdentifierをコピーできます。NSJSONSerializationクラスとNSArray, NSDictionaryのModern Objective-C Literal Syntaxを使っているのでOS X 10.7+が必要です。
iOS 5からのTwitter連携用に作られたのでしょうけど、JSON扱えるクラスがFoundationにあって助かった。

バイナリはこちら
ソースはgithubに。

Mac上でiOSバイナリをClassDump

「あのバージョンのバイナリ(SpringBoardとか)をClassDumpしたいけど稼働してる実機がないなぁ」って時の方法。

  1. vfdecrypt(Intel Mac) をDL。chmod +xして/usr/local/binにでも突っ込むといいでしょう。
  2. DumpしたいバージョンのipswをDL。
  3. ipswをunzipmkdir dmg; unzip hogehoge.ipsw -d dmg; cd dmg
  4. 対象のVFDecrypt keyを調べる。Root Filesystemのやつね。
  5. vfdecrypt -i hogehoge.dmg -k 対象のkey -o output.dmg
  6. output.dmgをマウントしてバイナリをコピる。
  7. class-dumpする。

以上。

自動retainされないNSMutableSet

addObjectしてもretainされないNSMutableSetを作る。使い所はグローバル変数のstatic NSMutableSet *でこれを使って特定クラスのオブジェクトを詰め込んでいって、あるタイミングで全インスタンスに対して何か処理をする為。retainさせないのはreleaseタイミングはデフォルトのままにするため。まぁaddObjectしたらすぐにreleaseするのと大差ないんだけどね(その場合もSEGVには注意しないといけない)。

CFSetCallBacks assignCallbacks = kCFTypeSetCallBacks;
assignCallbacks.retain = NULL;
assignCallbacks.release = NULL;
NSMutableSet *assignSet = (NSMutableSet *)CFSetCreateMutable(kCFAllocatorDefault, 0, &assignCallbacks);

これで[assignSet addObject:obj];してもobjのretainCountは増えない。解放されたオブジェクトを触るとSEGVになるので、以下のようにaddするオブジェクトのクラスにおけるdeallocの中で外す。

- (void)dealloc
{
        if ([assignSet containsObject:self])
                [assignSet removeObject:self];
        [super dealloc];  // %orig;
}
上記のようにdeallocいじらないといけないので、単一のクラスのオブジェクトを詰め込む場合以外ぐらいに留めておくのがよさそう。

参考 http://decafish.blog.so-net.ne.jp/2010-10-27-1

Sleipnizerで使っているAllAroundPullViewを公開しました。

タイトル通り、Sleipnizerで使っているPull-To-Gestureの部分のコードをgithubで公開しました。特徴はSleipnizerで使えるように4方向に対応している事です。branchのsleipnizerがまさに使ってる状態です。

https://github.com/r-plus/AllAroundPullView

使い方の詳細はgithubのREADMEを参照。

chpwnのPullToRefreshViewを元に以下な感じの代物です。

  • 色々と微妙な実装だったので修正(大分不親切な実装だった)
  • 4方向に対応
  • コード実行部分をdelegateからBlocksに変更
  • LabelとLastRefreshDateを削除
  • XcodeのDemoプロジェクトの追加
  • tweakで使えるようにARC非対応!非対応!
  • kCFCoreFoundationVersionNumberのススメ

    iOSのバージョンを判定するのには二通り程あって、UIDeviceから取得する方法

    float iOSVersion = [[[UIDevice currentDevice] systemVersion] floatValue];
    と、kCFCoreFoundationVersionNumberを使うやり方があります。
    kCFCoreFoundationVersionNumber >= 550.52 //550.52はiOS 4.2の値

    ちなみにfloatValue使うと小数点2個は取れないので5.1.1は5.100000になります。kCFCoreFoundationVersionNumberも3項目のバージョンでの値に変更はないため、3項目までちゃんと取る場合はNSNumericSearchできちんと比較する必要があります。

    で、tweakのバージョン判定では2項目までで十分な事が多いのでkCF~の方がオススメです。うっかりspringboardが対象のtweakのconstructorでUIDevice使うと危険なので。UIDeviceを使ってはいませんが、iOS5が出た当初に発現していた画面1/4問題は、例えばLastAppでは以下のバージョン判定の1行が原因で起こっていました。

    isFirmware3x_ = (class_getInstanceMethod(objc_getClass("SBApplication"), @selector(pid)) != NULL);
    これを以下に書き換えることで対処したと思われます。
    isFormware3x_ = (kCFCoreFoundationVersionNumber < 550.32) ? YES : NO; // 550.32はiOS 4.0の数字

    新OSが出た際の確認方法は/System/Library/Frameworks/CoreFoundation.framework/Info.plistのCFBundleVersionから確認できます。iOS5.1.xは690.10ですね。

    2.0からの番号はこのあたりを参考に。chpwn / IconSupport - Firmware.h