summary refs log tree commit diff stats
path: root/libs/cocos2d/CCTMXXMLParser.m
diff options
context:
space:
mode:
Diffstat (limited to 'libs/cocos2d/CCTMXXMLParser.m')
-rwxr-xr-xlibs/cocos2d/CCTMXXMLParser.m456
1 files changed, 456 insertions, 0 deletions
diff --git a/libs/cocos2d/CCTMXXMLParser.m b/libs/cocos2d/CCTMXXMLParser.m new file mode 100755 index 0000000..77cea0e --- /dev/null +++ b/libs/cocos2d/CCTMXXMLParser.m
@@ -0,0 +1,456 @@
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
31
32#import <Foundation/Foundation.h>
33#include <zlib.h>
34
35#import "ccMacros.h"
36#import "Support/CGPointExtension.h"
37#import "CCTMXXMLParser.h"
38#import "CCTMXTiledMap.h"
39#import "CCTMXObjectGroup.h"
40#import "Support/base64.h"
41#import "Support/ZipUtils.h"
42#import "Support/CCFileUtils.h"
43
44#pragma mark -
45#pragma mark TMXLayerInfo
46
47
48@implementation CCTMXLayerInfo
49
50@synthesize name = name_, layerSize = layerSize_, tiles = tiles_, visible = visible_, opacity = opacity_, ownTiles = ownTiles_, minGID = minGID_, maxGID = maxGID_, properties = properties_;
51@synthesize offset = offset_;
52-(id) init
53{
54 if( (self=[super init])) {
55 ownTiles_ = YES;
56 minGID_ = 100000;
57 maxGID_ = 0;
58 self.name = nil;
59 tiles_ = NULL;
60 offset_ = CGPointZero;
61 self.properties = [NSMutableDictionary dictionaryWithCapacity:5];
62 }
63 return self;
64}
65- (void) dealloc
66{
67 CCLOGINFO(@"cocos2d: deallocing %@",self);
68
69 [name_ release];
70 [properties_ release];
71
72 if( ownTiles_ && tiles_ ) {
73 free( tiles_ );
74 tiles_ = NULL;
75 }
76 [super dealloc];
77}
78
79@end
80
81#pragma mark -
82#pragma mark TMXTilesetInfo
83@implementation CCTMXTilesetInfo
84
85@synthesize name = name_, firstGid = firstGid_, tileSize = tileSize_, spacing = spacing_, margin = margin_, sourceImage = sourceImage_, imageSize = imageSize_;
86
87- (void) dealloc
88{
89 CCLOGINFO(@"cocos2d: deallocing %@", self);
90 [sourceImage_ release];
91 [name_ release];
92 [super dealloc];
93}
94
95-(CGRect) rectForGID:(unsigned int)gid
96{
97 CGRect rect;
98 rect.size = tileSize_;
99
100 gid = gid - firstGid_;
101
102 int max_x = (imageSize_.width - margin_*2 + spacing_) / (tileSize_.width + spacing_);
103 // int max_y = (imageSize.height - margin*2 + spacing) / (tileSize.height + spacing);
104
105 rect.origin.x = (gid % max_x) * (tileSize_.width + spacing_) + margin_;
106 rect.origin.y = (gid / max_x) * (tileSize_.height + spacing_) + margin_;
107
108 return rect;
109}
110@end
111
112#pragma mark -
113#pragma mark CCTMXMapInfo
114
115@interface CCTMXMapInfo (Private)
116/* initalises parsing of an XML file, either a tmx (Map) file or tsx (Tileset) file */
117-(void) parseXMLFile:(NSString *)xmlFilename;
118@end
119
120
121@implementation CCTMXMapInfo
122
123@synthesize orientation = orientation_, mapSize = mapSize_, layers = layers_, tilesets = tilesets_, tileSize = tileSize_, filename = filename_, objectGroups = objectGroups_, properties = properties_;
124@synthesize tileProperties = tileProperties_;
125
126+(id) formatWithTMXFile:(NSString*)tmxFile
127{
128 return [[[self alloc] initWithTMXFile:tmxFile] autorelease];
129}
130
131-(id) initWithTMXFile:(NSString*)tmxFile
132{
133 if( (self=[super init])) {
134
135 self.tilesets = [NSMutableArray arrayWithCapacity:4];
136 self.layers = [NSMutableArray arrayWithCapacity:4];
137 self.filename = tmxFile;
138 self.objectGroups = [NSMutableArray arrayWithCapacity:4];
139 self.properties = [NSMutableDictionary dictionaryWithCapacity:5];
140 self.tileProperties = [NSMutableDictionary dictionaryWithCapacity:5];
141
142 // tmp vars
143 currentString = [[NSMutableString alloc] initWithCapacity:1024];
144 storingCharacters = NO;
145 layerAttribs = TMXLayerAttribNone;
146 parentElement = TMXPropertyNone;
147
148 [self parseXMLFile:filename_];
149 }
150 return self;
151}
152- (void) dealloc
153{
154 CCLOGINFO(@"cocos2d: deallocing %@", self);
155 [tilesets_ release];
156 [layers_ release];
157 [filename_ release];
158 [currentString release];
159 [objectGroups_ release];
160 [properties_ release];
161 [tileProperties_ release];
162 [super dealloc];
163}
164
165- (void) parseXMLFile:(NSString *)xmlFilename
166{
167 NSURL *url = [NSURL fileURLWithPath:[CCFileUtils fullPathFromRelativePath:xmlFilename] ];
168 NSData *data = [NSData dataWithContentsOfURL:url];
169 NSXMLParser *parser = [[NSXMLParser alloc] initWithData:data];
170
171 // we'll do the parsing
172 [parser setDelegate:self];
173 [parser setShouldProcessNamespaces:NO];
174 [parser setShouldReportNamespacePrefixes:NO];
175 [parser setShouldResolveExternalEntities:NO];
176 [parser parse];
177
178 NSAssert1( ! [parser parserError], @"Error parsing file: %@.", xmlFilename );
179
180 [parser release];
181}
182
183// the XML parser calls here with all the elements
184-(void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict
185{
186 if([elementName isEqualToString:@"map"]) {
187 NSString *version = [attributeDict valueForKey:@"version"];
188 if( ! [version isEqualToString:@"1.0"] )
189 CCLOG(@"cocos2d: TMXFormat: Unsupported TMX version: %@", version);
190 NSString *orientationStr = [attributeDict valueForKey:@"orientation"];
191 if( [orientationStr isEqualToString:@"orthogonal"])
192 orientation_ = CCTMXOrientationOrtho;
193 else if ( [orientationStr isEqualToString:@"isometric"])
194 orientation_ = CCTMXOrientationIso;
195 else if( [orientationStr isEqualToString:@"hexagonal"])
196 orientation_ = CCTMXOrientationHex;
197 else
198 CCLOG(@"cocos2d: TMXFomat: Unsupported orientation: %@", orientation_);
199
200 mapSize_.width = [[attributeDict valueForKey:@"width"] intValue];
201 mapSize_.height = [[attributeDict valueForKey:@"height"] intValue];
202 tileSize_.width = [[attributeDict valueForKey:@"tilewidth"] intValue];
203 tileSize_.height = [[attributeDict valueForKey:@"tileheight"] intValue];
204
205 // The parent element is now "map"
206 parentElement = TMXPropertyMap;
207 } else if([elementName isEqualToString:@"tileset"]) {
208
209 // If this is an external tileset then start parsing that
210 NSString *externalTilesetFilename = [attributeDict valueForKey:@"source"];
211 if (externalTilesetFilename) {
212 // Tileset file will be relative to the map file. So we need to convert it to an absolute path
213 NSString *dir = [filename_ stringByDeletingLastPathComponent]; // Directory of map file
214 externalTilesetFilename = [dir stringByAppendingPathComponent:externalTilesetFilename]; // Append path to tileset file
215
216 [self parseXMLFile:externalTilesetFilename];
217 } else {
218
219 CCTMXTilesetInfo *tileset = [CCTMXTilesetInfo new];
220 tileset.name = [attributeDict valueForKey:@"name"];
221 tileset.firstGid = [[attributeDict valueForKey:@"firstgid"] intValue];
222 tileset.spacing = [[attributeDict valueForKey:@"spacing"] intValue];
223 tileset.margin = [[attributeDict valueForKey:@"margin"] intValue];
224 CGSize s;
225 s.width = [[attributeDict valueForKey:@"tilewidth"] intValue];
226 s.height = [[attributeDict valueForKey:@"tileheight"] intValue];
227 tileset.tileSize = s;
228
229 [tilesets_ addObject:tileset];
230 [tileset release];
231 }
232
233 }else if([elementName isEqualToString:@"tile"]){
234 CCTMXTilesetInfo* info = [tilesets_ lastObject];
235 NSMutableDictionary* dict = [NSMutableDictionary dictionaryWithCapacity:3];
236 parentGID_ = [info firstGid] + [[attributeDict valueForKey:@"id"] intValue];
237 [tileProperties_ setObject:dict forKey:[NSNumber numberWithInt:parentGID_]];
238
239 parentElement = TMXPropertyTile;
240
241 }else if([elementName isEqualToString:@"layer"]) {
242 CCTMXLayerInfo *layer = [CCTMXLayerInfo new];
243 layer.name = [attributeDict valueForKey:@"name"];
244
245 CGSize s;
246 s.width = [[attributeDict valueForKey:@"width"] intValue];
247 s.height = [[attributeDict valueForKey:@"height"] intValue];
248 layer.layerSize = s;
249
250 layer.visible = ![[attributeDict valueForKey:@"visible"] isEqualToString:@"0"];
251
252 if( [attributeDict valueForKey:@"opacity"] )
253 layer.opacity = 255 * [[attributeDict valueForKey:@"opacity"] floatValue];
254 else
255 layer.opacity = 255;
256
257 int x = [[attributeDict valueForKey:@"x"] intValue];
258 int y = [[attributeDict valueForKey:@"y"] intValue];
259 layer.offset = ccp(x,y);
260
261 [layers_ addObject:layer];
262 [layer release];
263
264 // The parent element is now "layer"
265 parentElement = TMXPropertyLayer;
266
267 } else if([elementName isEqualToString:@"objectgroup"]) {
268
269 CCTMXObjectGroup *objectGroup = [[CCTMXObjectGroup alloc] init];
270 objectGroup.groupName = [attributeDict valueForKey:@"name"];
271 CGPoint positionOffset;
272 positionOffset.x = [[attributeDict valueForKey:@"x"] intValue] * tileSize_.width;
273 positionOffset.y = [[attributeDict valueForKey:@"y"] intValue] * tileSize_.height;
274 objectGroup.positionOffset = positionOffset;
275
276 [objectGroups_ addObject:objectGroup];
277 [objectGroup release];
278
279 // The parent element is now "objectgroup"
280 parentElement = TMXPropertyObjectGroup;
281
282 } else if([elementName isEqualToString:@"image"]) {
283
284 CCTMXTilesetInfo *tileset = [tilesets_ lastObject];
285
286 // build full path
287 NSString *imagename = [attributeDict valueForKey:@"source"];
288 NSString *path = [filename_ stringByDeletingLastPathComponent];
289 tileset.sourceImage = [path stringByAppendingPathComponent:imagename];
290
291 } else if([elementName isEqualToString:@"data"]) {
292 NSString *encoding = [attributeDict valueForKey:@"encoding"];
293 NSString *compression = [attributeDict valueForKey:@"compression"];
294
295 if( [encoding isEqualToString:@"base64"] ) {
296 layerAttribs |= TMXLayerAttribBase64;
297 storingCharacters = YES;
298
299 if( [compression isEqualToString:@"gzip"] )
300 layerAttribs |= TMXLayerAttribGzip;
301
302 else if( [compression isEqualToString:@"zlib"] )
303 layerAttribs |= TMXLayerAttribZlib;
304
305 NSAssert( !compression || [compression isEqualToString:@"gzip"] || [compression isEqualToString:@"zlib"], @"TMX: unsupported compression method" );
306 }
307
308 NSAssert( layerAttribs != TMXLayerAttribNone, @"TMX tile map: Only base64 and/or gzip/zlib maps are supported" );
309
310 } else if([elementName isEqualToString:@"object"]) {
311
312 CCTMXObjectGroup *objectGroup = [objectGroups_ lastObject];
313
314 // The value for "type" was blank or not a valid class name
315 // Create an instance of TMXObjectInfo to store the object and its properties
316 NSMutableDictionary *dict = [[NSMutableDictionary alloc] initWithCapacity:5];
317
318 // Set the name of the object to the value for "name"
319 [dict setValue:[attributeDict valueForKey:@"name"] forKey:@"name"];
320
321 // Assign all the attributes as key/name pairs in the properties dictionary
322 [dict setValue:[attributeDict valueForKey:@"type"] forKey:@"type"];
323 int x = [[attributeDict valueForKey:@"x"] intValue] + objectGroup.positionOffset.x;
324 [dict setValue:[NSNumber numberWithInt:x] forKey:@"x"];
325 int y = [[attributeDict valueForKey:@"y"] intValue] + objectGroup.positionOffset.y;
326 // Correct y position. (Tiled uses Flipped, cocos2d uses Standard)
327 y = (mapSize_.height * tileSize_.height) - y - [[attributeDict valueForKey:@"height"] intValue];
328 [dict setValue:[NSNumber numberWithInt:y] forKey:@"y"];
329 [dict setValue:[attributeDict valueForKey:@"width"] forKey:@"width"];
330 [dict setValue:[attributeDict valueForKey:@"height"] forKey:@"height"];
331
332 // Add the object to the objectGroup
333 [[objectGroup objects] addObject:dict];
334 [dict release];
335
336 // The parent element is now "object"
337 parentElement = TMXPropertyObject;
338
339 } else if([elementName isEqualToString:@"property"]) {
340
341 if ( parentElement == TMXPropertyNone ) {
342
343 CCLOG( @"TMX tile map: Parent element is unsupported. Cannot add property named '%@' with value '%@'",
344 [attributeDict valueForKey:@"name"], [attributeDict valueForKey:@"value"] );
345
346 } else if ( parentElement == TMXPropertyMap ) {
347
348 // The parent element is the map
349 [properties_ setValue:[attributeDict valueForKey:@"value"] forKey:[attributeDict valueForKey:@"name"]];
350
351 } else if ( parentElement == TMXPropertyLayer ) {
352
353 // The parent element is the last layer
354 CCTMXLayerInfo *layer = [layers_ lastObject];
355 // Add the property to the layer
356 [[layer properties] setValue:[attributeDict valueForKey:@"value"] forKey:[attributeDict valueForKey:@"name"]];
357
358 } else if ( parentElement == TMXPropertyObjectGroup ) {
359
360 // The parent element is the last object group
361 CCTMXObjectGroup *objectGroup = [objectGroups_ lastObject];
362 [[objectGroup properties] setValue:[attributeDict valueForKey:@"value"] forKey:[attributeDict valueForKey:@"name"]];
363
364 } else if ( parentElement == TMXPropertyObject ) {
365
366 // The parent element is the last object
367 CCTMXObjectGroup *objectGroup = [objectGroups_ lastObject];
368 NSMutableDictionary *dict = [[objectGroup objects] lastObject];
369
370 NSString *propertyName = [attributeDict valueForKey:@"name"];
371 NSString *propertyValue = [attributeDict valueForKey:@"value"];
372
373 [dict setValue:propertyValue forKey:propertyName];
374 } else if ( parentElement == TMXPropertyTile ) {
375
376 NSMutableDictionary* dict = [tileProperties_ objectForKey:[NSNumber numberWithInt:parentGID_]];
377 NSString *propertyName = [attributeDict valueForKey:@"name"];
378 NSString *propertyValue = [attributeDict valueForKey:@"value"];
379 [dict setObject:propertyValue forKey:propertyName];
380
381 }
382 }
383}
384
385- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
386{
387 int len = 0;
388
389 if([elementName isEqualToString:@"data"] && layerAttribs&TMXLayerAttribBase64) {
390 storingCharacters = NO;
391
392 CCTMXLayerInfo *layer = [layers_ lastObject];
393
394 unsigned char *buffer;
395 len = base64Decode((unsigned char*)[currentString UTF8String], (unsigned int) [currentString length], &buffer);
396 if( ! buffer ) {
397 CCLOG(@"cocos2d: TiledMap: decode data error");
398 return;
399 }
400
401 if( layerAttribs & (TMXLayerAttribGzip | TMXLayerAttribZlib) ) {
402 unsigned char *deflated;
403 CGSize s = [layer layerSize];
404 int sizeHint = s.width * s.height * sizeof(uint32_t);
405
406 int inflatedLen = ccInflateMemoryWithHint(buffer, len, &deflated, sizeHint);
407 NSAssert( inflatedLen == sizeHint, @"CCTMXXMLParser: Hint failed!");
408
409 inflatedLen = (int)&inflatedLen; // XXX: to avoid warings in compiler
410
411 free( buffer );
412
413 if( ! deflated ) {
414 CCLOG(@"cocos2d: TiledMap: inflate data error");
415 return;
416 }
417
418 layer.tiles = (unsigned int*) deflated;
419 } else
420 layer.tiles = (unsigned int*) buffer;
421
422 [currentString setString:@""];
423
424 } else if ([elementName isEqualToString:@"map"]) {
425 // The map element has ended
426 parentElement = TMXPropertyNone;
427
428 } else if ([elementName isEqualToString:@"layer"]) {
429 // The layer element has ended
430 parentElement = TMXPropertyNone;
431
432 } else if ([elementName isEqualToString:@"objectgroup"]) {
433 // The objectgroup element has ended
434 parentElement = TMXPropertyNone;
435
436 } else if ([elementName isEqualToString:@"object"]) {
437 // The object element has ended
438 parentElement = TMXPropertyNone;
439 }
440}
441
442- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
443{
444 if (storingCharacters)
445 [currentString appendString:string];
446}
447
448
449//
450// the level did not load, file not found, etc.
451//
452-(void)parser:(NSXMLParser *)parser parseErrorOccurred:(NSError *)parseError{
453 CCLOG(@"cocos2d: Error on XML Parse: %@", [parseError localizedDescription] );
454}
455
456@end