PageRenderTime 40ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 0ms

/Smooth Drawing/libs/cocos2d/CCTMXXMLParser.m

https://gitlab.com/Mr.Tomato/smooth-drawing
Objective C | 523 lines | 348 code | 110 blank | 65 comment | 78 complexity | 136c9110e48aa27c4eb2530c2b18e679 MD5 | raw file
  1. /*
  2. * cocos2d for iPhone: http://www.cocos2d-iphone.org
  3. *
  4. * Copyright (c) 2009-2010 Ricardo Quesada
  5. * Copyright (c) 2011 Zynga Inc.
  6. *
  7. * Permission is hereby granted, free of charge, to any person obtaining a copy
  8. * of this software and associated documentation files (the "Software"), to deal
  9. * in the Software without restriction, including without limitation the rights
  10. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  11. * copies of the Software, and to permit persons to whom the Software is
  12. * furnished to do so, subject to the following conditions:
  13. *
  14. * The above copyright notice and this permission notice shall be included in
  15. * all copies or substantial portions of the Software.
  16. *
  17. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  18. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  19. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  20. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  21. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  22. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  23. * THE SOFTWARE.
  24. *
  25. *
  26. * TMX Tiled Map support:
  27. * http://www.mapeditor.org
  28. *
  29. */
  30. #import <Foundation/Foundation.h>
  31. #include <zlib.h>
  32. #import "ccMacros.h"
  33. #import "Support/CGPointExtension.h"
  34. #import "CCTMXXMLParser.h"
  35. #import "CCTMXTiledMap.h"
  36. #import "CCTMXObjectGroup.h"
  37. #import "Support/base64.h"
  38. #import "Support/ZipUtils.h"
  39. #import "Support/CCFileUtils.h"
  40. #pragma mark -
  41. #pragma mark TMXLayerInfo
  42. @implementation CCTMXLayerInfo
  43. @synthesize name = name_, layerSize = layerSize_, tiles = tiles_, visible = visible_, opacity = opacity_, ownTiles = ownTiles_, minGID = minGID_, maxGID = maxGID_, properties = properties_;
  44. @synthesize offset = offset_;
  45. -(id) init
  46. {
  47. if( (self=[super init])) {
  48. ownTiles_ = YES;
  49. minGID_ = 100000;
  50. maxGID_ = 0;
  51. self.name = nil;
  52. tiles_ = NULL;
  53. offset_ = CGPointZero;
  54. self.properties = [NSMutableDictionary dictionaryWithCapacity:5];
  55. }
  56. return self;
  57. }
  58. - (void) dealloc
  59. {
  60. CCLOGINFO(@"cocos2d: deallocing %@",self);
  61. [name_ release];
  62. [properties_ release];
  63. if( ownTiles_ && tiles_ ) {
  64. free( tiles_ );
  65. tiles_ = NULL;
  66. }
  67. [super dealloc];
  68. }
  69. @end
  70. #pragma mark -
  71. #pragma mark TMXTilesetInfo
  72. @implementation CCTMXTilesetInfo
  73. @synthesize name = name_, firstGid = firstGid_, tileSize = tileSize_, spacing = spacing_, margin = margin_, sourceImage = sourceImage_, imageSize = imageSize_;
  74. - (void) dealloc
  75. {
  76. CCLOGINFO(@"cocos2d: deallocing %@", self);
  77. [sourceImage_ release];
  78. [name_ release];
  79. [super dealloc];
  80. }
  81. -(CGRect) rectForGID:(unsigned int)gid
  82. {
  83. CGRect rect;
  84. rect.size = tileSize_;
  85. gid &= kCCFlippedMask;
  86. gid = gid - firstGid_;
  87. int max_x = (imageSize_.width - margin_*2 + spacing_) / (tileSize_.width + spacing_);
  88. // int max_y = (imageSize.height - margin*2 + spacing) / (tileSize.height + spacing);
  89. rect.origin.x = (gid % max_x) * (tileSize_.width + spacing_) + margin_;
  90. rect.origin.y = (gid / max_x) * (tileSize_.height + spacing_) + margin_;
  91. return rect;
  92. }
  93. @end
  94. #pragma mark -
  95. #pragma mark CCTMXMapInfo
  96. @interface CCTMXMapInfo (Private)
  97. /* initalises parsing of an XML file, either a tmx (Map) file or tsx (Tileset) file */
  98. -(void) parseXMLFile:(NSString *)xmlFilename;
  99. /* initalises parsing of an XML string, either a tmx (Map) string or tsx (Tileset) string */
  100. - (void) parseXMLString:(NSString *)xmlString;
  101. /* handles the work of parsing for parseXMLFile: and parseXMLString: */
  102. - (NSError*) parseXMLData:(NSData*)data;
  103. @end
  104. @implementation CCTMXMapInfo
  105. @synthesize orientation = orientation_, mapSize = mapSize_, layers = layers_, tilesets = tilesets_, tileSize = tileSize_, filename = filename_, resources = resources_, objectGroups = objectGroups_, properties = properties_;
  106. @synthesize tileProperties = tileProperties_;
  107. +(id) formatWithTMXFile:(NSString*)tmxFile
  108. {
  109. return [[[self alloc] initWithTMXFile:tmxFile] autorelease];
  110. }
  111. +(id) formatWithXML:(NSString*)tmxString resourcePath:(NSString*)resourcePath
  112. {
  113. return [[[self alloc] initWithXML:tmxString resourcePath:resourcePath] autorelease];
  114. }
  115. - (void) internalInit:(NSString*)tmxFileName resourcePath:(NSString*)resourcePath
  116. {
  117. self.tilesets = [NSMutableArray arrayWithCapacity:4];
  118. self.layers = [NSMutableArray arrayWithCapacity:4];
  119. self.filename = tmxFileName;
  120. self.resources = resourcePath;
  121. self.objectGroups = [NSMutableArray arrayWithCapacity:4];
  122. self.properties = [NSMutableDictionary dictionaryWithCapacity:5];
  123. self.tileProperties = [NSMutableDictionary dictionaryWithCapacity:5];
  124. // tmp vars
  125. currentString = [[NSMutableString alloc] initWithCapacity:1024];
  126. storingCharacters = NO;
  127. layerAttribs = TMXLayerAttribNone;
  128. parentElement = TMXPropertyNone;
  129. }
  130. -(id) initWithXML:(NSString *)tmxString resourcePath:(NSString*)resourcePath
  131. {
  132. if( (self=[super init])) {
  133. [self internalInit:nil resourcePath:resourcePath];
  134. [self parseXMLString:tmxString];
  135. }
  136. return self;
  137. }
  138. -(id) initWithTMXFile:(NSString*)tmxFile
  139. {
  140. if( (self=[super init])) {
  141. [self internalInit:tmxFile resourcePath:nil];
  142. [self parseXMLFile:filename_];
  143. }
  144. return self;
  145. }
  146. - (void) dealloc
  147. {
  148. CCLOGINFO(@"cocos2d: deallocing %@", self);
  149. [tilesets_ release];
  150. [layers_ release];
  151. [filename_ release];
  152. [resources_ release];
  153. [currentString release];
  154. [objectGroups_ release];
  155. [properties_ release];
  156. [tileProperties_ release];
  157. [super dealloc];
  158. }
  159. - (NSError*) parseXMLData:(NSData*)data
  160. {
  161. NSXMLParser *parser = [[[NSXMLParser alloc] initWithData:data] autorelease];
  162. // we'll do the parsing
  163. [parser setDelegate:self];
  164. [parser setShouldProcessNamespaces:NO];
  165. [parser setShouldReportNamespacePrefixes:NO];
  166. [parser setShouldResolveExternalEntities:NO];
  167. [parser parse];
  168. return [parser parserError];
  169. }
  170. - (void) parseXMLString:(NSString *)xmlString
  171. {
  172. NSData* data = [xmlString dataUsingEncoding:NSUTF8StringEncoding];
  173. NSError* err = [self parseXMLData:data];
  174. (void)err;
  175. NSAssert1( !err, @"Error parsing TMX data: %@.", [NSString stringWithCharacters:[data bytes] length:[data length]] );
  176. }
  177. - (void) parseXMLFile:(NSString *)xmlFilename
  178. {
  179. NSURL *url = [NSURL fileURLWithPath:[CCFileUtils fullPathFromRelativePath:xmlFilename] ];
  180. NSData *data = [NSData dataWithContentsOfURL:url];
  181. NSError* err = [self parseXMLData:data];
  182. (void)err;
  183. NSAssert3(!err, @"Error parsing TMX file: %@, %@ (%d).", xmlFilename, [err localizedDescription], [err code]);
  184. }
  185. // the XML parser calls here with all the elements
  186. -(void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict
  187. {
  188. if([elementName isEqualToString:@"map"]) {
  189. NSString *version = [attributeDict objectForKey:@"version"];
  190. if( ! [version isEqualToString:@"1.0"] )
  191. CCLOG(@"cocos2d: TMXFormat: Unsupported TMX version: %@", version);
  192. NSString *orientationStr = [attributeDict objectForKey:@"orientation"];
  193. if( [orientationStr isEqualToString:@"orthogonal"])
  194. orientation_ = CCTMXOrientationOrtho;
  195. else if ( [orientationStr isEqualToString:@"isometric"])
  196. orientation_ = CCTMXOrientationIso;
  197. else if( [orientationStr isEqualToString:@"hexagonal"])
  198. orientation_ = CCTMXOrientationHex;
  199. else
  200. CCLOG(@"cocos2d: TMXFomat: Unsupported orientation: %@", orientation_);
  201. mapSize_.width = [[attributeDict objectForKey:@"width"] intValue];
  202. mapSize_.height = [[attributeDict objectForKey:@"height"] intValue];
  203. tileSize_.width = [[attributeDict objectForKey:@"tilewidth"] intValue];
  204. tileSize_.height = [[attributeDict objectForKey:@"tileheight"] intValue];
  205. // The parent element is now "map"
  206. parentElement = TMXPropertyMap;
  207. } else if([elementName isEqualToString:@"tileset"]) {
  208. // If this is an external tileset then start parsing that
  209. NSString *externalTilesetFilename = [attributeDict objectForKey:@"source"];
  210. if (externalTilesetFilename) {
  211. // Tileset file will be relative to the map file. So we need to convert it to an absolute path
  212. NSString *dir = [filename_ stringByDeletingLastPathComponent]; // Directory of map file
  213. if (!dir)
  214. dir = resources_;
  215. externalTilesetFilename = [dir stringByAppendingPathComponent:externalTilesetFilename]; // Append path to tileset file
  216. [self parseXMLFile:externalTilesetFilename];
  217. } else {
  218. CCTMXTilesetInfo *tileset = [CCTMXTilesetInfo new];
  219. tileset.name = [attributeDict objectForKey:@"name"];
  220. tileset.firstGid = [[attributeDict objectForKey:@"firstgid"] intValue];
  221. tileset.spacing = [[attributeDict objectForKey:@"spacing"] intValue];
  222. tileset.margin = [[attributeDict objectForKey:@"margin"] intValue];
  223. CGSize s;
  224. s.width = [[attributeDict objectForKey:@"tilewidth"] intValue];
  225. s.height = [[attributeDict objectForKey:@"tileheight"] intValue];
  226. tileset.tileSize = s;
  227. [tilesets_ addObject:tileset];
  228. [tileset release];
  229. }
  230. }else if([elementName isEqualToString:@"tile"]){
  231. CCTMXTilesetInfo* info = [tilesets_ lastObject];
  232. NSMutableDictionary* dict = [NSMutableDictionary dictionaryWithCapacity:3];
  233. parentGID_ = [info firstGid] + [[attributeDict objectForKey:@"id"] intValue];
  234. [tileProperties_ setObject:dict forKey:[NSNumber numberWithInt:parentGID_]];
  235. parentElement = TMXPropertyTile;
  236. }else if([elementName isEqualToString:@"layer"]) {
  237. CCTMXLayerInfo *layer = [CCTMXLayerInfo new];
  238. layer.name = [attributeDict objectForKey:@"name"];
  239. CGSize s;
  240. s.width = [[attributeDict objectForKey:@"width"] intValue];
  241. s.height = [[attributeDict objectForKey:@"height"] intValue];
  242. layer.layerSize = s;
  243. layer.visible = ![[attributeDict objectForKey:@"visible"] isEqualToString:@"0"];
  244. if( [attributeDict objectForKey:@"opacity"] )
  245. layer.opacity = 255 * [[attributeDict objectForKey:@"opacity"] floatValue];
  246. else
  247. layer.opacity = 255;
  248. int x = [[attributeDict objectForKey:@"x"] intValue];
  249. int y = [[attributeDict objectForKey:@"y"] intValue];
  250. layer.offset = ccp(x,y);
  251. [layers_ addObject:layer];
  252. [layer release];
  253. // The parent element is now "layer"
  254. parentElement = TMXPropertyLayer;
  255. } else if([elementName isEqualToString:@"objectgroup"]) {
  256. CCTMXObjectGroup *objectGroup = [[CCTMXObjectGroup alloc] init];
  257. objectGroup.groupName = [attributeDict objectForKey:@"name"];
  258. CGPoint positionOffset;
  259. positionOffset.x = [[attributeDict objectForKey:@"x"] intValue] * tileSize_.width;
  260. positionOffset.y = [[attributeDict objectForKey:@"y"] intValue] * tileSize_.height;
  261. objectGroup.positionOffset = positionOffset;
  262. [objectGroups_ addObject:objectGroup];
  263. [objectGroup release];
  264. // The parent element is now "objectgroup"
  265. parentElement = TMXPropertyObjectGroup;
  266. } else if([elementName isEqualToString:@"image"]) {
  267. CCTMXTilesetInfo *tileset = [tilesets_ lastObject];
  268. // build full path
  269. NSString *imagename = [attributeDict objectForKey:@"source"];
  270. NSString *path = [filename_ stringByDeletingLastPathComponent];
  271. if (!path)
  272. path = resources_;
  273. tileset.sourceImage = [path stringByAppendingPathComponent:imagename];
  274. } else if([elementName isEqualToString:@"data"]) {
  275. NSString *encoding = [attributeDict objectForKey:@"encoding"];
  276. NSString *compression = [attributeDict objectForKey:@"compression"];
  277. if( [encoding isEqualToString:@"base64"] ) {
  278. layerAttribs |= TMXLayerAttribBase64;
  279. storingCharacters = YES;
  280. if( [compression isEqualToString:@"gzip"] )
  281. layerAttribs |= TMXLayerAttribGzip;
  282. else if( [compression isEqualToString:@"zlib"] )
  283. layerAttribs |= TMXLayerAttribZlib;
  284. NSAssert( !compression || [compression isEqualToString:@"gzip"] || [compression isEqualToString:@"zlib"], @"TMX: unsupported compression method" );
  285. }
  286. NSAssert( layerAttribs != TMXLayerAttribNone, @"TMX tile map: Only base64 and/or gzip/zlib maps are supported" );
  287. } else if([elementName isEqualToString:@"object"]) {
  288. CCTMXObjectGroup *objectGroup = [objectGroups_ lastObject];
  289. // The value for "type" was blank or not a valid class name
  290. // Create an instance of TMXObjectInfo to store the object and its properties
  291. NSMutableDictionary *dict = [[NSMutableDictionary alloc] initWithCapacity:10];
  292. // Parse everything automatically
  293. NSArray *array = [NSArray arrayWithObjects:@"name", @"type", @"width", @"height", @"gid", nil];
  294. for( NSObject *key in array ) {
  295. NSObject *obj = [attributeDict objectForKey:key];
  296. if( obj )
  297. [dict setObject:obj forKey:key];
  298. }
  299. // But X and Y since they need special treatment
  300. // X
  301. NSString *value = [attributeDict objectForKey:@"x"];
  302. if( value ) {
  303. int x = [value intValue] + objectGroup.positionOffset.x;
  304. [dict setObject:[NSNumber numberWithInt:x] forKey:@"x"];
  305. }
  306. // Y
  307. value = [attributeDict objectForKey:@"y"];
  308. if( value ) {
  309. int y = [value intValue] + objectGroup.positionOffset.y;
  310. // Correct y position. (Tiled uses Flipped, cocos2d uses Standard)
  311. y = (mapSize_.height * tileSize_.height) - y - [[attributeDict objectForKey:@"height"] intValue];
  312. [dict setObject:[NSNumber numberWithInt:y] forKey:@"y"];
  313. }
  314. // Add the object to the objectGroup
  315. [[objectGroup objects] addObject:dict];
  316. [dict release];
  317. // The parent element is now "object"
  318. parentElement = TMXPropertyObject;
  319. } else if([elementName isEqualToString:@"property"]) {
  320. if ( parentElement == TMXPropertyNone ) {
  321. CCLOG( @"TMX tile map: Parent element is unsupported. Cannot add property named '%@' with value '%@'",
  322. [attributeDict objectForKey:@"name"], [attributeDict objectForKey:@"value"] );
  323. } else if ( parentElement == TMXPropertyMap ) {
  324. // The parent element is the map
  325. [properties_ setObject:[attributeDict objectForKey:@"value"] forKey:[attributeDict objectForKey:@"name"]];
  326. } else if ( parentElement == TMXPropertyLayer ) {
  327. // The parent element is the last layer
  328. CCTMXLayerInfo *layer = [layers_ lastObject];
  329. // Add the property to the layer
  330. [[layer properties] setObject:[attributeDict objectForKey:@"value"] forKey:[attributeDict objectForKey:@"name"]];
  331. } else if ( parentElement == TMXPropertyObjectGroup ) {
  332. // The parent element is the last object group
  333. CCTMXObjectGroup *objectGroup = [objectGroups_ lastObject];
  334. [[objectGroup properties] setObject:[attributeDict objectForKey:@"value"] forKey:[attributeDict objectForKey:@"name"]];
  335. } else if ( parentElement == TMXPropertyObject ) {
  336. // The parent element is the last object
  337. CCTMXObjectGroup *objectGroup = [objectGroups_ lastObject];
  338. NSMutableDictionary *dict = [[objectGroup objects] lastObject];
  339. NSString *propertyName = [attributeDict objectForKey:@"name"];
  340. NSString *propertyValue = [attributeDict objectForKey:@"value"];
  341. [dict setObject:propertyValue forKey:propertyName];
  342. } else if ( parentElement == TMXPropertyTile ) {
  343. NSMutableDictionary* dict = [tileProperties_ objectForKey:[NSNumber numberWithInt:parentGID_]];
  344. NSString *propertyName = [attributeDict objectForKey:@"name"];
  345. NSString *propertyValue = [attributeDict objectForKey:@"value"];
  346. [dict setObject:propertyValue forKey:propertyName];
  347. }
  348. } else if ([elementName isEqualToString:@"polygon"]) {
  349. // find parent object's dict and add polygon-points to it
  350. CCTMXObjectGroup *objectGroup = [objectGroups_ lastObject];
  351. NSMutableDictionary *dict = [[objectGroup objects] lastObject];
  352. [dict setObject:[attributeDict objectForKey:@"points"] forKey:@"polygonPoints"];
  353. } else if ([elementName isEqualToString:@"polyline"]) {
  354. // find parent object's dict and add polyline-points to it
  355. CCTMXObjectGroup *objectGroup = [objectGroups_ lastObject];
  356. NSMutableDictionary *dict = [[objectGroup objects] lastObject];
  357. [dict setObject:[attributeDict objectForKey:@"points"] forKey:@"polylinePoints"];
  358. }
  359. }
  360. - (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
  361. {
  362. int len = 0;
  363. if([elementName isEqualToString:@"data"] && layerAttribs&TMXLayerAttribBase64) {
  364. storingCharacters = NO;
  365. CCTMXLayerInfo *layer = [layers_ lastObject];
  366. unsigned char *buffer;
  367. len = base64Decode((unsigned char*)[currentString UTF8String], (unsigned int) [currentString length], &buffer);
  368. if( ! buffer ) {
  369. CCLOG(@"cocos2d: TiledMap: decode data error");
  370. return;
  371. }
  372. if( layerAttribs & (TMXLayerAttribGzip | TMXLayerAttribZlib) ) {
  373. unsigned char *deflated;
  374. CGSize s = [layer layerSize];
  375. int sizeHint = s.width * s.height * sizeof(uint32_t);
  376. int inflatedLen = ccInflateMemoryWithHint(buffer, len, &deflated, sizeHint);
  377. NSAssert( inflatedLen == sizeHint, @"CCTMXXMLParser: Hint failed!");
  378. inflatedLen = (int)&inflatedLen; // XXX: to avoid warings in compiler
  379. free( buffer );
  380. if( ! deflated ) {
  381. CCLOG(@"cocos2d: TiledMap: inflate data error");
  382. return;
  383. }
  384. layer.tiles = (unsigned int*) deflated;
  385. } else
  386. layer.tiles = (unsigned int*) buffer;
  387. [currentString setString:@""];
  388. } else if ([elementName isEqualToString:@"map"]) {
  389. // The map element has ended
  390. parentElement = TMXPropertyNone;
  391. } else if ([elementName isEqualToString:@"layer"]) {
  392. // The layer element has ended
  393. parentElement = TMXPropertyNone;
  394. } else if ([elementName isEqualToString:@"objectgroup"]) {
  395. // The objectgroup element has ended
  396. parentElement = TMXPropertyNone;
  397. } else if ([elementName isEqualToString:@"object"]) {
  398. // The object element has ended
  399. parentElement = TMXPropertyNone;
  400. }
  401. }
  402. - (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
  403. {
  404. if (storingCharacters)
  405. [currentString appendString:string];
  406. }
  407. //
  408. // the level did not load, file not found, etc.
  409. //
  410. -(void)parser:(NSXMLParser *)parser parseErrorOccurred:(NSError *)parseError{
  411. CCLOG(@"cocos2d: Error on XML Parse: %@", [parseError localizedDescription] );
  412. }
  413. @end