#import "APLFile.h" #import "ApeTag.h" @implementation APLFile +createWithFile:(NSString*)f { return [[APLFile alloc] initWithFile:f]; } -(NSString*) readline:(NSFileHandle*)f { // rather hack-style substitution to fgets... NSMutableData* d = [NSMutableData dataWithCapacity:100]; //it will grow, here should be most expected value (may gain few nanosecond from it =) ) while(true) { NSData* byte = [f readDataOfLength:1]; if (!byte) { [f seekToFileOffset:([f offsetInFile]-[d length])]; return nil; } [d appendData:byte]; if (*((const char*)[byte bytes]) == '\n') break; } NSString* s = [[NSString alloc] initWithData:d encoding:NSUTF8StringEncoding]; //!handle encoding error (in case binary data starts not from newline, impossible on real apl, but who knows...) return s; } - (NSURL *)urlForPath:(NSString *)path relativeTo:(NSString *)baseFilename { NSRange protocolRange = [path rangeOfString:@"://"]; if (protocolRange.location != NSNotFound) return [NSURL URLWithString:path]; NSMutableString *unixPath = [path mutableCopy]; if (![unixPath hasPrefix:@"/"]) { //Only relative paths would have windows backslashes. [unixPath replaceOccurrencesOfString:@"\\" withString:@"/" options:0 range:NSMakeRange(0, [unixPath length])]; NSString *basePath = [[[baseFilename stringByStandardizingPath] stringByDeletingLastPathComponent] stringByAppendingString:@"/"]; [unixPath insertString:basePath atIndex:0]; } NSURL *url = [NSURL URLWithString:[[NSURL fileURLWithPath:unixPath] absoluteString]]; [unixPath release]; return url; } -initWithFile:(NSString*)filename { self = [super init]; if (self) { //startBlock must be always >= 0 NSFileHandle* f = (NSFileHandle*)[NSFileHandle fileHandleForReadingAtPath:filename]; if(!f){ NSLog(@"Failed to open apl file '%@' for reading", f); return nil; } NSString* header = @"[Monkey's Audio Image Link File]\r\n"; NSData* da = [f readDataOfLength:[header length]]; if (!da) { NSLog(@"Cannot read header"); return nil; } NSString* str = [[[NSString alloc] autorelease] initWithData:da encoding: NSASCIIStringEncoding]; if([str compare:header options:NSCaseInsensitiveSearch]) { NSLog(@"APL header mismatch"); return nil; } //now read by lines, skip empty, up to line (or any starting with '-' - may be other tags can be present) NSString* line = nil; NSScanner *scanner = nil; //NSCharacterSet *whitespace = [NSCharacterSet whitespaceAndNewlineCharacterSet]; while((line = [self readline:f])) { if (![line compare:@"----- APE TAG (DO NOT TOUCH!!!) -----\r\n" options:NSCaseInsensitiveSearch]) break; if([line characterAtIndex:0] == '-') break; [scanner release]; scanner = [[NSScanner alloc] initWithString:line]; NSString* field = nil, *value = nil; if (![scanner scanUpToString:@"=" intoString:&field]) continue; if (![scanner scanString:@"=" intoString:nil]) continue; if (![scanner scanUpToString:@"\r\n" intoString:&value]) continue; if (![field compare:@"Image File" options:NSCaseInsensitiveSearch]) { [file release]; file = [self urlForPath:value relativeTo:filename]; NSLog(@"APL refers to file '%@' read '%@'", file, value); continue; } if (![field compare:@"Start Block" options:NSCaseInsensitiveSearch]) { startBlock = [value intValue]; //!!! bugs with files over 2GB //NSLog(@"APL start block %d (%@)", startBlock, value); continue; } if (![field compare:@"Finish Block" options:NSCaseInsensitiveSearch]) { endBlock = [value intValue]; //!!! bugs with files over 2GB //NSLog(@"APL start block %d (%@)", endBlock, value); continue; } } [scanner release]; //check here for EOF? cocoa does not have this functionality :( tag = [ApeTag createFromFileRead:f]; [f closeFile]; } return self; } -(long)startBlock { return startBlock; } -(long)endBlock { return endBlock; } -(ApeTag*) tag { return tag; } -(NSURL*) file { return file; } @end