123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408 |
- //
- // NBTFile.m
- // InsideJob
- //
- // Created by Adam Preble on 10/6/10.
- // Copyright 2010 Adam Preble. All rights reserved.
- //
- // Spec for the Named Binary Tag format: http://www.minecraft.net/docs/NBT.txt
- #import "NBTContainer.h"
- #import "NSData+CocoaDevAdditions.h"
- #ifndef NBT_LOGGING
- #define NBT_LOGGING 0
- #endif
- #if NBT_LOGGING
- #define NBTLog(format, ...) NSLog(format, ##__VA_ARGS__)
- #else
- #define NBTLog(format, ...) while(0)
- #endif
- @interface NBTContainer ()
- - (void)populateWithBytes:(const uint8_t *)bytes offset:(uint32_t *)offsetPointer;
- - (uint8_t)byteFromBytes:(const uint8_t *)bytes offset:(uint32_t *)offsetPointer;
- - (uint16_t)shortFromBytes:(const uint8_t *)bytes offset:(uint32_t *)offsetPointer;
- - (uint32_t)intFromBytes:(const uint8_t *)bytes offset:(uint32_t *)offsetPointer;
- - (uint64_t)longFromBytes:(const uint8_t *)bytes offset:(uint32_t *)offsetPointer;
- - (NSString *)stringFromBytes:(const uint8_t *)bytes offset:(uint32_t *)offsetPointer;
- - (void)appendString:(NSString *)str toData:(NSMutableData *)data;
- - (void)appendByte:(uint8_t)v toData:(NSMutableData *)data;
- - (void)appendShort:(uint16_t)v toData:(NSMutableData *)data;
- - (void)appendInt:(uint32_t)v toData:(NSMutableData *)data;
- - (void)appendLong:(uint64_t)v toData:(NSMutableData *)data;
- - (void)appendFloat:(float)v toData:(NSMutableData *)data;
- - (void)appendDouble:(double)v toData:(NSMutableData *)data;
- - (NSData *)data;
- @end
- @implementation NBTContainer
- @synthesize name, children, type;
- @synthesize stringValue, numberValue, listType;
- - (id)init
- {
- if ((self = [super init]))
- {
- }
- return self;
- }
- - (void)dealloc
- {
- [name release];
- [children release];
- [stringValue release];
- [numberValue release];
- [super dealloc];
- }
- + (NBTContainer *)containerWithName:(NSString *)theName type:(NBTType)theType numberValue:(NSNumber *)theNumber
- {
- NBTContainer *cont = [[[NBTContainer alloc] init] autorelease];
- cont.name = theName;
- cont.type = theType;
- cont.numberValue = theNumber;
- return cont;
- }
- + (id)nbtContainerWithData:(NSData *)data;
- {
- id obj = [[[self class] alloc] init];
- [obj readFromData:data];
- return [obj autorelease];
- }
- - (void)readFromData:(NSData *)data
- {
- data = [data gzipInflate];
-
- const uint8_t *bytes = (const uint8_t *)[data bytes];
-
- uint32_t offset = 0;
- [self populateWithBytes:bytes offset:&offset];
- }
- - (NSData *)writeData
- {
- return [[self data] gzipDeflate];
- }
- - (NBTContainer *)childNamed:(NSString *)theName
- {
- if (self.type != NBTTypeCompound)
- {
- NSLog(@"ERROR: Cannot find children inside a non-compound NBTContainer.");
- return nil;
- }
- for (NBTContainer *container in self.children)
- {
- if ([container.name isEqual:theName])
- return container;
- }
- return nil;
- }
- #pragma mark -
- #pragma mark Private I/O API
- - (void)populateWithBytes:(const uint8_t *)bytes offset:(uint32_t *)offsetPointer
- {
- uint32_t offset = *offsetPointer;
- self.type = [self byteFromBytes:bytes offset:&offset];
- self.name = [self stringFromBytes:bytes offset:&offset];
-
- if (self.type == NBTTypeCompound)
- {
- NBTLog(@">> start compound named %@", self.name);
- self.children = [NSMutableArray array];
-
- while (1)
- {
- NBTType childType = bytes[offset]; // peek
- if (childType == NBTTypeEnd)
- {
- offset += 1;
- break;
- }
-
- NBTContainer *child = [[NBTContainer alloc] init];
- [child populateWithBytes:bytes offset:&offset];
- [self.children addObject:child];
- [child release];
- }
- NBTLog(@"<< end compound %@", self.name);
- }
- else if (self.type == NBTTypeList)
- {
- listType = [self byteFromBytes:bytes offset:&offset];
- uint32_t listLength = [self intFromBytes:bytes offset:&offset];
-
- NBTLog(@">> start list named %@ with type=%d length=%d", self.name, listType, listLength);
-
- self.children = [NSMutableArray array];
- while (listLength > 0)
- {
- if (listType == NBTTypeFloat)
- {
- uint32_t i = [self intFromBytes:bytes offset:&offset];
- float f = *((float*)&i);
- NSNumber *num = [NSNumber numberWithFloat:f];
- [self.children addObject:num];
- }
- else if (listType == NBTTypeDouble)
- {
- uint64_t l = [self longFromBytes:bytes offset:&offset];
- double d = *((double*)&l);
- NSNumber *num = [NSNumber numberWithDouble:d];
- [self.children addObject:num];
- }
- else if (listType == NBTTypeCompound)
- {
- NSMutableArray *array = [NSMutableArray array];
- while (1)
- {
- NBTType childType = bytes[offset]; // peek
- if (childType == NBTTypeEnd)
- {
- offset += 1;
- break;
- }
-
- NBTContainer *child = [[NBTContainer alloc] init];
- [child populateWithBytes:bytes offset:&offset];
- [array addObject:child];
- [child release];
- }
- [self.children addObject:array];
- }
- else
- {
- NSLog(@"Unhandled list type: %d", listType);
- }
- listLength--;
- }
-
- NBTLog(@"<< end list %@", self.name);
- }
- else if (self.type == NBTTypeString)
- {
- self.stringValue = [self stringFromBytes:bytes offset:&offset];
- NBTLog(@" name=%@ string=%@", self.name, self.stringValue);
- }
- else if (self.type == NBTTypeLong)
- {
- self.numberValue = [NSNumber numberWithUnsignedLongLong:[self longFromBytes:bytes offset:&offset]];
- NBTLog(@" name=%@ long=%qu", self.name, [self.numberValue unsignedLongLongValue]);
- }
- else if (self.type == NBTTypeInt)
- {
- self.numberValue = [NSNumber numberWithUnsignedInt:[self intFromBytes:bytes offset:&offset]];
- NBTLog(@" name=%@ int=0x%x", self.name, [self.numberValue unsignedIntValue]);
- }
- else if (self.type == NBTTypeShort)
- {
- self.numberValue = [NSNumber numberWithUnsignedShort:[self shortFromBytes:bytes offset:&offset]];
- NBTLog(@" name=%@ short=0x%x", self.name, [self.numberValue unsignedShortValue]);
- }
- else if (self.type == NBTTypeByte)
- {
- self.numberValue = [NSNumber numberWithUnsignedChar:[self byteFromBytes:bytes offset:&offset]];
- NBTLog(@" name=%@ byte=0x%x", self.name, [self.numberValue unsignedCharValue]);
- }
- else if (self.type == NBTTypeFloat)
- {
- uint32_t i = [self intFromBytes:bytes offset:&offset];
- float f = *((float *)&i);
- self.numberValue = [NSNumber numberWithFloat:f];
- NBTLog(@" name=%@ float=%f", self.name, [self.numberValue floatValue]);
- }
- else
- {
- NSLog(@"Unhandled type: %d", self.type);
- }
-
- *offsetPointer = offset;
- }
- - (NSData *)data
- {
- NSMutableData *data = [NSMutableData data];
- [self appendByte:self.type toData:data];
- [self appendString:self.name toData:data];
-
- if (self.type == NBTTypeCompound)
- {
- for (NBTContainer *child in self.children)
- {
- [data appendData:[child data]];
- }
- uint8_t t = NBTTypeEnd;
- [data appendBytes:&t length:1];
- }
- else if (self.type == NBTTypeList)
- {
- [self appendByte:self.listType toData:data];
- [self appendInt:self.children.count toData:data];
- for (id item in self.children)
- {
- if (listType == NBTTypeCompound)
- {
- for (NBTContainer *i in item)
- {
- [data appendData:[i data]];
- }
- uint8_t t = NBTTypeEnd;
- [data appendBytes:&t length:1];
- }
- else if (listType == NBTTypeFloat)
- {
- [self appendFloat:[item floatValue] toData:data];
- }
- else if (listType == NBTTypeDouble)
- {
- [self appendDouble:[item doubleValue] toData:data];
- }
- else if (listType == NBTTypeByte)
- {
- [self appendByte:[item unsignedCharValue] toData:data];
- }
- else
- {
- NSLog(@"Unhandled list type: %d", listType);
- }
- }
- }
- else if (self.type == NBTTypeString)
- {
- [self appendString:self.stringValue toData:data];
- }
- else if (self.type == NBTTypeLong)
- {
- [self appendLong:[self.numberValue unsignedLongLongValue] toData:data];
- }
- else if (self.type == NBTTypeShort)
- {
- [self appendShort:[self.numberValue unsignedShortValue] toData:data];
- }
- else if (self.type == NBTTypeInt)
- {
- [self appendInt:[self.numberValue unsignedIntValue] toData:data];
- }
- else if (self.type == NBTTypeByte)
- {
- [self appendByte:[self.numberValue unsignedCharValue] toData:data];
- }
- else if (self.type == NBTTypeDouble)
- {
- [self appendDouble:[self.numberValue doubleValue] toData:data];
- }
- else if (self.type == NBTTypeFloat)
- {
- [self appendFloat:[self.numberValue floatValue] toData:data];
- }
- else
- {
- NSLog(@"Unhandled type: %d", self.type);
- }
-
- return data;
- }
- #pragma mark -
- #pragma mark Data Helpers
- - (NSString *)stringFromBytes:(const uint8_t *)bytes offset:(uint32_t *)offsetPointer
- {
- uint32_t offset = *offsetPointer;
- uint16_t length = (bytes[offset] << 8) | bytes[offset + 1];
- *offsetPointer += 2 + length;
- return [[[NSString alloc] initWithBytes:bytes + offset + 2 length:length encoding:NSUTF8StringEncoding] autorelease];
- }
- - (uint8_t)byteFromBytes:(const uint8_t *)bytes offset:(uint32_t *)offsetPointer
- {
- uint32_t offset = *offsetPointer;
- uint8_t n = bytes[offset];
- *offsetPointer += 1;
- return n;
- }
- - (uint16_t)shortFromBytes:(const uint8_t *)bytes offset:(uint32_t *)offsetPointer
- {
- uint32_t offset = *offsetPointer;
- uint16_t n = (bytes[offset + 0] << 8) | bytes[offset + 1];
- *offsetPointer += 2;
- return n;
- }
- - (uint32_t)intFromBytes:(const uint8_t *)bytes offset:(uint32_t *)offsetPointer
- {
- uint32_t offset = *offsetPointer;
- uint32_t n = ntohl(*((uint32_t *)(bytes + offset)));
- *offsetPointer += 4;
- return n;
- }
- - (uint64_t)longFromBytes:(const uint8_t *)bytes offset:(uint32_t *)offsetPointer
- {
- uint32_t offset = *offsetPointer;
- uint64_t n = ntohl(*((uint32_t *)(bytes + offset)));
- n <<= 32;
- offset += 4;
- n += ntohl(*((uint32_t *)(bytes + offset)));
- *offsetPointer += 8;
- return n;
- }
- - (void)appendString:(NSString *)str toData:(NSMutableData *)data
- {
- [self appendShort:str.length toData:data];
- [data appendData:[str dataUsingEncoding:NSUTF8StringEncoding]];
- }
- - (void)appendByte:(uint8_t)v toData:(NSMutableData *)data
- {
- [data appendBytes:&v length:1];
- }
- - (void)appendShort:(uint16_t)v toData:(NSMutableData *)data
- {
- v = htons(v);
- [data appendBytes:&v length:2];
- }
- - (void)appendInt:(uint32_t)v toData:(NSMutableData *)data
- {
- v = htonl(v);
- [data appendBytes:&v length:4];
- }
- - (void)appendLong:(uint64_t)v toData:(NSMutableData *)data
- {
- uint32_t v0 = htonl(v >> 32);
- uint32_t v1 = htonl(v);
- [data appendBytes:&v0 length:4];
- [data appendBytes:&v1 length:4];
- }
- - (void)appendFloat:(float)v toData:(NSMutableData *)data
- {
- uint32_t vi = *((uint32_t *)&v);
- vi = htonl(vi);
- [data appendBytes:&vi length:4];
- }
- - (void)appendDouble:(double)v toData:(NSMutableData *)data
- {
- uint64_t vl = *((uint64_t *)&v);
- uint32_t v0 = htonl(vl >> 32);
- uint32_t v1 = htonl(vl);
- [data appendBytes:&v0 length:4];
- [data appendBytes:&v1 length:4];
- }
- @end
|