// // MBackup.m // CellLocations // // Created by Simon Urbanek on 4/22/11. // Copyright 2011 Simon Urbanek. All rights reserved. // #import "MBackup.h" #pragma pack(push) #pragma pack(2) typedef struct mbdx_record { unsigned char key[20]; unsigned char offset[4]; unsigned char mode[2]; } mbdx_record; #pragma pack(pop) @interface MBDXFile : NSObject { NSInteger records; NSData *data; mbdx_record *index; } - (id) initWithPath: (NSString*) aPath; - (NSString*) keyForOffset: (NSUInteger) off; @end @implementation MBDXFile - (id) initWithPath: (NSString*) aPath { if ((self = [super init])) { NSFileHandle *f = [NSFileHandle fileHandleForReadingAtPath:aPath]; if (f) { NSData *header = [f readDataOfLength:10]; const unsigned char *hd = (const unsigned char*) [header bytes]; if ([header length] == 10 && hd[0] == 'm' && hd[1] == 'b' && hd[2] == 'd' && hd[3] == 'x') { records = ((unsigned int) hd[9]) | ((unsigned int) hd[8]) << 8 | ((unsigned int) hd[7]) << 16 | ((unsigned int) hd[6]) << 24; data = [f readDataToEndOfFile]; index = (mbdx_record*) [data bytes]; if ([data length] < records * sizeof(mbdx_record)) { NSLog(@"CellLocations.MBackup: truncated MBDX file %@ (expected %d, encountered %d)", aPath, records * sizeof(mbdx_record), [data length]); records = [data length] / sizeof(mbdx_record); } [data retain]; NSLog(@" - %d records", records); return self; } [f closeFile]; } [self release]; self = nil; } return self; } - (void) dealloc { if (data) [data release]; [super dealloc]; } - (NSString*) keyForOffset: (NSUInteger) off { int i = 0; while (i < records) { unsigned int ro = ((unsigned int)index[i].offset[3]) | ((unsigned int)index[i].offset[2]) << 8 | ((unsigned int)index[i].offset[1]) << 16 | ((unsigned int)index[i].offset[0]) << 24; if (ro >= off && ro < off + 20) { NSLog(@"keyForOffset: record %d at offset %x (matching %x)", i, ro, off); char buf[42], *c = buf; int j; for (j = 0; j < 20; j++, c += 2) sprintf(c, "%02x", (int) index[i].key[j]); buf[40] = 0; return [NSString stringWithUTF8String:buf]; } i++; } return nil; } @end @implementation MBackup - (id) initWithPath: (NSString*) aPath { if ((self = [super init])) { path = [aPath copy]; info = [NSDictionary dictionaryWithContentsOfFile:[path stringByAppendingString:@"/Info.plist"]]; if (info) [info retain]; mbdx = [[MBDXFile alloc] initWithPath:[path stringByAppendingString:@"/Manifest.mbdx"]]; } return self; } - (void) dealloc { if (path) [path release]; if (info) [info release]; if (mbdx) [mbdx release]; [super dealloc]; } - (BOOL) hasInfo { return (info != nil); } - (BOOL) hasManifest { return (mbdx != nil); } - (NSDictionary*) info { return info; } - (NSString*) path { return path; } - (NSString*) keyForPathContaining: (NSString*) substring { if (!mbdx) return nil; NSFileHandle *fh = [NSFileHandle fileHandleForReadingAtPath:[path stringByAppendingString:@"/Manifest.mbdb"]]; if (fh) { const char *csub = [substring UTF8String]; if (csub) { unsigned int sublen = strlen(csub); NSData *cont = [fh readDataToEndOfFile]; const char *cc = [cont bytes], *oc = cc, *cs = cc + [cont length] - sublen; while (cc < cs) { if (*cc == *csub && !memcmp(cc, csub, sublen)) { NSLog(@"found match at %x", cc - oc); /* rewind two NULs back - one for the path length and the other for the domain length. * we are assuming that both involved strings are less than 256 characters long */ while (cc > oc && *cc) cc--; /* rewind to previous NUL */ if (cc > oc) cc--; while (cc > oc && *cc) cc--; /* rewind to previous NUL */ NSLog(@" - records probably starts at %x", cc - oc); return [mbdx keyForOffset: (cc - oc < 6) ? 0 : (cc - oc - 6)]; } cc++; } } } return nil; } @end