IJMinecraftLevel.m 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  1. //
  2. // IJMinecraftLevel.m
  3. // InsideJob
  4. //
  5. // Created by Adam Preble on 10/7/10.
  6. // Copyright 2010 Adam Preble. All rights reserved.
  7. // Changes for opening folder Copyright 2011 Manoel Trapier
  8. //
  9. #import "IJMinecraftLevel.h"
  10. #import "IJInventoryItem.h"
  11. @implementation IJMinecraftLevel
  12. - (NBTContainer *)containerWithName:(NSString *)theName inArray:(NSArray *)array
  13. {
  14. for (NBTContainer *container in array)
  15. {
  16. if ([container.name isEqual:theName])
  17. return container;
  18. }
  19. return nil;
  20. }
  21. - (NBTContainer *)inventoryList
  22. {
  23. // Inventory is found in:
  24. // - compound "Data"
  25. // - compound "Player"
  26. // - list "Inventory"
  27. // *
  28. NBTContainer *dataCompound = [self childNamed:@"Data"];
  29. NBTContainer *playerCompound = [dataCompound childNamed:@"Player"];
  30. NBTContainer *inventoryList = [playerCompound childNamed:@"Inventory"];
  31. // TODO: Check for error conditions here.
  32. return inventoryList;
  33. }
  34. - (NSArray *)inventory
  35. {
  36. NSMutableArray *output = [NSMutableArray array];
  37. for (NSArray *listItems in [self inventoryList].children)
  38. {
  39. IJInventoryItem *invItem = [[IJInventoryItem alloc] init];
  40. invItem.itemId = [[self containerWithName:@"id" inArray:listItems].numberValue shortValue];
  41. invItem.count = [[self containerWithName:@"Count" inArray:listItems].numberValue unsignedCharValue];
  42. invItem.damage = [[self containerWithName:@"Damage" inArray:listItems].numberValue shortValue];
  43. invItem.slot = [[self containerWithName:@"Slot" inArray:listItems].numberValue unsignedCharValue];
  44. [output addObject:invItem];
  45. [invItem release];
  46. }
  47. return output;
  48. }
  49. - (void)setInventory:(NSArray *)newInventory
  50. {
  51. NSMutableArray *newChildren = [NSMutableArray array];
  52. NBTContainer *inventoryList = [self inventoryList];
  53. if (inventoryList.listType != NBTTypeCompound)
  54. {
  55. // There appears to be a bug in the way Minecraft writes empty inventory lists; it appears to
  56. // set the list type to 'byte', so we will correct it here.
  57. NSLog(@"%s Fixing inventory list type; was %d.", __PRETTY_FUNCTION__, inventoryList.listType);
  58. inventoryList.listType = NBTTypeCompound;
  59. }
  60. for (IJInventoryItem *invItem in newInventory)
  61. {
  62. NSArray *listItems = [NSArray arrayWithObjects:
  63. [NBTContainer containerWithName:@"id" type:NBTTypeShort numberValue:[NSNumber numberWithShort:invItem.itemId]],
  64. [NBTContainer containerWithName:@"Damage" type:NBTTypeShort numberValue:[NSNumber numberWithShort:invItem.damage]],
  65. [NBTContainer containerWithName:@"Count" type:NBTTypeByte numberValue:[NSNumber numberWithShort:invItem.count]],
  66. [NBTContainer containerWithName:@"Slot" type:NBTTypeByte numberValue:[NSNumber numberWithShort:invItem.slot]],
  67. nil];
  68. [newChildren addObject:listItems];
  69. }
  70. inventoryList.children = newChildren;
  71. }
  72. - (NBTContainer *)worldTimeContainer
  73. {
  74. return [[self childNamed:@"Data"] childNamed:@"Time"];
  75. }
  76. #pragma mark -
  77. #pragma mark Helpers
  78. + (NSString *)pathForWorldAtIndex:(int)worldIndex
  79. {
  80. NSArray *paths = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES);
  81. NSString *path = [paths objectAtIndex:0];
  82. path = [path stringByAppendingPathComponent:@"minecraft"];
  83. path = [path stringByAppendingPathComponent:@"saves"];
  84. path = [path stringByAppendingPathComponent:[NSString stringWithFormat:@"World%d", worldIndex]];
  85. return path;
  86. }
  87. + (NSData *)dataWithInt64:(int64_t)v
  88. {
  89. NSMutableData *data = [NSMutableData data];
  90. uint32_t v0 = htonl(v >> 32);
  91. uint32_t v1 = htonl(v);
  92. [data appendBytes:&v0 length:4];
  93. [data appendBytes:&v1 length:4];
  94. return data;
  95. }
  96. + (int64_t)int64FromData:(NSData *)data
  97. {
  98. uint8_t *bytes = (uint8_t *)[data bytes];
  99. uint64_t n = ntohl(*((uint32_t *)(bytes + 0)));
  100. n <<= 32;
  101. n += ntohl(*((uint32_t *)(bytes + 4)));
  102. return n;
  103. }
  104. /******************************************************************************/
  105. + (NSString *)pathForLevelDatAtFolder:(NSString *)worldPath
  106. {
  107. return [worldPath stringByAppendingPathComponent:@"level.dat"];
  108. }
  109. + (NSString *)pathForSessionLockAtFolder:(NSString *)worldPath
  110. {
  111. return [worldPath stringByAppendingPathComponent:@"session.lock"];
  112. }
  113. + (BOOL)worldExistsAtFolder:(NSString *)worldPath
  114. {
  115. return [[NSFileManager defaultManager] fileExistsAtPath:[[self class] pathForLevelDatAtFolder:worldPath]];
  116. }
  117. + (int64_t)writeToSessionLockAtFolder:(NSString *)worldPath
  118. {
  119. NSString *path = [IJMinecraftLevel pathForSessionLockAtFolder:worldPath];
  120. NSDate *now = [NSDate date];
  121. NSTimeInterval interval = [now timeIntervalSince1970];
  122. int64_t milliseconds = (int64_t)(interval * 1000.0);
  123. // write as number of milliseconds
  124. NSData *data = [IJMinecraftLevel dataWithInt64:milliseconds];
  125. [data writeToFile:path atomically:YES];
  126. return milliseconds;
  127. }
  128. + (BOOL)checkSessionLockAtFolder:(NSString *)worldPath value:(int64_t)checkValue
  129. {
  130. NSString *path = [IJMinecraftLevel pathForSessionLockAtFolder:worldPath];
  131. NSData *data = [NSData dataWithContentsOfFile:path];
  132. if (!data)
  133. {
  134. NSLog(@"Failed to read session lock at %@", path);
  135. return NO;
  136. }
  137. int64_t milliseconds = [IJMinecraftLevel int64FromData:data];
  138. return checkValue == milliseconds;
  139. }
  140. @end