//
// JSbdecoder.m
// BitMaster
//
// Created by Jon Shier on 4/9/07.
// Copyright 2007 __MyCompanyName__. All rights reserved.
//
#import "JSbdecoder.h"
@implementation JSbdecoder
- (NSDictionary *) decodeTorrentFile: (NSString *) pathToTorrentFile {
torrentData = [NSData dataWithContentsOfFile: pathToTorrentFile options: NSUncachedRead error: nil];
rawTorrentData = [torrentData bytes];
position = 0;
return [self convertObjectDataToStrings:[[self bdecodeData] objectAtIndex: 0]];
}
- (NSArray *) bdecodeData {
NSMutableArray *methodArray = [NSMutableArray array];
do {
if(rawTorrentData[position] == 'i') {
position++;
[methodArray addObject:[self decodeInt]];
} else if(rawTorrentData[position] == 'l') {
position++;
[methodArray addObject:[self decodeList]];
} else if(rawTorrentData[position] == 'd') {
position++;
[methodArray addObject:[self decodeDictionary]];
} else if(isdigit(rawTorrentData[position])) {
[methodArray addObject:[self decodeBytes]];
} else if(rawTorrentData[position] == 'e') {
position++;
//Should only hit this when at the end of lists, dictionaries, or integers.
break;
} else {
NSLog(@"Error decoding torrent. Unexpected character '%c' at position %d.", rawTorrentData[position], position);
return methodArray;
}
} while (position < [torrentData length]);
return methodArray;
}
- (NSNumber *) decodeInt {
NSUInteger methodPosition = position;
while(rawTorrentData[position] != 'e') {
position++;
}
position++;
return [NSNumber numberWithInteger: [[NSString stringWithUTF8String: [[torrentData subdataWithRange: NSMakeRange(methodPosition, (position - methodPosition))] bytes]] integerValue]];
}
- (NSData *) decodeBytes {
NSUInteger methodPosition = position;
while(rawTorrentData[position] != ':') {
position++;
}
NSUInteger bytesLength = [[NSString stringWithUTF8String: [[torrentData subdataWithRange: NSMakeRange(methodPosition, (position - methodPosition))] bytes]] integerValue];
position++;
methodPosition = position;
position += bytesLength;
return [torrentData subdataWithRange: NSMakeRange(methodPosition, bytesLength)];
}
- (NSArray *) decodeList {
return [self bdecodeData];
}
- (NSDictionary *) decodeDictionary {
NSMutableDictionary *methodDictionary = [NSMutableDictionary dictionary];
NSArray *methodArray = [NSArray arrayWithArray:[self bdecodeData]];
for(NSUInteger i = 0; i < [methodArray count]; i += 2) {
[methodDictionary setObject: [methodArray objectAtIndex: i + 1] forKey: [methodArray objectAtIndex: i]];
}
return methodDictionary;
}
- (id) convertObjectDataToStrings: (id) object {
if ([object conformsToProtocol: @protocol(NSFastEnumeration)]) {
NSMutableArray *valueArray = [NSMutableArray array];
if ([object isKindOfClass: [NSArray class]]) {
for (id enumeratedObject in object) {
[valueArray addObject: [self convertObjectDataToStrings: enumeratedObject]];
}
return valueArray;
} else if ([object isKindOfClass: [NSDictionary class]]) {
valueArray = [[object allValues] mutableCopy];
NSMutableArray *keyArray = [NSMutableArray arrayWithArray: [object allKeys]];
for (NSUInteger i = 0; i < [keyArray count]; i++) {
[keyArray replaceObjectAtIndex: i withObject: [self convertObjectDataToStrings: [keyArray objectAtIndex: i]]];
}
for (NSUInteger i = 0; i < [valueArray count]; i++) {
if (![[keyArray objectAtIndex: i] isEqualToString: @"pieces"]) {
[valueArray replaceObjectAtIndex: i withObject: [self convertObjectDataToStrings: [valueArray objectAtIndex: i]]];
}
}
return [NSDictionary dictionaryWithObjects: valueArray forKeys: keyArray];
}
} else if ([object isKindOfClass: [NSNumber class]]) {
return object;
} else if ([object isKindOfClass: [NSData class]]) {
return [[NSString alloc] initWithBytes: [object bytes] length: [object length] encoding: NSUTF8StringEncoding];
} else {
NSLog(@"Unexpected object %@ of class %@", object, [object class]);
}
return object;
}
@end