]> git.saurik.com Git - cydia.git/blobdiff - MobileCydia.mm
Don't show role::cydia packages in Expert mode of the installed page.
[cydia.git] / MobileCydia.mm
index 428c8848958b7963d94be64ca6cccc41fbd2b1d8..151cae371787e57c8cabd34f74f5001dca9c9579 100644 (file)
@@ -197,6 +197,13 @@ void PrintTimes() {
 
 #define _pooled _H<NSAutoreleasePool> _pool([[NSAutoreleasePool alloc] init], true);
 
+#define CYPoolStart() \
+    NSAutoreleasePool *_pool([[NSAutoreleasePool alloc] init]); \
+    do
+#define CYPoolEnd() \
+    while (false); \
+    [_pool release];
+
 static const NSUInteger UIViewAutoresizingFlexibleBoth(UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight);
 
 void NSLogPoint(const char *fix, const CGPoint &point) {
@@ -293,6 +300,7 @@ static _finline void UpdateExternalStatus(uint64_t newStatus) {
 @end
 /* }}} */
 
+/* Cydia Action Sheet {{{ */
 @interface CYActionSheet : UIAlertView {
     unsigned button_;
 }
@@ -339,6 +347,7 @@ static _finline void UpdateExternalStatus(uint64_t newStatus) {
 }
 
 @end
+/* }}} */
 
 /* NSForcedOrderingSearch doesn't work on the iPhone */
 static const NSStringCompareOptions MatchCompareOptions_ = NSLiteralSearch | NSCaseInsensitiveSearch;
@@ -375,17 +384,15 @@ static const CFStringCompareFlags LaxCompareFlags_ = kCFCompareCaseInsensitive |
 
 #define ForRelease 1
 #define TraceLogging (1 && !ForRelease)
-#define HistogramInsertionSort (0 && !ForRelease)
+#define HistogramInsertionSort (!ForRelease ? 0 : 0)
 #define ProfileTimes (0 && !ForRelease)
 #define ForSaurik (0 && !ForRelease)
 #define LogBrowser (0 && !ForRelease)
 #define TrackResize (0 && !ForRelease)
-#define ManualRefresh (0 && !ForRelease)
+#define ManualRefresh (1 && !ForRelease)
 #define ShowInternals (0 && !ForRelease)
 #define IgnoreInstall (0 && !ForRelease)
-#define RecycleWebViews 0
-#define RecyclePackageViews (0 && ForRelease)
-#define AlwaysReload (1 && !ForRelease)
+#define AlwaysReload (0 && !ForRelease)
 
 #if !TraceLogging
 #undef _trace
@@ -404,7 +411,6 @@ static const CFStringCompareFlags LaxCompareFlags_ = kCFCompareCaseInsensitive |
 typedef uint32_t (*SKRadixFunction)(id, void *);
 
 @interface NSMutableArray (Radix)
-- (void) radixSortUsingSelector:(SEL)selector withObject:(id)object;
 - (void) radixSortUsingFunction:(SKRadixFunction)function withContext:(void *)argument;
 @end
 
@@ -413,7 +419,20 @@ struct RadixItem_ {
     uint32_t key;
 };
 
-static void RadixSort_(NSMutableArray *self, size_t count, struct RadixItem_ *swap) {
+@implementation NSMutableArray (Radix)
+
+- (void) radixSortUsingFunction:(SKRadixFunction)function withContext:(void *)argument {
+    size_t count([self count]);
+    struct RadixItem_ *swap(new RadixItem_[count * 2]);
+
+    for (size_t i(0); i != count; ++i) {
+        RadixItem_ &item(swap[i]);
+        item.index = i;
+
+        id object([self objectAtIndex:i]);
+        item.key = function(object, argument);
+    }
+
     struct RadixItem_ *lhs(swap), *rhs(swap + count);
 
     static const size_t width = 32;
@@ -454,68 +473,15 @@ static void RadixSort_(NSMutableArray *self, size_t count, struct RadixItem_ *sw
 
     delete [] hist;
 
-    NSMutableArray *values([NSMutableArray arrayWithCapacity:count]);
+    const void **values(new const void *[count]);
     for (size_t i(0); i != count; ++i)
-        [values addObject:[self objectAtIndex:lhs[i].index]];
-    [self setArray:values];
+        values[i] = [self objectAtIndex:lhs[i].index];
+    CFArrayReplaceValues((CFMutableArrayRef) self, CFRangeMake(0, count), values, count);
+    delete [] values;
 
     delete [] swap;
 }
 
-@implementation NSMutableArray (Radix)
-
-- (void) radixSortUsingSelector:(SEL)selector withObject:(id)object {
-    size_t count([self count]);
-    if (count == 0)
-        return;
-
-#if 0
-    NSInvocation *invocation([NSInvocation invocationWithMethodSignature:[NSMethodSignature signatureWithObjCTypes:"L12@0:4@8"]]);
-    [invocation setSelector:selector];
-    [invocation setArgument:&object atIndex:2];
-#else
-    /* XXX: this is an unsafe optimization of doomy hell */
-    Method method(class_getInstanceMethod([[self objectAtIndex:0] class], selector));
-    _assert(method != NULL);
-    uint32_t (*imp)(id, SEL, id) = reinterpret_cast<uint32_t (*)(id, SEL, id)>(method_getImplementation(method));
-    _assert(imp != NULL);
-#endif
-
-    struct RadixItem_ *swap(new RadixItem_[count * 2]);
-
-    for (size_t i(0); i != count; ++i) {
-        RadixItem_ &item(swap[i]);
-        item.index = i;
-
-        id object([self objectAtIndex:i]);
-
-#if 0
-        [invocation setTarget:object];
-        [invocation invoke];
-        [invocation getReturnValue:&item.key];
-#else
-        item.key = imp(object, selector, object);
-#endif
-    }
-
-    RadixSort_(self, count, swap);
-}
-
-- (void) radixSortUsingFunction:(SKRadixFunction)function withContext:(void *)argument {
-    size_t count([self count]);
-    struct RadixItem_ *swap(new RadixItem_[count * 2]);
-
-    for (size_t i(0); i != count; ++i) {
-        RadixItem_ &item(swap[i]);
-        item.index = i;
-
-        id object([self objectAtIndex:i]);
-        item.key = function(object, argument);
-    }
-
-    RadixSort_(self, count, swap);
-}
-
 @end
 /* }}} */
 /* Insertion Sort {{{ */
@@ -552,7 +518,7 @@ void CFArrayInsertionSortValues(CFMutableArrayRef array, CFRange range, CFCompar
     const void **values(new const void *[range.length]);
     CFArrayGetValues(array, range, values);
 
-#if HistogramInsertionSort
+#if HistogramInsertionSort > 0
     uint32_t total(0), *offsets(new uint32_t[range.length]);
 #endif
 
@@ -560,9 +526,13 @@ void CFArrayInsertionSortValues(CFMutableArrayRef array, CFRange range, CFCompar
         const void *value(values[index]);
         //CFIndex correct(SKBSearch_(&value, sizeof(const void *), values, index, comparator, context));
         CFIndex correct(index);
-        while (comparator(value, values[correct - 1], context) == kCFCompareLessThan)
+        while (comparator(value, values[correct - 1], context) == kCFCompareLessThan) {
+#if HistogramInsertionSort > 1
+            NSLog(@"%@ < %@", value, values[correct - 1]);
+#endif
             if (--correct == 0)
                 break;
+        }
         if (correct != index) {
             size_t offset(index - correct);
 #if HistogramInsertionSort
@@ -579,7 +549,7 @@ void CFArrayInsertionSortValues(CFMutableArrayRef array, CFRange range, CFCompar
     CFArrayReplaceValues(array, range, values, range.length);
     delete [] values;
 
-#if HistogramInsertionSort
+#if HistogramInsertionSort > 0
     for (CFIndex index(0); index != range.length; ++index)
         if (offsets[index] != 0)
             NSLog(@"Insertion Displacement [%u]: %u", index, offsets[index]);
@@ -725,6 +695,16 @@ NSUInteger DOMNodeList$countByEnumeratingWithState$objects$count$(DOMNodeList *s
 /* }}} */
 
 /* C++ NSString Wrapper Cache {{{ */
+static _finline CFStringRef CYStringCreate(const char *data, size_t size) {
+    return size == 0 ? NULL :
+        CFStringCreateWithBytesNoCopy(kCFAllocatorDefault, reinterpret_cast<const uint8_t *>(data), size, kCFStringEncodingUTF8, NO, kCFAllocatorNull) ?:
+        CFStringCreateWithBytesNoCopy(kCFAllocatorDefault, reinterpret_cast<const uint8_t *>(data), size, kCFStringEncodingISOLatin1, NO, kCFAllocatorNull);
+}
+
+static _finline CFStringRef CYStringCreate(const char *data) {
+    return CYStringCreate(data, strlen(data));
+}
+
 class CYString {
   private:
     char *data_;
@@ -803,14 +783,10 @@ class CYString {
         return size_ == rhs.size_ && memcmp(data_, rhs.data_, size_) == 0;
     }
 
-    operator CFStringRef() {
-        if (cache_ == NULL) {
-            if (size_ == 0)
-                return nil;
-            cache_ = CFStringCreateWithBytesNoCopy(kCFAllocatorDefault, reinterpret_cast<uint8_t *>(data_), size_, kCFStringEncodingUTF8, NO, kCFAllocatorNull);
-            if (cache_ == NULL)
-                cache_ = CFStringCreateWithBytesNoCopy(kCFAllocatorDefault, reinterpret_cast<uint8_t *>(data_), size_, kCFStringEncodingISOLatin1, NO, kCFAllocatorNull);
-        } return cache_;
+    _finline operator CFStringRef() {
+        if (cache_ == NULL)
+            cache_ = CYStringCreate(data_, size_);
+        return cache_;
     }
 
     _finline operator id() {
@@ -1055,6 +1031,7 @@ static NSString *SerialNumber_ = nil;
 static NSString *ChipID_ = nil;
 static NSString *Token_ = nil;
 static NSString *UniqueID_ = nil;
+static NSString *PLMN_ = nil;
 static NSString *Build_ = nil;
 static NSString *Product_ = nil;
 static NSString *Safari_ = nil;
@@ -1074,10 +1051,6 @@ static bool Changed_;
 static NSDate *now_;
 
 static bool IsWildcat_;
-
-#if RecycleWebViews
-static NSMutableArray *Documents_;
-#endif
 /* }}} */
 
 /* Display Helpers {{{ */
@@ -1102,10 +1075,6 @@ NSString *SizeString(double size) {
     return [NSString stringWithFormat:@"%s%.1f %s", (negative ? "-" : ""), size, powers_[power]];
 }
 
-static _finline CFStringRef CFCString(const char *value) {
-    return CFStringCreateWithBytesNoCopy(kCFAllocatorDefault, reinterpret_cast<const uint8_t *>(value), strlen(value), kCFStringEncodingUTF8, NO, kCFAllocatorNull);
-}
-
 const char *StripVersion_(const char *version) {
     const char *colon(strchr(version, ':'));
     if (colon != NULL)
@@ -1119,7 +1088,7 @@ CFStringRef StripVersion(const char *version) {
         version = colon + 1;
     return CFStringCreateWithBytes(kCFAllocatorDefault, reinterpret_cast<const uint8_t *>(version), strlen(version), kCFStringEncodingUTF8, NO);
     // XXX: performance
-    return CFCString(version);
+    return CYStringCreate(version);
 }
 
 NSString *LocalizeSection(NSString *section) {
@@ -1216,6 +1185,7 @@ bool isSectionVisible(NSString *section) {
 - (void) syncData;
 - (void) showSettings;
 - (UIProgressHUD *) addProgressHUD;
+- (BOOL) hudIsShowing;
 - (void) removeProgressHUD:(UIProgressHUD *)hud;
 - (CYViewController *) pageForPackage:(NSString *)name;
 - (PackageController *) packageController;
@@ -1363,7 +1333,7 @@ typedef std::map< unsigned long, _H<Source> > SourceMap;
     pkgSourceList *list_;
 
     SourceMap sources_;
-    NSMutableArray *packages_;
+    CFMutableArrayRef packages_;
 
     _transient NSObject<ConfigurationDelegate, ProgressDelegate> *delegate_;
     Status status_;
@@ -1940,11 +1910,14 @@ uint32_t PackagePrefixRadix(Package *self, void *context) {
 
         for (size_t i(0); i != 4; ++i)
             if (isalpha(data[i]))
-                data[i] &= 0xdf;
+                data[i] |= 0x20;
     }
 
     if (offset == 0)
-        data[0] = (data[0] & 0x3f) | "\x80\x00\xc0\x40"[data[0] >> 6];
+        if (data[0] == '@')
+            data[0] = 0x7f;
+        else
+            data[0] = (data[0] & 0x1f) | "\x80\x00\xc0\x40"[data[0] >> 6];
 
     /* XXX: ntohl may be more honest */
     return OSSwapInt32(*reinterpret_cast<uint32_t *>(data));
@@ -2002,8 +1975,6 @@ struct PackageNameOrdering :
 - (void) dealloc {
     if (source_ != nil)
         [source_ release];
-    if (section$_ != nil)
-        [section$_ release];
 
     if (latest_ != nil)
         [latest_ release];
@@ -2119,12 +2090,15 @@ struct PackageNameOrdering :
 - (Package *) initWithVersion:(pkgCache::VerIterator)version withZone:(NSZone *)zone inPool:(apr_pool_t *)pool database:(Database *)database {
     if ((self = [super init]) != nil) {
     _profile(Package$initWithVersion)
-    @synchronized (database) {
         era_ = [database era];
         pool_ = pool;
 
         version_ = version;
-        iterator_ = version.ParentPkg();
+
+        _profile(Package$initWithVersion$ParentPkg)
+            iterator_ = version.ParentPkg();
+        _end
+
         database_ = database;
 
         _profile(Package$initWithVersion$Latest)
@@ -2150,6 +2124,13 @@ struct PackageNameOrdering :
             name_.set(pool, iterator_.Display());
         _end
 
+        _profile(Package$initWithVersion$lowercaseString)
+            char *data(id_.data());
+            for (size_t i(0), e(id_.size()); i != e; ++i)
+                // XXX: do not use tolower() as this is not locale-specific? :(
+                data[i] |= 0x20;
+        _end
+
         if (!file_.end()) {
             _profile(Package$initWithVersion$Source)
                 source_ = [database_ getSource:file_.File()];
@@ -2167,9 +2148,9 @@ struct PackageNameOrdering :
                 tags_ = [[NSMutableArray alloc] initWithCapacity:8];
                 do {
                     const char *name(tag.Name());
-                    [tags_ addObject:(NSString *)CFCString(name)];
+                    [tags_ addObject:(NSString *)CYStringCreate(name)];
                     if (role_ == nil && strncmp(name, "role::", 6) == 0 /*&& strcmp(name, "role::leaper") != 0*/)
-                        role_ = (NSString *) CFCString(name + 6);
+                        role_ = (NSString *) CYStringCreate(name + 6);
                     if (required_ && strncmp(name, "require::", 9) == 0 && (
                         true
                     ))
@@ -2180,10 +2161,9 @@ struct PackageNameOrdering :
         _end
 
         bool changed(false);
-        NSString *key([static_cast<id>(id_) lowercaseString]);
 
         _profile(Package$initWithVersion$Metadata)
-            metadata_ = [Packages_ objectForKey:key];
+            metadata_ = [Packages_ objectForKey:id_];
 
             if (metadata_ == nil) {
                 firstSeen_ = now_;
@@ -2223,7 +2203,7 @@ struct PackageNameOrdering :
             metadata_ = [metadata_ retain];
 
             if (changed) {
-                [Packages_ setObject:metadata_ forKey:key];
+                [Packages_ setObject:metadata_ forKey:id_];
                 Changed_ = true;
             }
         _end
@@ -2232,14 +2212,18 @@ struct PackageNameOrdering :
             section_.set(pool_, iterator_.Section());
         _end
 
-        obsolete_ = [self hasTag:@"cydia::obsolete"];
-        essential_ = ((iterator_->Flags & pkgCache::Flag::Essential) == 0 ? NO : YES) || [self hasTag:@"cydia::essential"];
-        [self setVisible];
-    } _end } return self;
+        _profile(Package$initWithVersion$hasTag)
+            obsolete_ = [self hasTag:@"cydia::obsolete"];
+            essential_ = ((iterator_->Flags & pkgCache::Flag::Essential) == 0 ? NO : YES) || [self hasTag:@"cydia::essential"];
+        _end
+
+        _profile(Package$initWithVersion$setVisible)
+            [self setVisible];
+        _end
+    _end } return self;
 }
 
 + (Package *) packageWithIterator:(pkgCache::PkgIterator)iterator withZone:(NSZone *)zone inPool:(apr_pool_t *)pool database:(Database *)database {
-@synchronized ([Database class]) {
     pkgCache::VerIterator version;
 
     _profile(Package$packageWithIterator$GetCandidateVer)
@@ -2255,7 +2239,7 @@ struct PackageNameOrdering :
         inPool:pool
         database:database
     ] autorelease];
-} }
+}
 
 - (pkgCache::PkgIterator) iterator {
     return iterator_;
@@ -2266,7 +2250,7 @@ struct PackageNameOrdering :
         if (section_.empty())
             return nil;
 
-        std::replace(section_.data(), section_.data() + section_.size(), ' ', '_');
+        std::replace(section_.data(), section_.data() + section_.size(), '_', ' ');
         NSString *name(section_);
 
       lookup:
@@ -2276,7 +2260,7 @@ struct PackageNameOrdering :
                 goto lookup;
             }
 
-        section$_ = [[name stringByReplacingCharacter:'_' withCharacter:' '] retain];
+        section$_ = name;
     } return section$_;
 }
 
@@ -2321,8 +2305,10 @@ struct PackageNameOrdering :
 }
 
 - (NSString *) longDescription {
-    if (file_.end())
+@synchronized (database_) {
+    if ([database_ era] != era_ || file_.end())
         return nil;
+
     pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
     NSString *description([NSString stringWithUTF8String:parser->LongDesc().c_str()]);
 
@@ -2338,7 +2324,7 @@ struct PackageNameOrdering :
     }
 
     return [trimmed componentsJoinedByString:@"\n"];
-}
+} }
 
 - (NSString *) shortDescription {
     return tagline_;
@@ -2413,8 +2399,28 @@ struct PackageNameOrdering :
 }
 
 - (BOOL) unfiltered {
-    NSString *section([self section]);
-    return !obsolete_ && [self hasSupportingRole] && (section == nil || isSectionVisible(section));
+    _profile(Package$unfiltered$obsolete)
+        if (obsolete_)
+            return false;
+    _end
+
+    _profile(Package$unfiltered$hasSupportingRole)
+        if (![self hasSupportingRole])
+            return false;
+    _end
+
+    NSString *section;
+
+    _profile(Package$unfiltered$section)
+        section = [self section];
+    _end
+
+    _profile(Package$unfiltered$isSectionVisible)
+        if (section != nil && !isSectionVisible(section))
+            return false;
+    _end
+
+    return true;
 }
 
 - (BOOL) visible {
@@ -2818,7 +2824,7 @@ struct PackageNameOrdering :
 }
 
 - (bool) isInstalledAndVisible:(NSNumber *)number {
-    return (![number boolValue] || [self visible]) && ![self uninstalled];
+    return ((![number boolValue] && ![role_ isEqualToString:@"cydia"]) || [self visible]) && ![self uninstalled];
 }
 
 - (bool) isVisibleInSection:(NSString *)name {
@@ -3069,7 +3075,7 @@ static NSString *Warning_;
 }
 
 - (Package *) packageWithName:(NSString *)name {
-@synchronized ([Database class]) {
+@synchronized (self) {
     if (static_cast<pkgDepCache *>(cache_) == NULL)
         return nil;
     pkgCache::PkgIterator iterator(cache_->FindPkg([name UTF8String]));
@@ -3087,7 +3093,7 @@ static NSString *Warning_;
         zone_ = NSCreateZone(1024 * 1024, 256 * 1024, NO);
         apr_pool_create(&pool_, NULL);
 
-        packages_ = [[NSMutableArray alloc] init];
+        packages_ = CFArrayCreateMutable(kCFAllocatorDefault, 0, NULL);
 
         int fds[2];
 
@@ -3155,7 +3161,7 @@ static NSString *Warning_;
 }
 
 - (NSArray *) packages {
-    return packages_;
+    return (NSArray *) packages_;
 }
 
 - (NSArray *) sources {
@@ -3171,7 +3177,7 @@ static NSString *Warning_;
 
     NSMutableArray *issues([NSMutableArray arrayWithCapacity:4]);
 
-    for (Package *package in packages_) {
+    for (Package *package in [self packages]) {
         if (![package broken])
             continue;
         pkgCache::PkgIterator pkg([package iterator]);
@@ -3263,13 +3269,13 @@ static NSString *Warning_;
     return [self popErrorWithTitle:title] || !success;
 }
 
-- (void) reloadData { _pooled
-@synchronized ([Database class]) {
-    @synchronized (self) {
-        ++era_;
-    }
+- (void) reloadData { CYPoolStart() {
+@synchronized (self) {
+    ++era_;
+
+    CFArrayApplyFunction(packages_, CFRangeMake(0, CFArrayGetCount(packages_)), reinterpret_cast<CFArrayApplierFunction>(&CFRelease), NULL);
+    CFArrayRemoveAllValues(packages_);
 
-    [packages_ removeAllObjects];
     sources_.clear();
 
     _error->Discard();
@@ -3362,8 +3368,6 @@ static NSString *Warning_;
             return;
     }
 
-    _trace();
-
     for (pkgSourceList::const_iterator source = list_->begin(); source != list_->end(); ++source) {
         std::vector<pkgIndexFile *> *indices = (*source)->GetIndexFiles();
         for (std::vector<pkgIndexFile *>::const_iterator index = indices->begin(); index != indices->end(); ++index)
@@ -3375,8 +3379,6 @@ static NSString *Warning_;
             }
     }
 
-    _trace();
-
     {
         /*std::vector<Package *> packages;
         packages.reserve(std::max(10000U, [packages_ count] + 1000));
@@ -3388,7 +3390,7 @@ static NSString *Warning_;
         for (pkgCache::PkgIterator iterator = cache_->PkgBegin(); !iterator.end(); ++iterator)
             if (Package *package = [Package packageWithIterator:iterator withZone:zone_ inPool:pool_ database:self])
                 //packages.push_back(package);
-                [packages_ addObject:package];
+                CFArrayAppendValue(packages_, [package retain]);
 
         _trace();
 
@@ -3398,9 +3400,9 @@ static NSString *Warning_;
             packages_ = [[NSArray alloc] initWithObjects:&packages.front() count:packages.size()];
         _trace();*/
 
-        [packages_ radixSortUsingFunction:reinterpret_cast<SKRadixFunction>(&PackagePrefixRadix) withContext:reinterpret_cast<void *>(16)];
-        [packages_ radixSortUsingFunction:reinterpret_cast<SKRadixFunction>(&PackagePrefixRadix) withContext:reinterpret_cast<void *>(4)];
-        [packages_ radixSortUsingFunction:reinterpret_cast<SKRadixFunction>(&PackagePrefixRadix) withContext:reinterpret_cast<void *>(0)];
+        [(NSMutableArray *) packages_ radixSortUsingFunction:reinterpret_cast<SKRadixFunction>(&PackagePrefixRadix) withContext:reinterpret_cast<void *>(16)];
+        [(NSMutableArray *) packages_ radixSortUsingFunction:reinterpret_cast<SKRadixFunction>(&PackagePrefixRadix) withContext:reinterpret_cast<void *>(4)];
+        [(NSMutableArray *) packages_ radixSortUsingFunction:reinterpret_cast<SKRadixFunction>(&PackagePrefixRadix) withContext:reinterpret_cast<void *>(0)];
 
         /*_trace();
         PrintTimes();
@@ -3414,13 +3416,13 @@ static NSString *Warning_;
 
         //CFArraySortValues((CFMutableArrayRef) packages_, CFRangeMake(0, [packages_ count]), reinterpret_cast<CFComparatorFunction>(&PackageNameCompare), NULL);
 
-        CFArrayInsertionSortValues((CFMutableArrayRef) packages_, CFRangeMake(0, [packages_ count]), reinterpret_cast<CFComparatorFunction>(&PackageNameCompare), NULL);
+        CFArrayInsertionSortValues(packages_, CFRangeMake(0, CFArrayGetCount(packages_)), reinterpret_cast<CFComparatorFunction>(&PackageNameCompare), NULL);
 
         //[packages_ sortUsingFunction:reinterpret_cast<NSComparisonResult (*)(id, id, void *)>(&PackageNameCompare) context:NULL];
 
         _trace();
     }
-} }
+} } CYPoolEnd() _trace(); }
 
 - (void) configure {
     NSString *dpkg = [NSString stringWithFormat:@"dpkg --configure -a --status-fd %u", statusfd_];
@@ -3566,7 +3568,7 @@ static NSString *Warning_;
 }
 
 - (void) setVisible {
-    for (Package *package in packages_)
+    for (Package *package in [self packages])
         [package setVisible];
 }
 
@@ -3695,14 +3697,10 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
         return @"getPackageById";
     else if (selector == @selector(installPackages:))
         return @"installPackages";
-    else if (selector == @selector(setAutoPopup:))
-        return @"setAutoPopup";
     else if (selector == @selector(setButtonImage:withStyle:toFunction:))
         return @"setButtonImage";
     else if (selector == @selector(setButtonTitle:withStyle:toFunction:))
         return @"setButtonTitle";
-    else if (selector == @selector(setFinishHook:))
-        return @"setFinishHook";
     else if (selector == @selector(setPopupHook:))
         return @"setPopupHook";
     else if (selector == @selector(setSpecial:))
@@ -3813,10 +3811,6 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     [delegate_ performSelectorOnMainThread:@selector(installPackages:) withObject:packages waitUntilDone:NO];
 }
 
-- (void) setAutoPopup:(BOOL)popup {
-    [indirect_ setAutoPopup:popup];
-}
-
 - (void) setButtonImage:(NSString *)button withStyle:(NSString *)style toFunction:(id)function {
     [indirect_ setButtonImage:button withStyle:style toFunction:function];
 }
@@ -3838,10 +3832,6 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     Changed_ = true;
 }
 
-- (void) setFinishHook:(id)function {
-    [indirect_ setFinishHook:function];
-}
-
 - (void) setPopupHook:(id)function {
     [indirect_ setPopupHook:function];
 }
@@ -3887,8 +3877,8 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 - (void) setHeaders:(NSDictionary *)headers forHost:(NSString *)host {
 }
 
-- (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
-    [super webView:sender didClearWindowObject:window forFrame:frame];
+- (void) webView:(WebView *)view didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
+    [super webView:view didClearWindowObject:window forFrame:frame];
 
     WebDataSource *source([frame dataSource]);
     NSURLResponse *response([source response]);
@@ -3924,8 +3914,8 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
         [request setValue:Role_ forHTTPHeaderField:@"X-Role"];
 }
 
-- (NSURLRequest *) webView:(WebView *)sender resource:(id)identifier willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)redirectResponse fromDataSource:(WebDataSource *)source {
-    NSMutableURLRequest *copy = [request mutableCopy];
+- (NSURLRequest *) webView:(WebView *)view resource:(id)resource willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)response fromDataSource:(WebDataSource *)source {
+    NSMutableURLRequest *copy([[super webView:view resource:resource willSendRequest:request redirectResponse:response fromDataSource:source] mutableCopy]);
     [self _setMoreHeaders:copy];
     return copy;
 }
@@ -3939,7 +3929,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     if ((self = [super initWithWidth:0 ofClass:[CYBrowserController class]]) != nil) {
         cydia_ = [[CydiaObject alloc] initWithDelegate:indirect_];
 
-        WebView *webview([document_ webView]);
+        WebView *webview([[webview_ _documentView] webView]);
 
         Package *package([[Database sharedInstance] packageWithName:@"cydia"]);
 
@@ -4015,15 +4005,18 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     }
 }
 
-- (id) invokeDefaultMethodWithArguments:(NSArray *)args {
+- (void) _doContinue {
     [self dismissModalViewControllerAnimated:YES];
     [delegate_ cancelAndClear:NO];
+}
 
+- (id) invokeDefaultMethodWithArguments:(NSArray *)args {
+    [self performSelectorOnMainThread:@selector(_doContinue) withObject:nil waitUntilDone:NO];
     return nil;
 }
 
-- (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
-    [super webView:sender didClearWindowObject:window forFrame:frame];
+- (void) webView:(WebView *)view didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
+    [super webView:view didClearWindowObject:window forFrame:frame];
     [window setValue:changes_ forKey:@"changes"];
     [window setValue:issues_ forKey:@"issues"];
     [window setValue:sizes_ forKey:@"sizes"];
@@ -4299,7 +4292,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 
     CGRect prgrect = {{
         (bounds.size.width - prgsize.width) / 2,
-        bounds.size.height - prgsize.height - 64
+        bounds.size.height - prgsize.height - 20
     }, prgsize};
 
     float closewidth = bounds.size.width - 20;
@@ -4308,7 +4301,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     [progress_ setFrame:prgrect];
     [status_ setFrame:CGRectMake(
         10,
-        bounds.size.height - prgsize.height - 94,
+        bounds.size.height - prgsize.height - 50,
         bounds.size.width - 20,
         24
     )];
@@ -4316,11 +4309,11 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
         10,
         20,
         bounds.size.width - 20,
-        bounds.size.height - 106
+        bounds.size.height - 62
     )];
     [close_ setFrame:CGRectMake(
         (bounds.size.width - closewidth) / 2,
-        bounds.size.height - prgsize.height - 94,
+        bounds.size.height - prgsize.height - 50,
         closewidth,
         32 + prgsize.height
     )];
@@ -4968,6 +4961,8 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     }
 
     [self setAccessoryType:editing ? UITableViewCellAccessoryNone : UITableViewCellAccessoryDisclosureIndicator];
+    [self setSelectionStyle:editing ? UITableViewCellSelectionStyleNone : UITableViewCellSelectionStyleBlue];
+
     [content_ setNeedsDisplay];
 }
 
@@ -5134,6 +5129,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     NSString *name_;
     bool commercial_;
     NSMutableArray *buttons_;
+    UIBarButtonItem *button_;
 }
 
 - (id) initWithDatabase:(Database *)database;
@@ -5148,7 +5144,12 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
         [package_ release];
     if (name_ != nil)
         [name_ release];
+
     [buttons_ release];
+
+    if (button_ != nil)
+        [button_ release];
+
     [super dealloc];
 }
 
@@ -5186,12 +5187,8 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     }
 }
 
-- (void) webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame {
-    return [super webView:sender didFinishLoadForFrame:frame];
-}
-
-- (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
-    [super webView:sender didClearWindowObject:window forFrame:frame];
+- (void) webView:(WebView *)view didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
+    [super webView:view didClearWindowObject:window forFrame:frame];
     [window setValue:package_ forKey:@"package"];
 }
 
@@ -5248,13 +5245,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 }
 
 - (UIBarButtonItem *) rightButton {
-    int count = [buttons_ count];
-    return [[[UIBarButtonItem alloc]
-        initWithTitle:count == 0 ? nil : count != 1 ? UCLocalize("MODIFY") : [buttons_ objectAtIndex:0]
-        style:UIBarButtonItemStylePlain
-        target:self
-        action:@selector(customButtonClicked)
-    ] autorelease];
+    return button_;
 }
 #endif
 
@@ -5297,30 +5288,24 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
             [buttons_ addObject:UCLocalize("REINSTALL")];
         if (![package_ uninstalled])
             [buttons_ addObject:UCLocalize("REMOVE")];
+    }
 
-        if (special_ != NULL) {
-            CGRect frame([document_ frame]);
-            frame.size.height = 0;
-            [document_ setFrame:frame];
-
-            if ([scroller_ respondsToSelector:@selector(scrollPointVisibleAtTopLeft:)])
-                [scroller_ scrollPointVisibleAtTopLeft:CGPointZero];
-            else
-                [scroller_ scrollRectToVisible:CGRectZero animated:NO];
-
-            WebThreadLock();
-            [[[document_ webView] windowScriptObject] setValue:package_ forKey:@"package"];
-
-            [self setButtonTitle:nil withStyle:nil toFunction:nil];
-
-            [self setFinishHook:nil];
-            [self setPopupHook:nil];
-            WebThreadUnlock();
+    if (button_ != nil)
+        [button_ release];
 
-            //[self yieldToSelector:@selector(callFunction:) withObject:special_];
-            [super callFunction:special_];
-        }
+    NSString *title;
+    switch ([buttons_ count]) {
+        case 0: title = nil; break;
+        case 1: title = [buttons_ objectAtIndex:0]; break;
+        default: title = UCLocalize("MODIFY"); break;
     }
+
+    button_ = [[UIBarButtonItem alloc]
+        initWithTitle:title
+        style:UIBarButtonItemStylePlain
+        target:self
+        action:@selector(customButtonClicked)
+    ];
 }
 
 - (bool) isLoading {
@@ -6326,6 +6311,8 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
         [request setValue:ChipID_ forHTTPHeaderField:@"X-Chip-ID"];
     if (UniqueID_ != nil)
         [request setValue:UniqueID_ forHTTPHeaderField:@"X-Unique-ID"];
+    if (PLMN_ != nil)
+        [request setValue:PLMN_ forHTTPHeaderField:@"X-Carrier-ID"];
 }
 
 - (void) aboutButtonClicked {
@@ -6787,6 +6774,9 @@ freeing the view controllers on tab change */
 }
 
 - (void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
+    if (editing_)
+        return;
+
     Section *section = [self sectionAtIndexPath:indexPath];
     NSString *name = [section name];
     NSString *title;
@@ -6964,7 +6954,7 @@ freeing the view controllers on tab change */
     UITableViewDelegate
 > {
     _transient Database *database_;
-    NSMutableArray *packages_;
+    CFMutableArrayRef packages_;
     NSMutableArray *sections_;
     UITableView *list_;
     unsigned upgrades_;
@@ -6982,7 +6972,8 @@ freeing the view controllers on tab change */
     [list_ setDelegate:nil];
     [list_ setDataSource:nil];
 
-    [packages_ release];
+    CFRelease(packages_);
+
     [sections_ release];
     [list_ release];
     [super dealloc];
@@ -7015,10 +7006,14 @@ freeing the view controllers on tab change */
     return [[sections_ objectAtIndex:section] count];
 }
 
+- (Package *) packageAtIndex:(NSUInteger)index {
+    return (Package *) CFArrayGetValueAtIndex(packages_, index);
+}
+
 - (Package *) packageAtIndexPath:(NSIndexPath *)path {
     Section *section([sections_ objectAtIndex:[path section]]);
     NSInteger row([path row]);
-    return [packages_ objectAtIndex:([section row] + row)];
+    return [self packageAtIndex:([section row] + row)];
 }
 
 - (UITableViewCell *) tableView:(UITableView *)table cellForRowAtIndexPath:(NSIndexPath *)path {
@@ -7044,7 +7039,7 @@ freeing the view controllers on tab change */
 
 - (void) refreshButtonClicked {
     [delegate_ beginUpdate];
-    [[self navigationItem] setLeftBarButtonItem:nil];
+    [[self navigationItem] setLeftBarButtonItem:nil animated:YES];
 }
 
 - (void) upgradeButtonClicked {
@@ -7058,7 +7053,8 @@ freeing the view controllers on tab change */
         database_ = database;
         [[self navigationItem] setTitle:UCLocalize("CHANGES")];
 
-        packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
+        packages_ = CFArrayCreateMutable(kCFAllocatorDefault, 0, NULL);
+
         sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
 
         list_ = [[UITableView alloc] initWithFrame:[[self view] bounds] style:UITableViewStylePlain];
@@ -7080,25 +7076,30 @@ freeing the view controllers on tab change */
             [package uninstalled] && [package valid] && [package visible] ||
             [package upgradableAndEssential:YES]
         )
-            [packages_ addObject:package];
+            CFArrayAppendValue(packages_, package);
 
     _trace();
-    [packages_ radixSortUsingFunction:reinterpret_cast<SKRadixFunction>(&PackageChangesRadix) withContext:NULL];
+    [(NSMutableArray *) packages_ radixSortUsingFunction:reinterpret_cast<SKRadixFunction>(&PackageChangesRadix) withContext:NULL];
     _trace();
 }
 
 - (void) reloadData {
     NSArray *packages = [database_ packages];
 
-    [packages_ removeAllObjects];
+    CFArrayRemoveAllValues(packages_);
+
     [sections_ removeAllObjects];
 
+#if 0
     UIProgressHUD *hud([delegate_ addProgressHUD]);
     // XXX: localize
     [hud setText:@"Loading Changes"];
-    NSLog(@"HUD:%@::%@", delegate_, hud);
+    //NSLog(@"HUD:%@::%@", delegate_, hud);
     [self yieldToSelector:@selector(_reloadPackages:) withObject:packages];
     [delegate_ removeProgressHUD:hud];
+#else
+    [self _reloadPackages:packages];
+#endif
 
     Section *upgradable = [[[Section alloc] initWithName:UCLocalize("AVAILABLE_UPGRADES") localize:NO] autorelease];
     Section *ignored = [[[Section alloc] initWithName:UCLocalize("IGNORED_UPGRADES") localize:NO] autorelease];
@@ -7110,8 +7111,8 @@ freeing the view controllers on tab change */
 
     CFDateFormatterRef formatter(CFDateFormatterCreate(NULL, Locale_, kCFDateFormatterMediumStyle, kCFDateFormatterMediumStyle));
 
-    for (size_t offset = 0, count = [packages_ count]; offset != count; ++offset) {
-        Package *package = [packages_ objectAtIndex:offset];
+    for (size_t offset = 0, count = CFArrayGetCount(packages_); offset != count; ++offset) {
+        Package *package = [self packageAtIndex:offset];
 
         BOOL uae = [package upgradableAndEssential:YES];
 
@@ -7156,7 +7157,7 @@ freeing the view controllers on tab change */
     if (unseens) {
         Section *last = [sections_ lastObject];
         size_t count = [last count];
-        [packages_ removeObjectsInRange:NSMakeRange([packages_ count] - count, count)];
+        CFArrayReplaceValues(packages_, CFRangeMake(CFArrayGetCount(packages_) - count, count), NULL, 0);
         [sections_ removeLastObject];
     }
 
@@ -7401,7 +7402,6 @@ freeing the view controllers on tab change */
 
 @end
 /* }}} */
-
 /* Signature Controller {{{ */
 @interface SignatureController : CYBrowserController {
     _transient Database *database_;
@@ -7419,9 +7419,9 @@ freeing the view controllers on tab change */
     [super dealloc];
 }
 
-- (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
+- (void) webView:(WebView *)view didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
     // XXX: dude!
-    [super webView:sender didClearWindowObject:window forFrame:frame];
+    [super webView:view didClearWindowObject:window forFrame:frame];
 }
 
 - (id) initWithDatabase:(Database *)database package:(NSString *)package {
@@ -7438,6 +7438,7 @@ freeing the view controllers on tab change */
 
 @end
 /* }}} */
+
 /* Role Controller {{{ */
 @interface RoleController : CYViewController <
     UITableViewDataSource,
@@ -7554,11 +7555,22 @@ freeing the view controllers on tab change */
     [self showDoneButton];
 }
 
-- (void) doneButtonClicked {
+- (void) saveAndClose {
     [self save];
+
+    [[self navigationItem] setRightBarButtonItem:nil];
     [[self navigationController] dismissModalViewControllerAnimated:YES];
 }
 
+- (void) doneButtonClicked {
+    UIActivityIndicatorView *spinner = [[[UIActivityIndicatorView alloc] initWithFrame:CGRectMake(0, 0, 20.0f, 20.0f)] autorelease];
+    [spinner startAnimating];
+    UIBarButtonItem *spinItem = [[[UIBarButtonItem alloc] initWithCustomView:spinner] autorelease];
+    [[self navigationItem] setRightBarButtonItem:spinItem];
+
+    [self performSelector:@selector(saveAndClose) withObject:nil afterDelay:0];
+}
+
 - (void) showDoneButton {
     UIBarButtonItem *rightItem = [[UIBarButtonItem alloc]
         initWithTitle:UCLocalize("DONE")
@@ -7606,6 +7618,68 @@ freeing the view controllers on tab change */
     else return nil;
 }
 
+@end
+/* }}} */
+/* Stash Controller {{{ */
+@interface CYStashController : CYViewController {
+    UIActivityIndicatorView *spinner_;
+    UILabel *status_;
+    UILabel *caption_;
+}
+@end
+
+@implementation CYStashController
+- (id) init {
+    if ((self = [super init])) {
+        [[self view] setBackgroundColor:[UIColor viewFlipsideBackgroundColor]];
+
+        spinner_ = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
+        CGRect spinrect = [spinner_ frame];
+        spinrect.origin.x = ([[self view] frame].size.width / 2) - (spinrect.size.width / 2);
+        spinrect.origin.y = [[self view] frame].size.height - 80.0f;
+        [spinner_ setFrame:spinrect];
+        [spinner_ setAutoresizingMask:UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleTopMargin];
+        [[self view] addSubview:spinner_];
+        [spinner_ release];
+        [spinner_ startAnimating];
+
+        CGRect captrect;
+        captrect.size.width = [[self view] frame].size.width;
+        captrect.size.height = 40.0f;
+        captrect.origin.x = 0;
+        captrect.origin.y = ([[self view] frame].size.height / 2) - (captrect.size.height * 2);
+        caption_ = [[UILabel alloc] initWithFrame:captrect];
+        [caption_ setText:@"Initializing Filesystem"];
+        [caption_ setAutoresizingMask:UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleBottomMargin];
+        [caption_ setFont:[UIFont boldSystemFontOfSize:28.0f]];
+        [caption_ setTextColor:[UIColor whiteColor]];
+        [caption_ setBackgroundColor:[UIColor clearColor]];
+        [caption_ setShadowColor:[UIColor blackColor]];
+        [caption_ setTextAlignment:UITextAlignmentCenter];
+        [[self view] addSubview:caption_];
+        [caption_ release];
+
+        CGRect statusrect;
+        statusrect.size.width = [[self view] frame].size.width;
+        statusrect.size.height = 30.0f;
+        statusrect.origin.x = 0;
+        statusrect.origin.y = ([[self view] frame].size.height / 2) - statusrect.size.height;
+        status_ = [[UILabel alloc] initWithFrame:statusrect];
+        [status_ setAutoresizingMask:UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleBottomMargin];
+        [status_ setText:@"(Cydia will exit when complete.)"];
+        [status_ setFont:[UIFont systemFontOfSize:16.0f]];
+        [status_ setTextColor:[UIColor whiteColor]];
+        [status_ setBackgroundColor:[UIColor clearColor]];
+        [status_ setShadowColor:[UIColor blackColor]];
+        [status_ setTextAlignment:UITextAlignmentCenter];
+        [[self view] addSubview:status_];
+        [status_ release];
+    } return self;
+}
+
+- (BOOL) shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)orientation {
+    return IsWildcat_ || orientation == UIInterfaceOrientationPortrait;
+}
 @end
 /* }}} */
 
@@ -7616,7 +7690,7 @@ freeing the view controllers on tab change */
 
     bool dropped_;
     bool updating_;
-    id updatedelegate_;
+    NSObject<CydiaDelegate> *updatedelegate_;
     UITabBarController *root_;
 }
 
@@ -7659,7 +7733,7 @@ freeing the view controllers on tab change */
 }
 
 - (BOOL) shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)orientation {
-    return IsWildcat_;
+    return ![updatedelegate_ hudIsShowing] && (IsWildcat_ || orientation == UIInterfaceOrientationPortrait);
 }
 
 - (void) setTabBarController:(UITabBarController *)controller {
@@ -7697,6 +7771,7 @@ freeing the view controllers on tab change */
 }
 
 - (void) completeUpdate {
+    if (!updating_) return;
     updating_ = false;
 
     [self raiseBar:YES];
@@ -7705,8 +7780,10 @@ freeing the view controllers on tab change */
 }
 
 - (void) cancelUpdate {
-    [refreshbar_ cancel];
-    [self completeUpdate];
+    updating_ = false;
+    [self raiseBar:YES];
+    [refreshbar_ stop];
+    [updatedelegate_ performSelector:@selector(updateData) withObject:nil afterDelay:0];
 }
 
 - (void) cancelPressed {
@@ -7882,7 +7959,6 @@ typedef enum {
 > {
     UIWindow *window_;
     CYContainer *container_;
-
     id tabbar_;
 
     NSMutableArray *essential_;
@@ -7893,7 +7969,7 @@ typedef enum {
     int tag_;
 
     UIKeyboard *keyboard_;
-    UIProgressHUD *hud_;
+    int huds_;
 
     SectionsController *sections_;
     ChangesController *changes_;
@@ -7903,9 +7979,7 @@ typedef enum {
     InstalledController *installed_;
     id queueDelegate_;
 
-#if RecyclePackageViews
-    NSMutableArray *details_;
-#endif
+    CYStashController *stash_;
 
     bool loaded_;
 }
@@ -8010,6 +8084,30 @@ static _finline void _setHomePage(Cydia *self) {
 - (void) _refreshIfPossible {
     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
 
+    bool recently = false;
+    NSDate *update([Metadata_ objectForKey:@"LastUpdate"]);
+    if (update != nil) {
+        NSTimeInterval interval([update timeIntervalSinceNow]);
+        if (interval <= 0 && interval > -(15*60))
+            recently = true;
+    }
+
+    // Don't automatic refresh if:
+    //  - We already refreshed recently.
+    //  - We already auto-refreshed this launch.
+    //  - Auto-refresh is disabled.
+    if (recently || loaded_ || ManualRefresh) {
+        [self performSelectorOnMainThread:@selector(_loaded) withObject:nil waitUntilDone:NO];
+
+        // If we are cancelling due to ManualRefresh or a recent refresh
+        // we need to make sure it knows it's already loaded.
+        loaded_ = true;
+        return;
+    } else {
+        // We are going to load, so remember that.
+        loaded_ = true;
+    }
+
     SCNetworkReachabilityFlags flags; {
         SCNetworkReachabilityRef reachability(SCNetworkReachabilityCreateWithName(NULL, "cydia.saurik.com"));
         SCNetworkReachabilityGetFlags(reachability, &flags);
@@ -8028,21 +8126,9 @@ static _finline void _setHomePage(Cydia *self) {
         )
     );
 
-    if (loaded_ || ManualRefresh || !reachable) loaded:
-        [self performSelectorOnMainThread:@selector(_loaded) withObject:nil waitUntilDone:NO];
-    else {
-        loaded_ = true;
-
-        NSDate *update([Metadata_ objectForKey:@"LastUpdate"]);
-
-        if (update != nil) {
-            NSTimeInterval interval([update timeIntervalSinceNow]);
-            if (interval <= 0 && interval > -(15*60))
-                goto loaded;
-        }
-
+    // If we can reach the server, auto-refresh!
+    if (reachable)
         [container_ performSelectorOnMainThread:@selector(setUpdate:) withObject:update waitUntilDone:NO];
-    }
 
     [pool release];
 }
@@ -8052,13 +8138,12 @@ static _finline void _setHomePage(Cydia *self) {
 }
 
 - (void) _reloadData {
-    UIProgressHUD *hud([self addProgressHUD]);
-    [hud setText:(loaded_ ? UCLocalize("RELOADING_DATA") : UCLocalize("LOADING_DATA"))];
+    UIProgressHUD *hud(loaded_ ? [self addProgressHUD] : nil);
+    [hud setText:UCLocalize("RELOADING_DATA")];
 
     [database_ yieldToSelector:@selector(reloadData) withObject:nil];
-    _trace();
 
-    [self removeProgressHUD:hud];
+    if (hud) [self removeProgressHUD:hud];
 
     size_t changes(0);
 
@@ -8076,18 +8161,19 @@ static _finline void _setHomePage(Cydia *self) {
         }
     }
 
+    UITabBarItem *changesItem = [[[tabbar_ viewControllers] objectAtIndex:[self indexOfTabWithTag:kChangesTag]] tabBarItem];
     if (changes != 0) {
         NSString *badge([[NSNumber numberWithInt:changes] stringValue]);
-        [[[[tabbar_ viewControllers] objectAtIndex:[self indexOfTabWithTag:kChangesTag]] tabBarItem] setBadgeValue:badge];
-        [[[[tabbar_ viewControllers] objectAtIndex:[self indexOfTabWithTag:kChangesTag]] tabBarItem] setAnimatedBadge:YES];
+        [changesItem setBadgeValue:badge];
+        [changesItem setAnimatedBadge:([essential_ count] > 0)];
 
         if ([self respondsToSelector:@selector(setApplicationBadge:)])
             [self setApplicationBadge:badge];
         else
             [self setApplicationBadgeString:badge];
     } else {
-        [[[[tabbar_ viewControllers] objectAtIndex:[self indexOfTabWithTag:kChangesTag]] tabBarItem] setBadgeValue:nil];
-        [[[[tabbar_ viewControllers] objectAtIndex:[self indexOfTabWithTag:kChangesTag]] tabBarItem] setAnimatedBadge:NO];
+        [changesItem setBadgeValue:nil];
+        [changesItem setAnimatedBadge:NO];
 
         if ([self respondsToSelector:@selector(removeApplicationBadge)])
             [self removeApplicationBadge];
@@ -8343,10 +8429,6 @@ static _finline void _setHomePage(Cydia *self) {
 - (void) setPackageController:(PackageController *)view {
     WebThreadLock();
     [view setPackage:nil];
-#if RecyclePackageViews
-    if ([details_ count] < 3)
-        [details_ addObject:view];
-#endif
     WebThreadUnlock();
 }
 
@@ -8355,25 +8437,7 @@ static _finline void _setHomePage(Cydia *self) {
 }
 
 - (PackageController *) packageController {
-#if RecyclePackageViews
-    PackageController *view;
-    size_t count([details_ count]);
-
-    if (count == 0) {
-        view = [self _packageController];
-      renew:
-        [details_ addObject:[self _packageController]];
-    } else {
-        view = [[[details_ lastObject] retain] autorelease];
-        [details_ removeLastObject];
-        if (count == 1)
-            goto renew;
-    }
-
-    return view;
-#else
     return [self _packageController];
-#endif
 }
 
 // Returns the navigation controller for the queuing badge.
@@ -8466,20 +8530,31 @@ static _finline void _setHomePage(Cydia *self) {
     [super applicationWillSuspend];
 }
 
+- (BOOL) hudIsShowing {
+    return (huds_ > 0);
+}
+
 - (void) applicationSuspend:(__GSEvent *)event {
-    // FIXME: This needs to be fixed, but we no longer have a progress_.
-    //        What's the best solution?
-    if (hud_ == nil)// && ![progress_ isRunning])
+    // Use external process status API internally.
+    // This is probably a really bad idea.
+    uint64_t status = 0;
+    int notify_token;
+    if (notify_register_check("com.saurik.Cydia.status", &notify_token) == NOTIFY_STATUS_OK) {
+        notify_get_state(notify_token, &status);
+        notify_cancel(notify_token);
+    }
+
+    if (![self hudIsShowing] && status == 0)
         [super applicationSuspend:event];
 }
 
 - (void) _animateSuspension:(BOOL)arg0 duration:(double)arg1 startTime:(double)arg2 scale:(float)arg3 {
-    if (hud_ == nil)
+    if (![self hudIsShowing])
         [super _animateSuspension:arg0 duration:arg1 startTime:arg2 scale:arg3];
 }
 
 - (void) _setSuspended:(BOOL)value {
-    if (hud_ == nil)
+    if (![self hudIsShowing])
         [super _setSuspended:value];
 }
 
@@ -8489,7 +8564,12 @@ static _finline void _setHomePage(Cydia *self) {
 
     [window_ setUserInteractionEnabled:NO];
     [hud show:YES];
-    [[container_ view] addSubview:hud];
+
+    UIViewController *target = container_;
+    while ([target modalViewController] != nil) target = [target modalViewController];
+    [[target view] addSubview:hud];
+
+    huds_++;
     return hud;
 }
 
@@ -8497,6 +8577,7 @@ static _finline void _setHomePage(Cydia *self) {
     [hud show:NO];
     [hud removeFromSuperview];
     [window_ setUserInteractionEnabled:YES];
+    huds_--;
 }
 
 - (CYViewController *) pageForPackage:(NSString *)name {
@@ -8578,6 +8659,65 @@ static _finline void _setHomePage(Cydia *self) {
         [super applicationWillResignActive:application];
 }
 
+- (void) addStashController {
+    stash_ = [[CYStashController alloc] init];
+    [window_ addSubview:[stash_ view]];
+}
+
+- (void) removeStashController {
+    [[stash_ view] removeFromSuperview];
+    [stash_ release];
+}
+
+- (void) stash {
+    [self setIdleTimerDisabled:YES];
+
+    [[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleBlackOpaque];
+    [self setStatusBarShowsProgress:YES];
+    UpdateExternalStatus(1);
+
+    [self yieldToSelector:@selector(system:) withObject:@"/usr/libexec/cydia/free.sh"];
+
+    UpdateExternalStatus(0);
+    [self setStatusBarShowsProgress:NO];
+
+    [self removeStashController];
+
+    if (ExecFork() == 0) {
+        execlp("launchctl", "launchctl", "stop", "com.apple.SpringBoard", NULL);
+        perror("launchctl stop");
+    }
+}
+
+- (void) setupTabBarController {
+    tabbar_ = [[CYTabBarController alloc] initWithDatabase:database_];
+    [tabbar_ setDelegate:self];
+
+    NSMutableArray *items([NSMutableArray arrayWithObjects:
+        [[[UITabBarItem alloc] initWithTitle:@"Cydia" image:[UIImage applicationImageNamed:@"home.png"] tag:kCydiaTag] autorelease],
+        [[[UITabBarItem alloc] initWithTitle:UCLocalize("SECTIONS") image:[UIImage applicationImageNamed:@"install.png"] tag:kSectionsTag] autorelease],
+        [[[UITabBarItem alloc] initWithTitle:UCLocalize("CHANGES") image:[UIImage applicationImageNamed:@"changes.png"] tag:kChangesTag] autorelease],
+        [[[UITabBarItem alloc] initWithTitle:UCLocalize("SEARCH") image:[UIImage applicationImageNamed:@"search.png"] tag:kSearchTag] autorelease],
+    nil]);
+
+    if (IsWildcat_) {
+        [items insertObject:[[[UITabBarItem alloc] initWithTitle:UCLocalize("SOURCES") image:[UIImage applicationImageNamed:@"source.png"] tag:kSourcesTag] autorelease] atIndex:3];
+        [items insertObject:[[[UITabBarItem alloc] initWithTitle:UCLocalize("INSTALLED") image:[UIImage applicationImageNamed:@"manage.png"] tag:kInstalledTag] autorelease] atIndex:3];
+    } else {
+        [items insertObject:[[[UITabBarItem alloc] initWithTitle:UCLocalize("MANAGE") image:[UIImage applicationImageNamed:@"manage.png"] tag:kManageTag] autorelease] atIndex:3];
+    }
+
+    NSMutableArray *controllers([NSMutableArray array]);
+
+    for (UITabBarItem *item in items) {
+        CYNavigationController *controller([[[CYNavigationController alloc] initWithDatabase:database_] autorelease]);
+        [controller setTabBarItem:item];
+        [controllers addObject:controller];
+    }
+
+    [tabbar_ setViewControllers:controllers];
+}
+
 - (void) applicationDidFinishLaunching:(id)unused {
     [CYBrowserController _initialize];
 
@@ -8601,8 +8741,6 @@ static _finline void _setHomePage(Cydia *self) {
     [window_ makeKey:self];
     [window_ setHidden:NO];
 
-    database_ = [Database sharedInstance];
-
     if (
         readlink("/Applications", NULL, 0) == -1 && errno == EINVAL ||
         readlink("/Library/Ringtones", NULL, 0) == -1 && errno == EINVAL ||
@@ -8615,82 +8753,89 @@ static _finline void _setHomePage(Cydia *self) {
         //readlink("/var/lib", NULL, 0) == -1 && errno == EINVAL ||
         false
     ) {
-        [self setIdleTimerDisabled:YES];
-
-        hud_ = [self addProgressHUD];
-        [hud_ setText:@"Reorganizing:\n\nWill Automatically\nClose When Done"];
-        [self setStatusBarShowsProgress:YES];
-
-        [self yieldToSelector:@selector(system:) withObject:@"/usr/libexec/cydia/free.sh"];
-
-        [self setStatusBarShowsProgress:NO];
-        [self removeProgressHUD:hud_];
-        hud_ = nil;
-
-        if (ExecFork() == 0) {
-            execlp("launchctl", "launchctl", "stop", "com.apple.SpringBoard", NULL);
-            perror("launchctl stop");
-        }
-
+        [self addStashController];
+        [self performSelector:@selector(stash) withObject:nil afterDelay:0];
         return;
     }
 
-    _trace();
-
-    NSMutableArray *items([NSMutableArray arrayWithObjects:
-        [[[UITabBarItem alloc] initWithTitle:@"Cydia" image:[UIImage applicationImageNamed:@"home.png"] tag:kCydiaTag] autorelease],
-        [[[UITabBarItem alloc] initWithTitle:UCLocalize("SECTIONS") image:[UIImage applicationImageNamed:@"install.png"] tag:kSectionsTag] autorelease],
-        [[[UITabBarItem alloc] initWithTitle:UCLocalize("CHANGES") image:[UIImage applicationImageNamed:@"changes.png"] tag:kChangesTag] autorelease],
-        [[[UITabBarItem alloc] initWithTitle:UCLocalize("SEARCH") image:[UIImage applicationImageNamed:@"search.png"] tag:kSearchTag] autorelease],
-    nil]);
-
-    if (IsWildcat_) {
-        [items insertObject:[[[UITabBarItem alloc] initWithTitle:UCLocalize("SOURCES") image:[UIImage applicationImageNamed:@"source.png"] tag:kSourcesTag] autorelease] atIndex:3];
-        [items insertObject:[[[UITabBarItem alloc] initWithTitle:UCLocalize("INSTALLED") image:[UIImage applicationImageNamed:@"manage.png"] tag:kInstalledTag] autorelease] atIndex:3];
-    } else {
-        [items insertObject:[[[UITabBarItem alloc] initWithTitle:UCLocalize("MANAGE") image:[UIImage applicationImageNamed:@"manage.png"] tag:kManageTag] autorelease] atIndex:3];
-    }
-
-    NSMutableArray *controllers([NSMutableArray array]);
+    database_ = [Database sharedInstance];
 
-    for (UITabBarItem *item in items) {
-        CYNavigationController *controller([[[CYNavigationController alloc] initWithDatabase:database_] autorelease]);
-        [controller setTabBarItem:item];
-        [controllers addObject:controller];
-    }
-
-    tabbar_ = [[CYTabBarController alloc] initWithDatabase:database_];
-    [tabbar_ setViewControllers:controllers];
-    [tabbar_ setDelegate:self];
-    [tabbar_ setSelectedIndex:0];
+    [self setupTabBarController];
 
     container_ = [[CYContainer alloc] initWithDatabase:database_];
     [container_ setUpdateDelegate:self];
     [container_ setTabBarController:tabbar_];
     [window_ addSubview:[container_ view]];
 
+    // Show pinstripes while loading data.
+    [[container_ view] setBackgroundColor:[UIColor pinStripeColor]];
+
     [self performSelector:@selector(loadData) withObject:nil afterDelay:0];
+_trace();
 }
 
 - (void) loadData {
+_trace();
     if (Role_ == nil) {
         [self showSettings];
         return;
     }
 
-    [UIKeyboard initImplementationNow];
-
-    [self reloadData];
+    [window_ setUserInteractionEnabled:NO];
 
-#if RecyclePackageViews
-    details_ = [[NSMutableArray alloc] initWithCapacity:4];
-    [details_ addObject:[self _packageController]];
-    [details_ addObject:[self _packageController]];
-#endif
+    UIView *container = [[UIView alloc] init];
+    [container setAutoresizingMask:UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleBottomMargin];
+
+    UIActivityIndicatorView *spinner = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
+    [spinner startAnimating];
+    [container addSubview:spinner];
+    [spinner release];
+
+    UILabel *label = [[UILabel alloc] init];
+    [label setFont:[UIFont boldSystemFontOfSize:15.0f]];
+    [label setBackgroundColor:[UIColor clearColor]];
+    [label setTextColor:[UIColor blackColor]];
+    [label setShadowColor:[UIColor whiteColor]];
+    [label setShadowOffset:CGSizeMake(0, 1)];
+    [label setText:UCLocalize("LOADING_DATA")];
+    [container addSubview:label];
+    [label release];
+
+    CGSize viewsize = [[tabbar_ view] frame].size;
+    CGSize spinnersize = [spinner bounds].size;
+    CGSize textsize = [[label text] sizeWithFont:[label font]];
+    float bothwidth = spinnersize.width + textsize.width + 5.0f;
+
+    CGRect containrect = {
+        CGPointMake(floorf((viewsize.width / 2) - (bothwidth / 2)), floorf((viewsize.height / 2) - (spinnersize.height / 2))),
+        CGSizeMake(bothwidth, spinnersize.height)
+    };
+    CGRect textrect = {
+        CGPointMake(spinnersize.width + 5.0f, floorf((spinnersize.height / 2) - (textsize.height / 2))),
+        textsize
+    };
+    CGRect spinrect = {
+        CGPointZero,
+        spinnersize
+    };
+
+    [container setFrame:containrect];
+    [spinner setFrame:spinrect];
+    [label setFrame:textrect];
+    [[container_ view] addSubview:container];
+    [container release];
 
+    [self reloadData];
     PrintTimes();
 
+    // Show the home page
+    [tabbar_ setSelectedIndex:0];
     _setHomePage(self);
+    [window_ setUserInteractionEnabled:YES];
+
+    // XXX: does this actually slow anything down?
+    [[container_ view] setBackgroundColor:[UIColor clearColor]];
+    [container removeFromSuperview];
 }
 
 - (void) showActionSheet:(UIActionSheet *)sheet fromItem:(UIBarButtonItem *)item {
@@ -8886,6 +9031,22 @@ int main(int argc, char *argv[]) { _pooled
 
     UniqueID_ = [[UIDevice currentDevice] uniqueIdentifier];
 
+    CFStringRef (*$CTSIMSupportCopyMobileSubscriberCountryCode)(CFAllocatorRef);
+    $CTSIMSupportCopyMobileSubscriberCountryCode = reinterpret_cast<CFStringRef (*)(CFAllocatorRef)>(dlsym(RTLD_DEFAULT, "CTSIMSupportCopyMobileSubscriberCountryCode"));
+    CFStringRef mcc($CTSIMSupportCopyMobileSubscriberCountryCode == NULL ? NULL : (*$CTSIMSupportCopyMobileSubscriberCountryCode)(kCFAllocatorDefault));
+
+    CFStringRef (*$CTSIMSupportCopyMobileSubscriberNetworkCode)(CFAllocatorRef);
+    $CTSIMSupportCopyMobileSubscriberNetworkCode = reinterpret_cast<CFStringRef (*)(CFAllocatorRef)>(dlsym(RTLD_DEFAULT, "CTSIMSupportCopyMobileSubscriberCountryCode"));
+    CFStringRef mnc($CTSIMSupportCopyMobileSubscriberNetworkCode == NULL ? NULL : (*$CTSIMSupportCopyMobileSubscriberNetworkCode)(kCFAllocatorDefault));
+
+    if (mcc != NULL && mnc != NULL)
+        PLMN_ = [NSString stringWithFormat:@"%@%@", mcc, mnc];
+
+    if (mnc != NULL)
+        CFRelease(mnc);
+    if (mcc != NULL)
+        CFRelease(mcc);
+
     if (NSDictionary *system = [NSDictionary dictionaryWithContentsOfFile:@"/System/Library/CoreServices/SystemVersion.plist"])
         Build_ = [system objectForKey:@"ProductBuildVersion"];
     if (NSDictionary *info = [NSDictionary dictionaryWithContentsOfFile:@"/Applications/MobileSafari.app/Info.plist"]) {
@@ -8898,7 +9059,6 @@ int main(int argc, char *argv[]) { _pooled
     Metadata_ = [[[NSMutableDictionary alloc] initWithContentsOfFile:@"/var/lib/cydia/metadata.plist"] autorelease];
     _trace();
     SectionMap_ = [[[NSDictionary alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"Sections" ofType:@"plist"]] autorelease];
-    _trace();
 
     if (Metadata_ == NULL)
         Metadata_ = [NSMutableDictionary dictionaryWithCapacity:2];
@@ -8931,10 +9091,6 @@ int main(int argc, char *argv[]) { _pooled
     }
     /* }}} */
 
-#if RecycleWebViews
-    Documents_ = [[[NSMutableArray alloc] initWithCapacity:4] autorelease];
-#endif
-
     Finishes_ = [NSArray arrayWithObjects:@"return", @"reopen", @"restart", @"reload", @"reboot", nil];
 
     if (substrate && access("/Library/MobileSubstrate/DynamicLibraries/SimulatedKeyEvents.dylib", F_OK) == 0)
@@ -8976,7 +9132,10 @@ int main(int argc, char *argv[]) { _pooled
 
     if (lang != NULL)
         _config->Set("APT::Acquire::Translation", lang);
-    _config->Set("Acquire::http::Timeout", 15);
+
+    // XXX: this timeout might be important :(
+    //_config->Set("Acquire::http::Timeout", 15);
+
     _config->Set("Acquire::http::MaxParallel", 3);
     /* }}} */
     /* Color Choices {{{ */