IJMinecraftLevel.m 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  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. // SMP Player have not the same structure, there is no "DATA" compound, nor "player"
  29. NBTContainer *playerCompound;
  30. NBTContainer *dataCompound = [self childNamed:@"Data"];
  31. if (dataCompound != nil)
  32. {
  33. playerCompound = [dataCompound childNamed:@"Player"];
  34. }
  35. else
  36. {
  37. NSLog(@"Player file is from a SMP file, not level.dat one");
  38. playerCompound = self;
  39. }
  40. NBTContainer *inventoryList = [playerCompound childNamed:@"Inventory"];
  41. // TODO: Check for error conditions here.
  42. return inventoryList;
  43. }
  44. - (NSArray *)inventory
  45. {
  46. NSMutableArray *output = [NSMutableArray array];
  47. for (NSArray *listItems in [self inventoryList].children)
  48. {
  49. IJInventoryItem *invItem = [[IJInventoryItem alloc] init];
  50. invItem.itemId = [[self containerWithName:@"id" inArray:listItems].numberValue shortValue];
  51. invItem.count = [[self containerWithName:@"Count" inArray:listItems].numberValue unsignedCharValue];
  52. invItem.damage = [[self containerWithName:@"Damage" inArray:listItems].numberValue shortValue];
  53. invItem.slot = [[self containerWithName:@"Slot" inArray:listItems].numberValue unsignedCharValue];
  54. [output addObject:invItem];
  55. [invItem release];
  56. }
  57. return output;
  58. }
  59. - (void)setInventory:(NSArray *)newInventory
  60. {
  61. NSMutableArray *newChildren = [NSMutableArray array];
  62. NBTContainer *inventoryList = [self inventoryList];
  63. if (inventoryList.listType != NBTTypeCompound)
  64. {
  65. // There appears to be a bug in the way Minecraft writes empty inventory lists; it appears to
  66. // set the list type to 'byte', so we will correct it here.
  67. NSLog(@"%s Fixing inventory list type; was %d.", __PRETTY_FUNCTION__, inventoryList.listType);
  68. inventoryList.listType = NBTTypeCompound;
  69. }
  70. for (IJInventoryItem *invItem in newInventory)
  71. {
  72. NSArray *listItems = [NSArray arrayWithObjects:
  73. [NBTContainer containerWithName:@"id" type:NBTTypeShort numberValue:[NSNumber numberWithShort:invItem.itemId]],
  74. [NBTContainer containerWithName:@"Damage" type:NBTTypeShort numberValue:[NSNumber numberWithShort:invItem.damage]],
  75. [NBTContainer containerWithName:@"Count" type:NBTTypeByte numberValue:[NSNumber numberWithShort:invItem.count]],
  76. [NBTContainer containerWithName:@"Slot" type:NBTTypeByte numberValue:[NSNumber numberWithShort:invItem.slot]],
  77. nil];
  78. [newChildren addObject:listItems];
  79. }
  80. inventoryList.children = newChildren;
  81. }
  82. - (NBTContainer *)worldTimeContainer
  83. {
  84. return [[self childNamed:@"Data"] childNamed:@"Time"];
  85. }
  86. #pragma mark -
  87. #pragma mark Helpers
  88. + (NSString *)pathForWorldAtIndex:(int)worldIndex
  89. {
  90. NSArray *paths = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES);
  91. NSString *path = [paths objectAtIndex:0];
  92. path = [path stringByAppendingPathComponent:@"minecraft"];
  93. path = [path stringByAppendingPathComponent:@"saves"];
  94. path = [path stringByAppendingPathComponent:[NSString stringWithFormat:@"World%d", worldIndex]];
  95. return path;
  96. }
  97. + (NSData *)dataWithInt64:(int64_t)v
  98. {
  99. NSMutableData *data = [NSMutableData data];
  100. uint32_t v0 = htonl(v >> 32);
  101. uint32_t v1 = htonl(v);
  102. [data appendBytes:&v0 length:4];
  103. [data appendBytes:&v1 length:4];
  104. return data;
  105. }
  106. + (int64_t)int64FromData:(NSData *)data
  107. {
  108. uint8_t *bytes = (uint8_t *)[data bytes];
  109. uint64_t n = ntohl(*((uint32_t *)(bytes + 0)));
  110. n <<= 32;
  111. n += ntohl(*((uint32_t *)(bytes + 4)));
  112. return n;
  113. }
  114. /******************************************************************************/
  115. + (NSString *)pathForLevelDatAtFolder:(NSString *)worldPath
  116. {
  117. return [worldPath stringByAppendingPathComponent:@"level.dat"];
  118. }
  119. + (NSString *)pathForSessionLockAtFolder:(NSString *)worldPath
  120. {
  121. return [worldPath stringByAppendingPathComponent:@"session.lock"];
  122. }
  123. + (NSString *)pathForPlayer:(NSString *)loadedPlayer withWorld:(NSString *)worldPath;
  124. {
  125. /* loadedPlayer == nil, we use level.dat, or else we use the name */
  126. if (loadedPlayer == nil)
  127. return [self pathForLevelDatAtFolder:worldPath];
  128. return [[[worldPath stringByAppendingPathComponent:@"players"] stringByAppendingPathComponent:loadedPlayer] stringByAppendingPathExtension:@".dat"];
  129. }
  130. + (BOOL)worldExistsAtFolder:(NSString *)worldPath
  131. {
  132. return [[NSFileManager defaultManager] fileExistsAtPath:[[self class] pathForLevelDatAtFolder:worldPath]];
  133. }
  134. + (int64_t)writeToSessionLockAtFolder:(NSString *)worldPath
  135. {
  136. NSString *path = [IJMinecraftLevel pathForSessionLockAtFolder:worldPath];
  137. NSDate *now = [NSDate date];
  138. NSTimeInterval interval = [now timeIntervalSince1970];
  139. int64_t milliseconds = (int64_t)(interval * 1000.0);
  140. // write as number of milliseconds
  141. NSData *data = [IJMinecraftLevel dataWithInt64:milliseconds];
  142. [data writeToFile:path atomically:YES];
  143. return milliseconds;
  144. }
  145. + (BOOL)checkSessionLockAtFolder:(NSString *)worldPath value:(int64_t)checkValue
  146. {
  147. NSString *path = [IJMinecraftLevel pathForSessionLockAtFolder:worldPath];
  148. NSData *data = [NSData dataWithContentsOfFile:path];
  149. if (!data)
  150. {
  151. NSLog(@"Failed to read session lock at %@", path);
  152. return NO;
  153. }
  154. int64_t milliseconds = [IJMinecraftLevel int64FromData:data];
  155. return checkValue == milliseconds;
  156. }
  157. @end