Adds Cocos libraries.
authordsc <david.schoonover@gmail.com>
Wed, 8 Jun 2011 23:08:20 +0000 (16:08 -0700)
committerdsc <david.schoonover@gmail.com>
Wed, 8 Jun 2011 23:08:20 +0000 (16:08 -0700)
198 files changed:
libs/CocosDenshion/CDAudioManager.h [new file with mode: 0644]
libs/CocosDenshion/CDAudioManager.m [new file with mode: 0644]
libs/CocosDenshion/CDConfig.h [new file with mode: 0644]
libs/CocosDenshion/CDOpenALSupport.h [new file with mode: 0644]
libs/CocosDenshion/CDOpenALSupport.m [new file with mode: 0644]
libs/CocosDenshion/CocosDenshion.h [new file with mode: 0644]
libs/CocosDenshion/CocosDenshion.m [new file with mode: 0644]
libs/CocosDenshion/SimpleAudioEngine.h [new file with mode: 0644]
libs/CocosDenshion/SimpleAudioEngine.m [new file with mode: 0644]
libs/FontLabel/FontLabel.h [new file with mode: 0644]
libs/FontLabel/FontLabel.m [new file with mode: 0644]
libs/FontLabel/FontLabelStringDrawing.h [new file with mode: 0644]
libs/FontLabel/FontLabelStringDrawing.m [new file with mode: 0644]
libs/FontLabel/FontManager.h [new file with mode: 0644]
libs/FontLabel/FontManager.m [new file with mode: 0644]
libs/FontLabel/ZAttributedString.h [new file with mode: 0644]
libs/FontLabel/ZAttributedString.m [new file with mode: 0644]
libs/FontLabel/ZAttributedStringPrivate.h [new file with mode: 0644]
libs/FontLabel/ZFont.h [new file with mode: 0644]
libs/FontLabel/ZFont.m [new file with mode: 0644]
libs/TouchJSON/CDataScanner.h [new file with mode: 0644]
libs/TouchJSON/CDataScanner.m [new file with mode: 0644]
libs/TouchJSON/Extensions/CDataScanner_Extensions.h [new file with mode: 0644]
libs/TouchJSON/Extensions/CDataScanner_Extensions.m [new file with mode: 0644]
libs/TouchJSON/Extensions/NSDictionary_JSONExtensions.h [new file with mode: 0644]
libs/TouchJSON/Extensions/NSDictionary_JSONExtensions.m [new file with mode: 0644]
libs/TouchJSON/JSON/CJSONDeserializer.h [new file with mode: 0644]
libs/TouchJSON/JSON/CJSONDeserializer.m [new file with mode: 0644]
libs/TouchJSON/JSON/CJSONScanner.h [new file with mode: 0644]
libs/TouchJSON/JSON/CJSONScanner.m [new file with mode: 0644]
libs/TouchJSON/JSON/CJSONSerializer.h [new file with mode: 0644]
libs/TouchJSON/JSON/CJSONSerializer.m [new file with mode: 0644]
libs/TouchJSON/JSON/JSONRepresentation.h [new file with mode: 0644]
libs/cocos2d/CCAction.h [new file with mode: 0644]
libs/cocos2d/CCAction.m [new file with mode: 0644]
libs/cocos2d/CCActionCamera.h [new file with mode: 0644]
libs/cocos2d/CCActionCamera.m [new file with mode: 0644]
libs/cocos2d/CCActionEase.h [new file with mode: 0644]
libs/cocos2d/CCActionEase.m [new file with mode: 0644]
libs/cocos2d/CCActionGrid.h [new file with mode: 0644]
libs/cocos2d/CCActionGrid.m [new file with mode: 0644]
libs/cocos2d/CCActionGrid3D.h [new file with mode: 0644]
libs/cocos2d/CCActionGrid3D.m [new file with mode: 0644]
libs/cocos2d/CCActionInstant.h [new file with mode: 0644]
libs/cocos2d/CCActionInstant.m [new file with mode: 0644]
libs/cocos2d/CCActionInterval.h [new file with mode: 0644]
libs/cocos2d/CCActionInterval.m [new file with mode: 0644]
libs/cocos2d/CCActionManager.h [new file with mode: 0644]
libs/cocos2d/CCActionManager.m [new file with mode: 0644]
libs/cocos2d/CCActionPageTurn3D.h [new file with mode: 0644]
libs/cocos2d/CCActionPageTurn3D.m [new file with mode: 0644]
libs/cocos2d/CCActionProgressTimer.h [new file with mode: 0644]
libs/cocos2d/CCActionProgressTimer.m [new file with mode: 0644]
libs/cocos2d/CCActionTiledGrid.h [new file with mode: 0644]
libs/cocos2d/CCActionTiledGrid.m [new file with mode: 0644]
libs/cocos2d/CCActionTween.h [new file with mode: 0644]
libs/cocos2d/CCActionTween.m [new file with mode: 0644]
libs/cocos2d/CCAnimation.h [new file with mode: 0644]
libs/cocos2d/CCAnimation.m [new file with mode: 0644]
libs/cocos2d/CCAnimationCache.h [new file with mode: 0644]
libs/cocos2d/CCAnimationCache.m [new file with mode: 0644]
libs/cocos2d/CCAtlasNode.h [new file with mode: 0644]
libs/cocos2d/CCAtlasNode.m [new file with mode: 0644]
libs/cocos2d/CCBlockSupport.h [new file with mode: 0644]
libs/cocos2d/CCBlockSupport.m [new file with mode: 0644]
libs/cocos2d/CCCamera.h [new file with mode: 0644]
libs/cocos2d/CCCamera.m [new file with mode: 0644]
libs/cocos2d/CCConfiguration.h [new file with mode: 0644]
libs/cocos2d/CCConfiguration.m [new file with mode: 0644]
libs/cocos2d/CCDirector.h [new file with mode: 0644]
libs/cocos2d/CCDirector.m [new file with mode: 0644]
libs/cocos2d/CCDrawingPrimitives.h [new file with mode: 0644]
libs/cocos2d/CCDrawingPrimitives.m [new file with mode: 0644]
libs/cocos2d/CCGrabber.h [new file with mode: 0644]
libs/cocos2d/CCGrabber.m [new file with mode: 0644]
libs/cocos2d/CCGrid.h [new file with mode: 0644]
libs/cocos2d/CCGrid.m [new file with mode: 0644]
libs/cocos2d/CCLabelAtlas.h [new file with mode: 0644]
libs/cocos2d/CCLabelAtlas.m [new file with mode: 0644]
libs/cocos2d/CCLabelBMFont.h [new file with mode: 0644]
libs/cocos2d/CCLabelBMFont.m [new file with mode: 0644]
libs/cocos2d/CCLabelTTF.h [new file with mode: 0644]
libs/cocos2d/CCLabelTTF.m [new file with mode: 0644]
libs/cocos2d/CCLayer.h [new file with mode: 0644]
libs/cocos2d/CCLayer.m [new file with mode: 0644]
libs/cocos2d/CCMenu.h [new file with mode: 0644]
libs/cocos2d/CCMenu.m [new file with mode: 0644]
libs/cocos2d/CCMenuItem.h [new file with mode: 0644]
libs/cocos2d/CCMenuItem.m [new file with mode: 0644]
libs/cocos2d/CCMotionStreak.h [new file with mode: 0644]
libs/cocos2d/CCMotionStreak.m [new file with mode: 0644]
libs/cocos2d/CCNode.h [new file with mode: 0644]
libs/cocos2d/CCNode.m [new file with mode: 0644]
libs/cocos2d/CCParallaxNode.h [new file with mode: 0644]
libs/cocos2d/CCParallaxNode.m [new file with mode: 0644]
libs/cocos2d/CCParticleExamples.h [new file with mode: 0644]
libs/cocos2d/CCParticleExamples.m [new file with mode: 0644]
libs/cocos2d/CCParticleSystem.h [new file with mode: 0644]
libs/cocos2d/CCParticleSystem.m [new file with mode: 0644]
libs/cocos2d/CCParticleSystemPoint.h [new file with mode: 0644]
libs/cocos2d/CCParticleSystemPoint.m [new file with mode: 0644]
libs/cocos2d/CCParticleSystemQuad.h [new file with mode: 0644]
libs/cocos2d/CCParticleSystemQuad.m [new file with mode: 0644]
libs/cocos2d/CCProgressTimer.h [new file with mode: 0644]
libs/cocos2d/CCProgressTimer.m [new file with mode: 0644]
libs/cocos2d/CCProtocols.h [new file with mode: 0644]
libs/cocos2d/CCRenderTexture.h [new file with mode: 0644]
libs/cocos2d/CCRenderTexture.m [new file with mode: 0644]
libs/cocos2d/CCRibbon.h [new file with mode: 0644]
libs/cocos2d/CCRibbon.m [new file with mode: 0644]
libs/cocos2d/CCScene.h [new file with mode: 0644]
libs/cocos2d/CCScene.m [new file with mode: 0644]
libs/cocos2d/CCScheduler.h [new file with mode: 0644]
libs/cocos2d/CCScheduler.m [new file with mode: 0644]
libs/cocos2d/CCSprite.h [new file with mode: 0644]
libs/cocos2d/CCSprite.m [new file with mode: 0644]
libs/cocos2d/CCSpriteBatchNode.h [new file with mode: 0644]
libs/cocos2d/CCSpriteBatchNode.m [new file with mode: 0644]
libs/cocos2d/CCSpriteFrame.h [new file with mode: 0644]
libs/cocos2d/CCSpriteFrame.m [new file with mode: 0644]
libs/cocos2d/CCSpriteFrameCache.h [new file with mode: 0644]
libs/cocos2d/CCSpriteFrameCache.m [new file with mode: 0644]
libs/cocos2d/CCTMXLayer.h [new file with mode: 0644]
libs/cocos2d/CCTMXLayer.m [new file with mode: 0644]
libs/cocos2d/CCTMXObjectGroup.h [new file with mode: 0644]
libs/cocos2d/CCTMXObjectGroup.m [new file with mode: 0644]
libs/cocos2d/CCTMXTiledMap.h [new file with mode: 0644]
libs/cocos2d/CCTMXTiledMap.m [new file with mode: 0644]
libs/cocos2d/CCTMXXMLParser.h [new file with mode: 0644]
libs/cocos2d/CCTMXXMLParser.m [new file with mode: 0644]
libs/cocos2d/CCTexture2D.h [new file with mode: 0644]
libs/cocos2d/CCTexture2D.m [new file with mode: 0644]
libs/cocos2d/CCTextureAtlas.h [new file with mode: 0644]
libs/cocos2d/CCTextureAtlas.m [new file with mode: 0644]
libs/cocos2d/CCTextureCache.h [new file with mode: 0644]
libs/cocos2d/CCTextureCache.m [new file with mode: 0644]
libs/cocos2d/CCTexturePVR.h [new file with mode: 0644]
libs/cocos2d/CCTexturePVR.m [new file with mode: 0644]
libs/cocos2d/CCTileMapAtlas.h [new file with mode: 0644]
libs/cocos2d/CCTileMapAtlas.m [new file with mode: 0644]
libs/cocos2d/CCTransition.h [new file with mode: 0644]
libs/cocos2d/CCTransition.m [new file with mode: 0644]
libs/cocos2d/CCTransitionPageTurn.h [new file with mode: 0644]
libs/cocos2d/CCTransitionPageTurn.m [new file with mode: 0644]
libs/cocos2d/CCTransitionRadial.h [new file with mode: 0644]
libs/cocos2d/CCTransitionRadial.m [new file with mode: 0644]
libs/cocos2d/Platforms/CCGL.h [new file with mode: 0644]
libs/cocos2d/Platforms/CCNS.h [new file with mode: 0644]
libs/cocos2d/Platforms/Mac/CCDirectorMac.h [new file with mode: 0644]
libs/cocos2d/Platforms/Mac/CCDirectorMac.m [new file with mode: 0644]
libs/cocos2d/Platforms/Mac/CCEventDispatcher.h [new file with mode: 0644]
libs/cocos2d/Platforms/Mac/CCEventDispatcher.m [new file with mode: 0644]
libs/cocos2d/Platforms/Mac/MacGLView.h [new file with mode: 0644]
libs/cocos2d/Platforms/Mac/MacGLView.m [new file with mode: 0644]
libs/cocos2d/Platforms/Mac/MacWindow.h [new file with mode: 0644]
libs/cocos2d/Platforms/Mac/MacWindow.m [new file with mode: 0644]
libs/cocos2d/Platforms/iOS/CCDirectorIOS.h [new file with mode: 0644]
libs/cocos2d/Platforms/iOS/CCDirectorIOS.m [new file with mode: 0644]
libs/cocos2d/Platforms/iOS/CCTouchDelegateProtocol.h [new file with mode: 0644]
libs/cocos2d/Platforms/iOS/CCTouchDispatcher.h [new file with mode: 0644]
libs/cocos2d/Platforms/iOS/CCTouchDispatcher.m [new file with mode: 0644]
libs/cocos2d/Platforms/iOS/CCTouchHandler.h [new file with mode: 0644]
libs/cocos2d/Platforms/iOS/CCTouchHandler.m [new file with mode: 0644]
libs/cocos2d/Platforms/iOS/EAGLView.h [new file with mode: 0644]
libs/cocos2d/Platforms/iOS/EAGLView.m [new file with mode: 0644]
libs/cocos2d/Platforms/iOS/ES1Renderer.h [new file with mode: 0644]
libs/cocos2d/Platforms/iOS/ES1Renderer.m [new file with mode: 0644]
libs/cocos2d/Platforms/iOS/ESRenderer.h [new file with mode: 0644]
libs/cocos2d/Platforms/iOS/glu.c [new file with mode: 0644]
libs/cocos2d/Platforms/iOS/glu.h [new file with mode: 0644]
libs/cocos2d/Support/CCArray.h [new file with mode: 0644]
libs/cocos2d/Support/CCArray.m [new file with mode: 0644]
libs/cocos2d/Support/CCFileUtils.h [new file with mode: 0644]
libs/cocos2d/Support/CCFileUtils.m [new file with mode: 0644]
libs/cocos2d/Support/CCProfiling.h [new file with mode: 0644]
libs/cocos2d/Support/CCProfiling.m [new file with mode: 0644]
libs/cocos2d/Support/CGPointExtension.h [new file with mode: 0644]
libs/cocos2d/Support/CGPointExtension.m [new file with mode: 0644]
libs/cocos2d/Support/OpenGL_Internal.h [new file with mode: 0644]
libs/cocos2d/Support/TGAlib.h [new file with mode: 0644]
libs/cocos2d/Support/TGAlib.m [new file with mode: 0644]
libs/cocos2d/Support/TransformUtils.h [new file with mode: 0644]
libs/cocos2d/Support/TransformUtils.m [new file with mode: 0644]
libs/cocos2d/Support/ZipUtils.h [new file with mode: 0644]
libs/cocos2d/Support/ZipUtils.m [new file with mode: 0644]
libs/cocos2d/Support/base64.c [new file with mode: 0644]
libs/cocos2d/Support/base64.h [new file with mode: 0644]
libs/cocos2d/Support/ccCArray.h [new file with mode: 0644]
libs/cocos2d/Support/ccUtils.c [new file with mode: 0644]
libs/cocos2d/Support/ccUtils.h [new file with mode: 0644]
libs/cocos2d/Support/uthash.h [new file with mode: 0644]
libs/cocos2d/Support/utlist.h [new file with mode: 0644]
libs/cocos2d/ccConfig.h [new file with mode: 0644]
libs/cocos2d/ccMacros.h [new file with mode: 0644]
libs/cocos2d/ccTypes.h [new file with mode: 0644]
libs/cocos2d/cocos2d.h [new file with mode: 0644]
libs/cocos2d/cocos2d.m [new file with mode: 0644]
src/game/QQGame.mm

diff --git a/libs/CocosDenshion/CDAudioManager.h b/libs/CocosDenshion/CDAudioManager.h
new file mode 100644 (file)
index 0000000..2475929
--- /dev/null
@@ -0,0 +1,243 @@
+/*
+ Copyright (c) 2010 Steve Oldmeadow
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ THE SOFTWARE.
+ $Id$
+ */
+
+#import "CocosDenshion.h"
+#if __IPHONE_OS_VERSION_MIN_REQUIRED >= 30000
+    #import <AVFoundation/AVFoundation.h>
+#else
+    #import "CDXMacOSXSupport.h"
+#endif
+
+/** Different modes of the engine */
+typedef enum {
+       kAMM_FxOnly,                                    //!Other apps will be able to play audio
+       kAMM_FxPlusMusic,                               //!Only this app will play audio
+       kAMM_FxPlusMusicIfNoOtherAudio, //!If another app is playing audio at start up then allow it to continue and don't play music
+       kAMM_MediaPlayback,                             //!This app takes over audio e.g music player app
+       kAMM_PlayAndRecord                              //!App takes over audio and has input and output
+} tAudioManagerMode;
+
+/** Possible states of the engine */
+typedef enum {
+       kAMStateUninitialised, //!Audio manager has not been initialised - do not use
+       kAMStateInitialising,  //!Audio manager is in the process of initialising - do not use
+       kAMStateInitialised        //!Audio manager is initialised - safe to use
+} tAudioManagerState;
+
+typedef enum {
+       kAMRBDoNothing,                     //Audio manager will not do anything on resign or becoming active
+       kAMRBStopPlay,                          //Background music is stopped on resign and resumed on become active
+       kAMRBStop                                       //Background music is stopped on resign but not resumed - maybe because you want to do this from within your game
+} tAudioManagerResignBehavior;
+
+/** Notifications */
+extern NSString * const kCDN_AudioManagerInitialised;
+
+@interface CDAsynchInitialiser : NSOperation {}        
+@end
+
+/** CDAudioManager supports two long audio source channels called left and right*/
+typedef enum {
+       kASC_Left = 0,
+       kASC_Right = 1
+} tAudioSourceChannel; 
+
+typedef enum {
+       kLAS_Init,
+       kLAS_Loaded,
+       kLAS_Playing,
+       kLAS_Paused,
+       kLAS_Stopped,
+} tLongAudioSourceState;
+
+@class CDLongAudioSource;
+@protocol CDLongAudioSourceDelegate <NSObject>
+@optional
+/** The audio source completed playing */
+- (void) cdAudioSourceDidFinishPlaying:(CDLongAudioSource *) audioSource;
+/** The file used to load the audio source has changed */
+- (void) cdAudioSourceFileDidChange:(CDLongAudioSource *) audioSource;
+@end
+
+/**
+ CDLongAudioSource represents an audio source that has a long duration which makes
+ it costly to load into memory for playback as an effect using CDSoundEngine. Examples
+ include background music and narration tracks. The audio file may or may not be compressed.
+ Bear in mind that current iDevices can only use hardware to decode a single compressed
+ audio file at a time and playing multiple compressed files will result in a performance drop
+ as software decompression will take place.
+ @since v0.99
+ */
+@interface CDLongAudioSource : NSObject <AVAudioPlayerDelegate, CDAudioInterruptProtocol>{
+       AVAudioPlayer   *audioSourcePlayer;
+       NSString                *audioSourceFilePath;
+       NSInteger               numberOfLoops;
+       float                   volume;
+       id<CDLongAudioSourceDelegate> delegate; 
+       BOOL                    mute;
+       BOOL                    enabled_;
+       BOOL                    backgroundMusic;
+@public        
+       BOOL                    systemPaused;//Used for auto resign handling
+       NSTimeInterval  systemPauseLocation;//Used for auto resign handling
+@protected
+       tLongAudioSourceState state;
+}      
+@property (readonly) AVAudioPlayer *audioSourcePlayer;
+@property (readonly) NSString *audioSourceFilePath;
+@property (readwrite, nonatomic) NSInteger numberOfLoops;
+@property (readwrite, nonatomic) float volume;
+@property (assign) id<CDLongAudioSourceDelegate> delegate;
+/* This long audio source functions as background music */
+@property (readwrite, nonatomic) BOOL backgroundMusic;
+
+/** Loads the file into the audio source */
+-(void) load:(NSString*) filePath;
+/** Plays the audio source */
+-(void) play;
+/** Stops playing the audio soruce */
+-(void) stop;
+/** Pauses the audio source */
+-(void) pause;
+/** Rewinds the audio source */
+-(void) rewind;
+/** Resumes playing the audio source if it was paused */
+-(void) resume;
+/** Returns whether or not the audio source is playing */
+-(BOOL) isPlaying;
+
+@end
+
+/** 
+ CDAudioManager manages audio requirements for a game.  It provides access to a CDSoundEngine object
+ for playing sound effects.  It provides access to two CDLongAudioSource object (left and right channel)
+ for playing long duration audio such as background music and narration tracks.  Additionally it manages
+ the audio session to take care of things like audio session interruption and interacting with the audio
+ of other apps that are running on the device.
+ Requirements:
+ - Firmware: OS 2.2 or greater 
+ - Files: CDAudioManager.*, CocosDenshion.*
+ - Frameworks: OpenAL, AudioToolbox, AVFoundation
+ @since v0.8
+ */
+@interface CDAudioManager : NSObject <CDLongAudioSourceDelegate, CDAudioInterruptProtocol, AVAudioSessionDelegate> {
+       CDSoundEngine           *soundEngine;
+       CDLongAudioSource       *backgroundMusic;
+       NSMutableArray          *audioSourceChannels;
+       NSString*                       _audioSessionCategory;
+       BOOL                            _audioWasPlayingAtStartup;
+       tAudioManagerMode       _mode;
+       SEL backgroundMusicCompletionSelector;
+       id backgroundMusicCompletionListener;
+       BOOL willPlayBackgroundMusic;
+       BOOL _mute;
+       BOOL _resigned;
+       BOOL _interrupted;
+       BOOL _audioSessionActive;
+       BOOL enabled_;
+       
+       //For handling resign/become active
+       BOOL _isObservingAppEvents;
+       tAudioManagerResignBehavior _resignBehavior;
+}
+
+@property (readonly) CDSoundEngine *soundEngine;
+@property (readonly) CDLongAudioSource *backgroundMusic;
+@property (readonly) BOOL willPlayBackgroundMusic;
+
+/** Returns the shared singleton */
++ (CDAudioManager *) sharedManager;
++ (tAudioManagerState) sharedManagerState;
+/** Configures the shared singleton with a mode*/
++ (void) configure: (tAudioManagerMode) mode;
+/** Initializes the engine asynchronously with a mode */
++ (void) initAsynchronously: (tAudioManagerMode) mode;
+/** Initializes the engine synchronously with a mode, channel definition and a total number of channels */
+- (id) init: (tAudioManagerMode) mode;
+-(void) audioSessionInterrupted;
+-(void) audioSessionResumed;
+-(void) setResignBehavior:(tAudioManagerResignBehavior) resignBehavior autoHandle:(BOOL) autoHandle;
+/** Returns true is audio is muted at a hardware level e.g user has ringer switch set to off */
+-(BOOL) isDeviceMuted;
+/** Returns true if another app is playing audio such as the iPod music player */
+-(BOOL) isOtherAudioPlaying;
+/** Sets the way the audio manager interacts with the operating system such as whether it shares output with other apps or obeys the mute switch */
+-(void) setMode:(tAudioManagerMode) mode;
+/** Shuts down the shared audio manager instance so that it can be reinitialised */
++(void) end;
+
+/** Call if you want to use built in resign behavior but need to do some additional audio processing on resign active. */
+- (void) applicationWillResignActive;
+/** Call if you want to use built in resign behavior but need to do some additional audio processing on become active. */
+- (void) applicationDidBecomeActive;
+
+//New AVAudioPlayer API
+/** Loads the data from the specified file path to the channel's audio source */
+-(CDLongAudioSource*) audioSourceLoad:(NSString*) filePath channel:(tAudioSourceChannel) channel;
+/** Retrieves the audio source for the specified channel */
+-(CDLongAudioSource*) audioSourceForChannel:(tAudioSourceChannel) channel;
+
+//Legacy AVAudioPlayer API
+/** Plays music in background. The music can be looped or not
+ It is recommended to use .aac files as background music since they are decoded by the device (hardware).
+ */
+-(void) playBackgroundMusic:(NSString*) filePath loop:(BOOL) loop;
+/** Preloads a background music */
+-(void) preloadBackgroundMusic:(NSString*) filePath;
+/** Stops playing the background music */
+-(void) stopBackgroundMusic;
+/** Pauses the background music */
+-(void) pauseBackgroundMusic;
+/** Rewinds the background music */
+-(void) rewindBackgroundMusic;
+/** Resumes playing the background music */
+-(void) resumeBackgroundMusic;
+/** Returns whether or not the background music is playing */
+-(BOOL) isBackgroundMusicPlaying;
+
+-(void) setBackgroundMusicCompletionListener:(id) listener selector:(SEL) selector;
+
+@end
+
+/** Fader for long audio source objects */
+@interface CDLongAudioSourceFader : CDPropertyModifier{}
+@end
+
+static const int kCDNoBuffer = -1;
+
+/** Allows buffers to be associated with file names */
+@interface CDBufferManager:NSObject{
+       NSMutableDictionary* loadedBuffers;
+       NSMutableArray  *freedBuffers;
+       CDSoundEngine *soundEngine;
+       int nextBufferId;
+}
+
+-(id) initWithEngine:(CDSoundEngine *) theSoundEngine;
+-(int) bufferForFile:(NSString*) filePath create:(BOOL) create;
+-(void) releaseBufferForFile:(NSString *) filePath;
+
+@end
+
diff --git a/libs/CocosDenshion/CDAudioManager.m b/libs/CocosDenshion/CDAudioManager.m
new file mode 100644 (file)
index 0000000..0929f3c
--- /dev/null
@@ -0,0 +1,887 @@
+/*
+ Copyright (c) 2010 Steve Oldmeadow
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ THE SOFTWARE.
+ $Id$
+ */
+
+
+#import "CDAudioManager.h"
+
+NSString * const kCDN_AudioManagerInitialised = @"kCDN_AudioManagerInitialised";
+
+//NSOperation object used to asynchronously initialise 
+@implementation CDAsynchInitialiser
+
+-(void) main {
+       [super main];
+       [CDAudioManager sharedManager];
+}      
+
+@end
+
+@implementation CDLongAudioSource
+
+@synthesize audioSourcePlayer, audioSourceFilePath, delegate, backgroundMusic;
+
+-(id) init {
+       if ((self = [super init])) {
+               state = kLAS_Init;
+               volume = 1.0f;
+               mute = NO;
+               enabled_ = YES;
+       }
+       return self;
+}
+
+-(void) dealloc {
+       CDLOGINFO(@"Denshion::CDLongAudioSource - deallocating %@", self);
+       [audioSourcePlayer release];
+       [audioSourceFilePath release];
+       [super dealloc];
+}      
+
+-(void) load:(NSString*) filePath {
+       //We have alread loaded a file previously,  check if we are being asked to load the same file
+       if (state == kLAS_Init || ![filePath isEqualToString:audioSourceFilePath]) {
+               CDLOGINFO(@"Denshion::CDLongAudioSource - Loading new audio source %@",filePath);
+               //New file
+               if (state != kLAS_Init) {
+                       [audioSourceFilePath release];//Release old file path
+                       [audioSourcePlayer release];//Release old AVAudioPlayer, they can't be reused
+               }
+               audioSourceFilePath = [filePath copy];
+               NSError *error = nil;
+               NSString *path = [CDUtilities fullPathFromRelativePath:audioSourceFilePath];
+               audioSourcePlayer = [(AVAudioPlayer*)[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:path] error:&error];
+               if (error == nil) {
+                       [audioSourcePlayer prepareToPlay];
+                       audioSourcePlayer.delegate = self;
+                       if (delegate && [delegate respondsToSelector:@selector(cdAudioSourceFileDidChange:)]) {
+                               //Tell our delegate the file has changed
+                               [delegate cdAudioSourceFileDidChange:self];
+                       }       
+               } else {
+                       CDLOG(@"Denshion::CDLongAudioSource - Error initialising audio player: %@",error);
+               }       
+       } else {
+               //Same file - just return it to a consistent state
+               [self pause];
+               [self rewind];
+       }
+       audioSourcePlayer.volume = volume;
+       audioSourcePlayer.numberOfLoops = numberOfLoops;
+       state = kLAS_Loaded;
+}      
+
+-(void) play {
+       if (enabled_) {
+               self->systemPaused = NO;
+               [audioSourcePlayer play];
+       } else {
+               CDLOGINFO(@"Denshion::CDLongAudioSource long audio source didn't play because it is disabled");
+       }       
+}      
+
+-(void) stop {
+       [audioSourcePlayer stop];
+}      
+
+-(void) pause {
+       [audioSourcePlayer pause];
+}      
+
+-(void) rewind {
+       [audioSourcePlayer setCurrentTime:0];
+}
+
+-(void) resume {
+       [audioSourcePlayer play];
+}      
+
+-(BOOL) isPlaying {
+       if (state != kLAS_Init) {
+               return [audioSourcePlayer isPlaying];
+       } else {
+               return NO;
+       }
+}
+
+-(void) setVolume:(float) newVolume
+{
+       volume = newVolume;
+       if (state != kLAS_Init && !mute) {
+               audioSourcePlayer.volume = newVolume;
+       }       
+}
+
+-(float) volume 
+{
+       return volume;
+}
+
+#pragma mark Audio Interrupt Protocol
+-(BOOL) mute
+{
+       return mute;
+}      
+
+-(void) setMute:(BOOL) muteValue 
+{
+       if (mute != muteValue) {
+               if (mute) {
+                       //Turn sound back on
+                       audioSourcePlayer.volume = volume;
+               } else {
+                       audioSourcePlayer.volume = 0.0f;
+               }
+               mute = muteValue;
+       }       
+}      
+
+-(BOOL) enabled 
+{
+       return enabled_;
+}      
+
+-(void) setEnabled:(BOOL)enabledValue 
+{
+       if (enabledValue != enabled_) {
+               enabled_ = enabledValue;
+               if (!enabled_) {
+                       //"Stop" the sounds
+                       [self pause];
+                       [self rewind];
+               }       
+       }       
+}      
+
+-(NSInteger) numberOfLoops {
+       return numberOfLoops;
+}      
+
+-(void) setNumberOfLoops:(NSInteger) loopCount
+{
+       audioSourcePlayer.numberOfLoops = loopCount;
+       numberOfLoops = loopCount;
+}      
+
+- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag {
+       CDLOGINFO(@"Denshion::CDLongAudioSource - audio player finished");
+#if TARGET_IPHONE_SIMULATOR    
+       CDLOGINFO(@"Denshion::CDLongAudioSource - workaround for OpenAL clobbered audio issue");
+       //This is a workaround for an issue in all simulators (tested to 3.1.2).  Problem is 
+       //that OpenAL audio playback is clobbered when an AVAudioPlayer stops.  Workaround
+       //is to keep the player playing on an endless loop with 0 volume and then when
+       //it is played again reset the volume and set loop count appropriately.
+       //NB: this workaround is not foolproof but it is good enough for most situations.
+       player.numberOfLoops = -1;
+       player.volume = 0;
+       [player play];
+#endif 
+       if (delegate && [delegate respondsToSelector:@selector(cdAudioSourceDidFinishPlaying:)]) {
+               [delegate cdAudioSourceDidFinishPlaying:self];
+       }       
+}      
+
+-(void)audioPlayerBeginInterruption:(AVAudioPlayer *)player {
+       CDLOGINFO(@"Denshion::CDLongAudioSource - audio player interrupted");
+}
+
+-(void)audioPlayerEndInterruption:(AVAudioPlayer *)player {
+       CDLOGINFO(@"Denshion::CDLongAudioSource - audio player resumed");
+       if (self.backgroundMusic) {
+               //Check if background music can play as rules may have changed during 
+               //the interruption. This is to address a specific issue in 4.x when
+               //fast task switching
+               if([CDAudioManager sharedManager].willPlayBackgroundMusic) {
+                       [player play];
+               }       
+       } else {
+               [player play];
+       }       
+}      
+
+@end
+
+
+@interface CDAudioManager (PrivateMethods)
+-(BOOL) audioSessionSetActive:(BOOL) active;
+-(BOOL) audioSessionSetCategory:(NSString*) category;
+-(void) badAlContextHandler;
+@end
+
+
+@implementation CDAudioManager
+#define BACKGROUND_MUSIC_CHANNEL kASC_Left
+
+@synthesize soundEngine, willPlayBackgroundMusic;
+static CDAudioManager *sharedManager;
+static tAudioManagerState _sharedManagerState = kAMStateUninitialised;
+static tAudioManagerMode configuredMode;
+static BOOL configured = FALSE;
+
+-(BOOL) audioSessionSetActive:(BOOL) active {
+       NSError *activationError = nil;
+       if ([[AVAudioSession sharedInstance] setActive:active error:&activationError]) {
+               _audioSessionActive = active;
+               CDLOGINFO(@"Denshion::CDAudioManager - Audio session set active %i succeeded", active); 
+               return YES;
+       } else {
+               //Failed
+               CDLOG(@"Denshion::CDAudioManager - Audio session set active %i failed with error %@", active, activationError); 
+               return NO;
+       }       
+}      
+
+-(BOOL) audioSessionSetCategory:(NSString*) category {
+       NSError *categoryError = nil;
+       if ([[AVAudioSession sharedInstance] setCategory:category error:&categoryError]) {
+               CDLOGINFO(@"Denshion::CDAudioManager - Audio session set category %@ succeeded", category); 
+               return YES;
+       } else {
+               //Failed
+               CDLOG(@"Denshion::CDAudioManager - Audio session set category %@ failed with error %@", category, categoryError); 
+               return NO;
+       }       
+}      
+
+// Init
++ (CDAudioManager *) sharedManager
+{
+       @synchronized(self)     {
+               if (!sharedManager) {
+                       if (!configured) {
+                               //Set defaults here
+                               configuredMode = kAMM_FxPlusMusicIfNoOtherAudio;
+                       }
+                       sharedManager = [[CDAudioManager alloc] init:configuredMode];
+                       _sharedManagerState = kAMStateInitialised;//This is only really relevant when using asynchronous initialisation
+                       [[NSNotificationCenter defaultCenter] postNotificationName:kCDN_AudioManagerInitialised object:nil];
+               }       
+       }
+       return sharedManager;
+}
+
++ (tAudioManagerState) sharedManagerState {
+       return _sharedManagerState;
+}      
+
+/**
+ * Call this to set up audio manager asynchronously.  Initialisation is finished when sharedManagerState == kAMStateInitialised
+ */
++ (void) initAsynchronously: (tAudioManagerMode) mode {
+       @synchronized(self) {
+               if (_sharedManagerState == kAMStateUninitialised) {
+                       _sharedManagerState = kAMStateInitialising;
+                       [CDAudioManager configure:mode];
+                       CDAsynchInitialiser *initOp = [[[CDAsynchInitialiser alloc] init] autorelease];
+                       NSOperationQueue *opQ = [[[NSOperationQueue alloc] init] autorelease];
+                       [opQ addOperation:initOp];
+               }       
+       }
+}      
+
++ (id) alloc
+{
+       @synchronized(self)     {
+               NSAssert(sharedManager == nil, @"Attempted to allocate a second instance of a singleton.");
+               return [super alloc];
+       }
+       return nil;
+}
+
+/*
+ * Call this method before accessing the shared manager in order to configure the shared audio manager
+ */
++ (void) configure: (tAudioManagerMode) mode {
+       configuredMode = mode;
+       configured = TRUE;
+}      
+
+-(BOOL) isOtherAudioPlaying {
+       UInt32 isPlaying = 0;
+       UInt32 varSize = sizeof(isPlaying);
+       AudioSessionGetProperty (kAudioSessionProperty_OtherAudioIsPlaying, &varSize, &isPlaying);
+       return (isPlaying != 0);
+}
+
+-(void) setMode:(tAudioManagerMode) mode {
+
+       _mode = mode;
+       switch (_mode) {
+                       
+               case kAMM_FxOnly:
+                       //Share audio with other app
+                       CDLOGINFO(@"Denshion::CDAudioManager - Audio will be shared");
+                       //_audioSessionCategory = kAudioSessionCategory_AmbientSound;
+                       _audioSessionCategory = AVAudioSessionCategoryAmbient;
+                       willPlayBackgroundMusic = NO;
+                       break;
+                       
+               case kAMM_FxPlusMusic:
+                       //Use audio exclusively - if other audio is playing it will be stopped
+                       CDLOGINFO(@"Denshion::CDAudioManager -  Audio will be exclusive");
+                       //_audioSessionCategory = kAudioSessionCategory_SoloAmbientSound;
+                       _audioSessionCategory = AVAudioSessionCategorySoloAmbient;
+                       willPlayBackgroundMusic = YES;
+                       break;
+                       
+               case kAMM_MediaPlayback:
+                       //Use audio exclusively, ignore mute switch and sleep
+                       CDLOGINFO(@"Denshion::CDAudioManager -  Media playback mode, audio will be exclusive");
+                       //_audioSessionCategory = kAudioSessionCategory_MediaPlayback;
+                       _audioSessionCategory = AVAudioSessionCategoryPlayback;
+                       willPlayBackgroundMusic = YES;
+                       break;
+                       
+               case kAMM_PlayAndRecord:
+                       //Use audio exclusively, ignore mute switch and sleep, has inputs and outputs
+                       CDLOGINFO(@"Denshion::CDAudioManager -  Play and record mode, audio will be exclusive");
+                       //_audioSessionCategory = kAudioSessionCategory_PlayAndRecord;
+                       _audioSessionCategory = AVAudioSessionCategoryPlayAndRecord;
+                       willPlayBackgroundMusic = YES;
+                       break;
+                       
+               default:
+                       //kAudioManagerFxPlusMusicIfNoOtherAudio
+                       if ([self isOtherAudioPlaying]) {
+                               CDLOGINFO(@"Denshion::CDAudioManager - Other audio is playing audio will be shared");
+                               //_audioSessionCategory = kAudioSessionCategory_AmbientSound;
+                               _audioSessionCategory = AVAudioSessionCategoryAmbient;
+                               willPlayBackgroundMusic = NO;
+                       } else {
+                               CDLOGINFO(@"Denshion::CDAudioManager - Other audio is not playing audio will be exclusive");
+                               //_audioSessionCategory = kAudioSessionCategory_SoloAmbientSound;
+                               _audioSessionCategory = AVAudioSessionCategorySoloAmbient;
+                               willPlayBackgroundMusic = YES;
+                       }       
+                       
+                       break;
+       }
+        
+       [self audioSessionSetCategory:_audioSessionCategory];
+       
+}      
+
+/**
+ * This method is used to work around various bugs introduced in 4.x OS versions. In some circumstances the 
+ * audio session is interrupted but never resumed, this results in the loss of OpenAL audio when following 
+ * standard practices. If we detect this situation then we will attempt to resume the audio session ourselves.
+ * Known triggers: lock the device then unlock it (iOS 4.2 gm), playback a song using MPMediaPlayer (iOS 4.0)
+ */
+- (void) badAlContextHandler {
+       if (_interrupted && alcGetCurrentContext() == NULL) {
+               CDLOG(@"Denshion::CDAudioManager - bad OpenAL context detected, attempting to resume audio session");
+               [self audioSessionResumed];
+       }       
+}      
+
+- (id) init: (tAudioManagerMode) mode {
+       if ((self = [super init])) {
+               
+               //Initialise the audio session 
+               AVAudioSession* session = [AVAudioSession sharedInstance];
+               session.delegate = self;
+       
+               _mode = mode;
+               backgroundMusicCompletionSelector = nil;
+               _isObservingAppEvents = FALSE;
+               _mute = NO;
+               _resigned = NO;
+               _interrupted = NO;
+               enabled_ = YES;
+               _audioSessionActive = NO;
+               [self setMode:mode];
+               soundEngine = [[CDSoundEngine alloc] init];
+               
+               //Set up audioSource channels
+               audioSourceChannels = [[NSMutableArray alloc] init];
+               CDLongAudioSource *leftChannel = [[CDLongAudioSource alloc] init];
+               leftChannel.backgroundMusic = YES;
+               CDLongAudioSource *rightChannel = [[CDLongAudioSource alloc] init];
+               rightChannel.backgroundMusic = NO;
+               [audioSourceChannels insertObject:leftChannel atIndex:kASC_Left];       
+               [audioSourceChannels insertObject:rightChannel atIndex:kASC_Right];
+               [leftChannel release];
+               [rightChannel release];
+               //Used to support legacy APIs
+               backgroundMusic = [self audioSourceForChannel:BACKGROUND_MUSIC_CHANNEL];
+               backgroundMusic.delegate = self;
+               
+               //Add handler for bad al context messages, these are posted by the sound engine.
+               [[NSNotificationCenter defaultCenter] addObserver:self  selector:@selector(badAlContextHandler) name:kCDN_BadAlContext object:nil];
+
+       }       
+       return self;            
+}      
+
+-(void) dealloc {
+       CDLOGINFO(@"Denshion::CDAudioManager - deallocating");
+       [self stopBackgroundMusic];
+       [soundEngine release];
+       [[NSNotificationCenter defaultCenter] removeObserver:self];
+       [self audioSessionSetActive:NO];
+       [audioSourceChannels release];
+       [super dealloc];
+}      
+
+/** Retrieves the audio source for the specified channel */
+-(CDLongAudioSource*) audioSourceForChannel:(tAudioSourceChannel) channel 
+{
+       return (CDLongAudioSource*)[audioSourceChannels objectAtIndex:channel];
+}      
+
+/** Loads the data from the specified file path to the channel's audio source */
+-(CDLongAudioSource*) audioSourceLoad:(NSString*) filePath channel:(tAudioSourceChannel) channel
+{
+       CDLongAudioSource *audioSource = [self audioSourceForChannel:channel];
+       if (audioSource) {
+               [audioSource load:filePath];
+       }
+       return audioSource;
+}      
+
+-(BOOL) isBackgroundMusicPlaying {
+       return [self.backgroundMusic isPlaying];
+}      
+
+//NB: originally I tried using a route change listener and intended to store the current route,
+//however, on a 3gs running 3.1.2 no route change is generated when the user switches the 
+//ringer mute switch to off (i.e. enables sound) therefore polling is the only reliable way to
+//determine ringer switch state
+-(BOOL) isDeviceMuted {
+
+#if TARGET_IPHONE_SIMULATOR
+       //Calling audio route stuff on the simulator causes problems
+       return NO;
+#else  
+       CFStringRef newAudioRoute;
+       UInt32 propertySize = sizeof (CFStringRef);
+       
+       AudioSessionGetProperty (
+                                                        kAudioSessionProperty_AudioRoute,
+                                                        &propertySize,
+                                                        &newAudioRoute
+                                                        );
+       
+       if (newAudioRoute == NULL) {
+               //Don't expect this to happen but playing safe otherwise a null in the CFStringCompare will cause a crash
+               return YES;
+       } else {        
+               CFComparisonResult newDeviceIsMuted =   CFStringCompare (
+                                                                                                                                newAudioRoute,
+                                                                                                                                (CFStringRef) @"",
+                                                                                                                                0
+                                                                                                                                );
+               
+               return (newDeviceIsMuted == kCFCompareEqualTo);
+       }       
+#endif
+}      
+
+#pragma mark Audio Interrupt Protocol
+
+-(BOOL) mute {
+       return _mute;
+}      
+
+-(void) setMute:(BOOL) muteValue {
+       if (muteValue != _mute) {
+               _mute = muteValue;
+               [soundEngine setMute:muteValue];
+               for( CDLongAudioSource *audioSource in audioSourceChannels) {
+                       audioSource.mute = muteValue;
+               }       
+       }       
+}
+
+-(BOOL) enabled {
+       return enabled_;
+}      
+
+-(void) setEnabled:(BOOL) enabledValue {
+       if (enabledValue != enabled_) {
+               enabled_ = enabledValue;
+               [soundEngine setEnabled:enabled_];
+               for( CDLongAudioSource *audioSource in audioSourceChannels) {
+                       audioSource.enabled = enabled_;
+               }       
+       }       
+}
+
+-(CDLongAudioSource*) backgroundMusic
+{
+       return backgroundMusic;
+}      
+
+//Load background music ready for playing
+-(void) preloadBackgroundMusic:(NSString*) filePath
+{
+       [self.backgroundMusic load:filePath];   
+}      
+
+-(void) playBackgroundMusic:(NSString*) filePath loop:(BOOL) loop
+{
+       [self.backgroundMusic load:filePath];
+
+       if (!willPlayBackgroundMusic || _mute) {
+               CDLOGINFO(@"Denshion::CDAudioManager - play bgm aborted because audio is not exclusive or sound is muted");
+               return;
+       }
+               
+       if (loop) {
+               [self.backgroundMusic setNumberOfLoops:-1];
+       } else {
+               [self.backgroundMusic setNumberOfLoops:0];
+       }       
+       [self.backgroundMusic play];
+}
+
+-(void) stopBackgroundMusic
+{
+       [self.backgroundMusic stop];
+}
+
+-(void) pauseBackgroundMusic
+{
+       [self.backgroundMusic pause];
+}      
+
+-(void) resumeBackgroundMusic
+{
+       if (!willPlayBackgroundMusic || _mute) {
+               CDLOGINFO(@"Denshion::CDAudioManager - resume bgm aborted because audio is not exclusive or sound is muted");
+               return;
+       }
+       
+       [self.backgroundMusic resume];
+}      
+
+-(void) rewindBackgroundMusic
+{
+       [self.backgroundMusic rewind];
+}      
+
+-(void) setBackgroundMusicCompletionListener:(id) listener selector:(SEL) selector {
+       backgroundMusicCompletionListener = listener;
+       backgroundMusicCompletionSelector = selector;
+}      
+
+/*
+ * Call this method to have the audio manager automatically handle application resign and
+ * become active.  Pass a tAudioManagerResignBehavior to indicate the desired behavior
+ * for resigning and becoming active again.
+ *
+ * If autohandle is YES then the applicationWillResignActive and applicationDidBecomActive 
+ * methods are automatically called, otherwise you must call them yourself at the appropriate time.
+ *
+ * Based on idea of Dominique Bongard
+ */
+-(void) setResignBehavior:(tAudioManagerResignBehavior) resignBehavior autoHandle:(BOOL) autoHandle { 
+
+       if (!_isObservingAppEvents && autoHandle) {
+               [[NSNotificationCenter defaultCenter] addObserver:self  selector:@selector(applicationWillResignActive:) name:@"UIApplicationWillResignActiveNotification" object:nil];
+               [[NSNotificationCenter defaultCenter] addObserver:self  selector:@selector(applicationDidBecomeActive:) name:@"UIApplicationDidBecomeActiveNotification" object:nil];
+               [[NSNotificationCenter defaultCenter] addObserver:self  selector:@selector(applicationWillTerminate:) name:@"UIApplicationWillTerminateNotification" object:nil];
+               _isObservingAppEvents = TRUE;
+       }
+       _resignBehavior = resignBehavior;
+}      
+
+- (void) applicationWillResignActive {
+       self->_resigned = YES;
+       
+       //Set the audio sesssion to one that allows sharing so that other audio won't be clobbered on resume
+       [self audioSessionSetCategory:AVAudioSessionCategoryAmbient];
+       
+       switch (_resignBehavior) {
+                       
+               case kAMRBStopPlay:
+                       
+                       for( CDLongAudioSource *audioSource in audioSourceChannels) {
+                               if (audioSource.isPlaying) {
+                                       audioSource->systemPaused = YES;
+                                       audioSource->systemPauseLocation = audioSource.audioSourcePlayer.currentTime;
+                                       [audioSource stop];
+                               } else {
+                                       //Music is either paused or stopped, if it is paused it will be restarted
+                                       //by OS so we will stop it.
+                                       audioSource->systemPaused = NO;
+                                       [audioSource stop];
+                               }
+                       }
+                       break;
+                       
+               case kAMRBStop:
+                       //Stop music regardless of whether it is playing or not because if it was paused
+                       //then the OS would resume it
+                       for( CDLongAudioSource *audioSource in audioSourceChannels) {
+                               [audioSource stop];
+                       }       
+                       
+               default:
+                       break;
+                       
+       }                       
+       CDLOGINFO(@"Denshion::CDAudioManager - handled resign active");
+}
+
+//Called when application resigns active only if setResignBehavior has been called
+- (void) applicationWillResignActive:(NSNotification *) notification
+{
+       [self applicationWillResignActive];
+}      
+
+- (void) applicationDidBecomeActive {
+       
+       if (self->_resigned) {
+               _resigned = NO;
+               //Reset the mode incase something changed with audio while we were inactive
+               [self setMode:_mode];
+               switch (_resignBehavior) {
+                               
+                       case kAMRBStopPlay:
+                               
+                               //Music had been stopped but stop maintains current time
+                               //so playing again will continue from where music was before resign active.
+                               //We check if music can be played because while we were inactive the user might have
+                               //done something that should force music to not play such as starting a track in the iPod
+                               if (self.willPlayBackgroundMusic) {
+                                       for( CDLongAudioSource *audioSource in audioSourceChannels) {
+                                               if (audioSource->systemPaused) {
+                                                       [audioSource resume];
+                                                       audioSource->systemPaused = NO;
+                                               }
+                                       }
+                               }
+                               break;
+                               
+                       default:
+                               break;
+                               
+               }
+               CDLOGINFO(@"Denshion::CDAudioManager - audio manager handled become active");
+       }
+}
+
+//Called when application becomes active only if setResignBehavior has been called
+- (void) applicationDidBecomeActive:(NSNotification *) notification
+{
+       [self applicationDidBecomeActive];
+}
+
+//Called when application terminates only if setResignBehavior has been called 
+- (void) applicationWillTerminate:(NSNotification *) notification
+{
+       CDLOGINFO(@"Denshion::CDAudioManager - audio manager handling terminate");
+       [self stopBackgroundMusic];
+}
+
+/** The audio source completed playing */
+- (void) cdAudioSourceDidFinishPlaying:(CDLongAudioSource *) audioSource {
+       CDLOGINFO(@"Denshion::CDAudioManager - audio manager got told background music finished");
+       if (backgroundMusicCompletionSelector != nil) {
+               [backgroundMusicCompletionListener performSelector:backgroundMusicCompletionSelector];
+       }       
+}      
+
+-(void) beginInterruption {
+       CDLOGINFO(@"Denshion::CDAudioManager - begin interruption");
+       [self audioSessionInterrupted];
+}
+
+-(void) endInterruption {
+       CDLOGINFO(@"Denshion::CDAudioManager - end interruption");
+       [self audioSessionResumed];
+}
+
+#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 40000
+-(void) endInterruptionWithFlags:(NSUInteger)flags {
+       CDLOGINFO(@"Denshion::CDAudioManager - interruption ended with flags %i",flags);
+       if (flags == AVAudioSessionInterruptionFlags_ShouldResume) {
+               [self audioSessionResumed];
+       }       
+}
+#endif
+
+-(void)audioSessionInterrupted 
+{ 
+    if (!_interrupted) {
+               CDLOGINFO(@"Denshion::CDAudioManager - Audio session interrupted"); 
+               _interrupted = YES;
+
+               // Deactivate the current audio session 
+           [self audioSessionSetActive:NO];
+               
+               if (alcGetCurrentContext() != NULL) {
+                       CDLOGINFO(@"Denshion::CDAudioManager - Setting OpenAL context to NULL"); 
+
+                       ALenum  error = AL_NO_ERROR;
+
+                       // set the current context to NULL will 'shutdown' openAL 
+                       alcMakeContextCurrent(NULL); 
+               
+                       if((error = alGetError()) != AL_NO_ERROR) {
+                               CDLOG(@"Denshion::CDAudioManager - Error making context current %x\n", error);
+                       } 
+                       #pragma unused(error)
+               }
+       }       
+} 
+
+-(void)audioSessionResumed 
+{ 
+       if (_interrupted) {
+               CDLOGINFO(@"Denshion::CDAudioManager - Audio session resumed"); 
+               _interrupted = NO;
+               
+               BOOL activationResult = NO;
+               // Reactivate the current audio session
+               activationResult = [self audioSessionSetActive:YES]; 
+               
+               //This code is to handle a problem with iOS 4.0 and 4.01 where reactivating the session can fail if
+               //task switching is performed too rapidly. A test case that reliably reproduces the issue is to call the
+               //iPhone and then hang up after two rings (timing may vary ;))
+               //Basically we keep waiting and trying to let the OS catch up with itself but the number of tries is
+               //limited.
+               if (!activationResult) {
+                       CDLOG(@"Denshion::CDAudioManager - Failure reactivating audio session, will try wait-try cycle"); 
+                       int activateCount = 0;
+                       while (!activationResult && activateCount < 10) {
+                               [NSThread sleepForTimeInterval:0.5];
+                               activationResult = [self audioSessionSetActive:YES]; 
+                               activateCount++;
+                               CDLOGINFO(@"Denshion::CDAudioManager - Reactivation attempt %i status = %i",activateCount,activationResult); 
+                       }       
+               }
+               
+               if (alcGetCurrentContext() == NULL) {
+                       CDLOGINFO(@"Denshion::CDAudioManager - Restoring OpenAL context"); 
+                       ALenum  error = AL_NO_ERROR;
+                       // Restore open al context 
+                       alcMakeContextCurrent([soundEngine openALContext]); 
+                       if((error = alGetError()) != AL_NO_ERROR) {
+                               CDLOG(@"Denshion::CDAudioManager - Error making context current%x\n", error);
+                       } 
+                       #pragma unused(error)
+               }       
+       }       
+}
+
++(void) end {
+       [sharedManager release];
+       sharedManager = nil;
+}      
+
+@end
+
+///////////////////////////////////////////////////////////////////////////////////////
+@implementation CDLongAudioSourceFader
+
+-(void) _setTargetProperty:(float) newVal {
+       ((CDLongAudioSource*)target).volume = newVal;
+}      
+
+-(float) _getTargetProperty {
+       return ((CDLongAudioSource*)target).volume;
+}
+
+-(void) _stopTarget {
+       //Pause instead of stop as stop releases resources and causes problems in the simulator
+       [((CDLongAudioSource*)target) pause];
+}
+
+-(Class) _allowableType {
+       return [CDLongAudioSource class];
+}      
+
+@end
+///////////////////////////////////////////////////////////////////////////////////////
+@implementation CDBufferManager
+
+-(id) initWithEngine:(CDSoundEngine *) theSoundEngine {
+       if ((self = [super init])) {
+               soundEngine = theSoundEngine;
+               loadedBuffers = [[NSMutableDictionary alloc] initWithCapacity:CD_BUFFERS_START];
+               freedBuffers = [[NSMutableArray alloc] init];
+               nextBufferId = 0;
+       }       
+       return self;
+}      
+
+-(void) dealloc {
+       [loadedBuffers release];
+       [freedBuffers release];
+       [super dealloc];
+}      
+
+-(int) bufferForFile:(NSString*) filePath create:(BOOL) create {
+       
+       NSNumber* soundId = (NSNumber*)[loadedBuffers objectForKey:filePath];
+       if(soundId == nil)
+       {
+               if (create) {
+                       NSNumber* bufferId = nil;
+                       //First try to get a buffer from the free buffers
+                       if ([freedBuffers count] > 0) {
+                               bufferId = [[[freedBuffers lastObject] retain] autorelease];
+                               [freedBuffers removeLastObject]; 
+                               CDLOGINFO(@"Denshion::CDBufferManager reusing buffer id %i",[bufferId intValue]);
+                       } else {
+                               bufferId = [[NSNumber alloc] initWithInt:nextBufferId];
+                               [bufferId autorelease];
+                               CDLOGINFO(@"Denshion::CDBufferManager generating new buffer id %i",[bufferId intValue]);
+                               nextBufferId++;
+                       }
+                       
+                       if ([soundEngine loadBuffer:[bufferId intValue] filePath:filePath]) {
+                               //File successfully loaded
+                               CDLOGINFO(@"Denshion::CDBufferManager buffer loaded %@ %@",bufferId,filePath);
+                               [loadedBuffers setObject:bufferId forKey:filePath];
+                               return [bufferId intValue];
+                       } else {
+                               //File didn't load, put buffer id on free list
+                               [freedBuffers addObject:bufferId];
+                               return kCDNoBuffer;
+                       }       
+               } else {
+                       //No matching buffer was found
+                       return kCDNoBuffer;
+               }       
+       } else {
+               return [soundId intValue];
+       }       
+}      
+
+-(void) releaseBufferForFile:(NSString *) filePath {
+       int bufferId = [self bufferForFile:filePath create:NO];
+       if (bufferId != kCDNoBuffer) {
+               [soundEngine unloadBuffer:bufferId];
+               [loadedBuffers removeObjectForKey:filePath];
+               NSNumber *freedBufferId = [[NSNumber alloc] initWithInt:bufferId];
+               [freedBufferId autorelease];
+               [freedBuffers addObject:freedBufferId];
+       }       
+}      
+@end
+
+
+
diff --git a/libs/CocosDenshion/CDConfig.h b/libs/CocosDenshion/CDConfig.h
new file mode 100644 (file)
index 0000000..2bd8f76
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ Copyright (c) 2010 Steve Oldmeadow
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ THE SOFTWARE.
+ $Id$
+ */
+#define COCOSDENSHION_VERSION "Aphex.rc"
+
+
+/**
+ If enabled code useful for debugging such as parameter check assertions will be performed.
+ If you experience any problems you should enable this and test your code with a debug build.
+ */
+//#define CD_DEBUG 1
+
+/**
+ The total number of sounds/buffers that can be loaded assuming memory is sufficient
+ */
+//Number of buffers slots that will be initially created
+#define CD_BUFFERS_START 64
+//Number of buffers that will be added 
+#define CD_BUFFERS_INCREMENT 16
+
+/**
+ If enabled, OpenAL code will use static buffers. When static buffers are used the audio
+ data is managed outside of OpenAL, this eliminates a memcpy operation which leads to 
+ higher performance when loading sounds.
+ However, the downside is that when the audio data is freed you must
+ be certain that it is no longer being accessed otherwise your app will crash. Testing on OS 2.2.1
+ and 3.1.2 has shown that this may occur if a buffer is being used by a source with state = AL_PLAYING
+ when the buffer is deleted. If the data is freed too quickly after the source is stopped then
+ a crash will occur. The implemented workaround is that when static buffers are used the unloadBuffer code will wait for
+ any playing sources to finish playing before the associated buffer and data are deleted, however, this delay may negate any 
+ performance gains that are achieved during loading.
+ Performance tests on a 1st gen iPod running OS 2.2.1 loading the CocosDenshionDemo sounds were ~0.14 seconds without
+ static buffers and ~0.12 seconds when using static buffers.
+
+ */
+//#define CD_USE_STATIC_BUFFERS 1
+
+
diff --git a/libs/CocosDenshion/CDOpenALSupport.h b/libs/CocosDenshion/CDOpenALSupport.h
new file mode 100644 (file)
index 0000000..661c69e
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ Disclaimer: IMPORTANT:  This Apple software is supplied to you by
+ Apple Inc. ("Apple") in consideration of your agreement to the
+ following terms, and your use, installation, modification or
+ redistribution of this Apple software constitutes acceptance of these
+ terms.  If you do not agree with these terms, please do not use,
+ install, modify or redistribute this Apple software.
+ In consideration of your agreement to abide by the following terms, and
+ subject to these terms, Apple grants you a personal, non-exclusive
+ license, under Apple's copyrights in this original Apple software (the
+ "Apple Software"), to use, reproduce, modify and redistribute the Apple
+ Software, with or without modifications, in source and/or binary forms;
+ provided that if you redistribute the Apple Software in its entirety and
+ without modifications, you must retain this notice and the following
+ text and disclaimers in all such redistributions of the Apple Software.
+ Neither the name, trademarks, service marks or logos of Apple Inc.
+ may be used to endorse or promote products derived from the Apple
+ Software without specific prior written permission from Apple.  Except
+ as expressly stated in this notice, no other rights or licenses, express
+ or implied, are granted by Apple herein, including but not limited to
+ any patent rights that may be infringed by your derivative works or by
+ other works in which the Apple Software may be incorporated.
+ The Apple Software is provided by Apple on an "AS IS" basis.  APPLE
+ MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION
+ THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS
+ FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND
+ OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
+ IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL
+ OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION,
+ MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED
+ AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE),
+ STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+ Copyright (C) 2009 Apple Inc. All Rights Reserved.
+ $Id$
+ */
+
+/*
+ This file contains code from version 1.1 and 1.4 of MyOpenALSupport.h taken from Apple's oalTouch version.
+ The 1.4 version code is used for loading IMA4 files, however, this code causes very noticeable clicking
+ when used to load wave files that are looped so the 1.1 version code is used specifically for loading
+ wav files.
+ */
+
+#ifndef __CD_OPENAL_H
+#define __CD_OPENAL_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif 
+       
+
+#import <OpenAL/al.h>
+#import <OpenAL/alc.h>
+#import <CoreFoundation/CFURL.h>
+
+
+//Taken from oalTouch MyOpenALSupport 1.1
+void* CDloadWaveAudioData(CFURLRef inFileURL, ALsizei *outDataSize, ALenum *outDataFormat, ALsizei*    outSampleRate);
+void* CDloadCafAudioData(CFURLRef inFileURL, ALsizei *outDataSize, ALenum *outDataFormat, ALsizei* outSampleRate);
+void* CDGetOpenALAudioData(CFURLRef inFileURL, ALsizei *outDataSize, ALenum *outDataFormat, ALsizei* outSampleRate);
+       
+#ifdef __cplusplus
+}
+#endif
+
+#endif 
+
+
diff --git a/libs/CocosDenshion/CDOpenALSupport.m b/libs/CocosDenshion/CDOpenALSupport.m
new file mode 100644 (file)
index 0000000..ab0df8e
--- /dev/null
@@ -0,0 +1,246 @@
+/*
+ Disclaimer: IMPORTANT:  This Apple software is supplied to you by
+ Apple Inc. ("Apple") in consideration of your agreement to the
+ following terms, and your use, installation, modification or
+ redistribution of this Apple software constitutes acceptance of these
+ terms.  If you do not agree with these terms, please do not use,
+ install, modify or redistribute this Apple software.
+ In consideration of your agreement to abide by the following terms, and
+ subject to these terms, Apple grants you a personal, non-exclusive
+ license, under Apple's copyrights in this original Apple software (the
+ "Apple Software"), to use, reproduce, modify and redistribute the Apple
+ Software, with or without modifications, in source and/or binary forms;
+ provided that if you redistribute the Apple Software in its entirety and
+ without modifications, you must retain this notice and the following
+ text and disclaimers in all such redistributions of the Apple Software.
+ Neither the name, trademarks, service marks or logos of Apple Inc.
+ may be used to endorse or promote products derived from the Apple
+ Software without specific prior written permission from Apple.  Except
+ as expressly stated in this notice, no other rights or licenses, express
+ or implied, are granted by Apple herein, including but not limited to
+ any patent rights that may be infringed by your derivative works or by
+ other works in which the Apple Software may be incorporated.
+ The Apple Software is provided by Apple on an "AS IS" basis.  APPLE
+ MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION
+ THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS
+ FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND
+ OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
+ IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL
+ OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION,
+ MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED
+ AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE),
+ STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+ Copyright (C) 2009 Apple Inc. All Rights Reserved.
+ $Id: CDOpenALSupport.h 16 2010-03-11 06:22:10Z steveoldmeadow $
+ */
+
+#import "CDOpenALSupport.h"
+#import "CocosDenshion.h"
+#import <AudioToolbox/AudioToolbox.h>
+#import <AudioToolbox/ExtendedAudioFile.h>
+
+//Taken from oalTouch MyOpenALSupport 1.1
+void* CDloadWaveAudioData(CFURLRef inFileURL, ALsizei *outDataSize, ALenum *outDataFormat, ALsizei*    outSampleRate)
+{
+       OSStatus                                                err = noErr;    
+       UInt64                                                  fileDataSize = 0;
+       AudioStreamBasicDescription             theFileFormat;
+       UInt32                                                  thePropertySize = sizeof(theFileFormat);
+       AudioFileID                                             afid = 0;
+       void*                                                   theData = NULL;
+       
+       // Open a file with ExtAudioFileOpen()
+       err = AudioFileOpenURL(inFileURL, kAudioFileReadPermission, 0, &afid);
+       if(err) { CDLOG(@"MyGetOpenALAudioData: AudioFileOpenURL FAILED, Error = %ld\n", err); goto Exit; }
+       
+       // Get the audio data format
+       err = AudioFileGetProperty(afid, kAudioFilePropertyDataFormat, &thePropertySize, &theFileFormat);
+       if(err) { CDLOG(@"MyGetOpenALAudioData: AudioFileGetProperty(kAudioFileProperty_DataFormat) FAILED, Error = %ld\n", err); goto Exit; }
+       
+       if (theFileFormat.mChannelsPerFrame > 2)  { 
+               CDLOG(@"MyGetOpenALAudioData - Unsupported Format, channel count is greater than stereo\n"); goto Exit;
+       }
+       
+       if ((theFileFormat.mFormatID != kAudioFormatLinearPCM) || (!TestAudioFormatNativeEndian(theFileFormat))) { 
+               CDLOG(@"MyGetOpenALAudioData - Unsupported Format, must be little-endian PCM\n"); goto Exit;
+       }
+       
+       if ((theFileFormat.mBitsPerChannel != 8) && (theFileFormat.mBitsPerChannel != 16)) { 
+               CDLOG(@"MyGetOpenALAudioData - Unsupported Format, must be 8 or 16 bit PCM\n"); goto Exit;
+       }
+       
+       
+       thePropertySize = sizeof(fileDataSize);
+       err = AudioFileGetProperty(afid, kAudioFilePropertyAudioDataByteCount, &thePropertySize, &fileDataSize);
+       if(err) { CDLOG(@"MyGetOpenALAudioData: AudioFileGetProperty(kAudioFilePropertyAudioDataByteCount) FAILED, Error = %ld\n", err); goto Exit; }
+       
+       // Read all the data into memory
+       UInt32          dataSize = (UInt32)fileDataSize;
+       theData = malloc(dataSize);
+       if (theData)
+       {
+               AudioFileReadBytes(afid, false, 0, &dataSize, theData);
+               if(err == noErr)
+               {
+                       // success
+                       *outDataSize = (ALsizei)dataSize;
+                       //This fix was added by me, however, 8 bit sounds have a clipping sound at the end so aren't really usable (SO)
+                       if (theFileFormat.mBitsPerChannel == 16) { 
+                               *outDataFormat = (theFileFormat.mChannelsPerFrame > 1) ? AL_FORMAT_STEREO16 : AL_FORMAT_MONO16;
+                       } else {
+                               *outDataFormat = (theFileFormat.mChannelsPerFrame > 1) ? AL_FORMAT_STEREO8 : AL_FORMAT_MONO8;   
+                       }       
+                       *outSampleRate = (ALsizei)theFileFormat.mSampleRate;
+               }
+               else 
+               { 
+                       // failure
+                       free (theData);
+                       theData = NULL; // make sure to return NULL
+                       CDLOG(@"MyGetOpenALAudioData: ExtAudioFileRead FAILED, Error = %ld\n", err); goto Exit;
+               }       
+       }
+       
+Exit:
+       // Dispose the ExtAudioFileRef, it is no longer needed
+       if (afid) AudioFileClose(afid);
+       return theData;
+}
+
+//Taken from oalTouch MyOpenALSupport 1.4
+void* CDloadCafAudioData(CFURLRef inFileURL, ALsizei *outDataSize, ALenum *outDataFormat, ALsizei* outSampleRate)
+{
+       OSStatus                                                status = noErr;
+       BOOL                                                    abort = NO;
+       SInt64                                                  theFileLengthInFrames = 0;
+       AudioStreamBasicDescription             theFileFormat;
+       UInt32                                                  thePropertySize = sizeof(theFileFormat);
+       ExtAudioFileRef                                 extRef = NULL;
+       void*                                                   theData = NULL;
+       AudioStreamBasicDescription             theOutputFormat;
+       UInt32                                                  dataSize = 0;
+       
+       // Open a file with ExtAudioFileOpen()
+       status = ExtAudioFileOpenURL(inFileURL, &extRef);
+       if (status != noErr)
+       {
+               CDLOG(@"MyGetOpenALAudioData: ExtAudioFileOpenURL FAILED, Error = %ld\n", status);
+               abort = YES;
+       }
+       if (abort)
+               goto Exit;
+       
+       // Get the audio data format
+       status = ExtAudioFileGetProperty(extRef, kExtAudioFileProperty_FileDataFormat, &thePropertySize, &theFileFormat);
+       if (status != noErr)
+       {
+               CDLOG(@"MyGetOpenALAudioData: ExtAudioFileGetProperty(kExtAudioFileProperty_FileDataFormat) FAILED, Error = %ld\n", status);
+               abort = YES;
+       }
+       if (abort)
+               goto Exit;
+       
+       if (theFileFormat.mChannelsPerFrame > 2)
+       {
+               CDLOG(@"MyGetOpenALAudioData - Unsupported Format, channel count is greater than stereo\n");
+               abort = YES;
+       }
+       if (abort)
+               goto Exit;
+       
+       // Set the client format to 16 bit signed integer (native-endian) data
+       // Maintain the channel count and sample rate of the original source format
+       theOutputFormat.mSampleRate = theFileFormat.mSampleRate;
+       theOutputFormat.mChannelsPerFrame = theFileFormat.mChannelsPerFrame;
+       
+       theOutputFormat.mFormatID = kAudioFormatLinearPCM;
+       theOutputFormat.mBytesPerPacket = 2 * theOutputFormat.mChannelsPerFrame;
+       theOutputFormat.mFramesPerPacket = 1;
+       theOutputFormat.mBytesPerFrame = 2 * theOutputFormat.mChannelsPerFrame;
+       theOutputFormat.mBitsPerChannel = 16;
+       theOutputFormat.mFormatFlags = kAudioFormatFlagsNativeEndian | kAudioFormatFlagIsPacked | kAudioFormatFlagIsSignedInteger;
+       
+       // Set the desired client (output) data format
+       status = ExtAudioFileSetProperty(extRef, kExtAudioFileProperty_ClientDataFormat, sizeof(theOutputFormat), &theOutputFormat);
+       if (status != noErr)
+       {
+               CDLOG(@"MyGetOpenALAudioData: ExtAudioFileSetProperty(kExtAudioFileProperty_ClientDataFormat) FAILED, Error = %ld\n", status);
+               abort = YES;
+       }
+       if (abort)
+               goto Exit;
+       
+       // Get the total frame count
+       thePropertySize = sizeof(theFileLengthInFrames);
+       status = ExtAudioFileGetProperty(extRef, kExtAudioFileProperty_FileLengthFrames, &thePropertySize, &theFileLengthInFrames);
+       if (status != noErr)
+       {
+               CDLOG(@"MyGetOpenALAudioData: ExtAudioFileGetProperty(kExtAudioFileProperty_FileLengthFrames) FAILED, Error = %ld\n", status);
+               abort = YES;
+       }
+       if (abort)
+               goto Exit;
+       
+       // Read all the data into memory
+       dataSize = (UInt32) theFileLengthInFrames * theOutputFormat.mBytesPerFrame;
+       theData = malloc(dataSize);
+       if (theData)
+       {
+               AudioBufferList         theDataBuffer;
+               theDataBuffer.mNumberBuffers = 1;
+               theDataBuffer.mBuffers[0].mDataByteSize = dataSize;
+               theDataBuffer.mBuffers[0].mNumberChannels = theOutputFormat.mChannelsPerFrame;
+               theDataBuffer.mBuffers[0].mData = theData;
+               
+               // Read the data into an AudioBufferList
+               status = ExtAudioFileRead(extRef, (UInt32*)&theFileLengthInFrames, &theDataBuffer);
+               if(status == noErr)
+               {
+                       // success
+                       *outDataSize = (ALsizei)dataSize;
+                       *outDataFormat = (theOutputFormat.mChannelsPerFrame > 1) ? AL_FORMAT_STEREO16 : AL_FORMAT_MONO16;
+                       *outSampleRate = (ALsizei)theOutputFormat.mSampleRate;
+               }
+               else
+               {
+                       // failure
+                       free (theData);
+                       theData = NULL; // make sure to return NULL
+                       CDLOG(@"MyGetOpenALAudioData: ExtAudioFileRead FAILED, Error = %ld\n", status);
+                       abort = YES;
+               }
+       }
+       if (abort)
+               goto Exit;
+       
+Exit:
+       // Dispose the ExtAudioFileRef, it is no longer needed
+       if (extRef) ExtAudioFileDispose(extRef);
+       return theData;
+}
+
+void* CDGetOpenALAudioData(CFURLRef inFileURL, ALsizei *outDataSize, ALenum *outDataFormat, ALsizei*   outSampleRate) {
+       
+       CFStringRef extension = CFURLCopyPathExtension(inFileURL);
+       CFComparisonResult isWavFile = 0;
+       if (extension != NULL) {
+               isWavFile = CFStringCompare (extension,(CFStringRef)@"wav", kCFCompareCaseInsensitive);
+               CFRelease(extension);
+       }       
+       
+       if (isWavFile == kCFCompareEqualTo) {
+               return CDloadWaveAudioData(inFileURL, outDataSize, outDataFormat, outSampleRate);       
+       } else {
+               return CDloadCafAudioData(inFileURL, outDataSize, outDataFormat, outSampleRate);                
+       }
+}
+
diff --git a/libs/CocosDenshion/CocosDenshion.h b/libs/CocosDenshion/CocosDenshion.h
new file mode 100644 (file)
index 0000000..638d852
--- /dev/null
@@ -0,0 +1,440 @@
+/*
+ Copyright (c) 2010 Steve Oldmeadow
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ THE SOFTWARE.
+ $Id$
+ */
+
+
+
+/** 
+@file
+@b IMPORTANT
+There are 3 different ways of using CocosDenshion. Depending on which you choose you 
+will need to include different files and frameworks.
+
+@par SimpleAudioEngine
+This is recommended for basic audio requirements. If you just want to play some sound fx
+and some background music and have no interest in learning the lower level workings then
+this is the interface to use.
+
+Requirements:
+ - Firmware: OS 2.2 or greater 
+ - Files: SimpleAudioEngine.*, CocosDenshion.*
+ - Frameworks: OpenAL, AudioToolbox, AVFoundation
+@par CDAudioManager
+CDAudioManager is basically a thin wrapper around an AVAudioPlayer object used for playing
+background music and a CDSoundEngine object used for playing sound effects. It manages the
+audio session for you deals with audio session interruption. It is fairly low level and it
+is expected you have some understanding of the underlying technologies. For example, for 
+many use cases regarding background music it is expected you will work directly with the
+backgroundMusic AVAudioPlayer which is exposed as a property.
+Requirements:
+  - Firmware: OS 2.2 or greater 
+  - Files: CDAudioManager.*, CocosDenshion.*
+  - Frameworks: OpenAL, AudioToolbox, AVFoundation
+
+@par CDSoundEngine
+CDSoundEngine is a sound engine built upon OpenAL and derived from Apple's oalTouch 
+example. It can playback up to 32 sounds simultaneously with control over pitch, pan
+and gain.  It can be set up to handle audio session interruption automatically.  You 
+may decide to use CDSoundEngine directly instead of CDAudioManager or SimpleAudioEngine
+because you require OS 2.0 compatibility.
+Requirements:
+  - Firmware: OS 2.0 or greater 
+  - Files: CocosDenshion.*
+  - Frameworks: OpenAL, AudioToolbox
+*/ 
+
+#import <OpenAL/al.h>
+#import <OpenAL/alc.h>
+#import <AudioToolbox/AudioToolbox.h>
+#import <Foundation/Foundation.h>
+#import "CDConfig.h"
+
+
+#if !defined(CD_DEBUG) || CD_DEBUG == 0
+#define CDLOG(...) do {} while (0)
+#define CDLOGINFO(...) do {} while (0)
+
+#elif CD_DEBUG == 1
+#define CDLOG(...) NSLog(__VA_ARGS__)
+#define CDLOGINFO(...) do {} while (0)
+
+#elif CD_DEBUG > 1
+#define CDLOG(...) NSLog(__VA_ARGS__)
+#define CDLOGINFO(...) NSLog(__VA_ARGS__)
+#endif // CD_DEBUG
+
+
+#import "CDOpenALSupport.h"
+
+//Tested source limit on 2.2.1 and 3.1.2 with up to 128 sources and appears to work. Older OS versions e.g 2.2 may support only 32
+#define CD_SOURCE_LIMIT 32 //Total number of sources we will ever want, may actually get less
+#define CD_NO_SOURCE 0xFEEDFAC //Return value indicating playback failed i.e. no source
+#define CD_IGNORE_AUDIO_SESSION 0xBEEFBEE //Used internally to indicate audio session will not be handled
+#define CD_MUTE      0xFEEDBAB //Return value indicating sound engine is muted or non functioning
+#define CD_NO_SOUND = -1;
+
+#define CD_SAMPLE_RATE_HIGH 44100
+#define CD_SAMPLE_RATE_MID  22050
+#define CD_SAMPLE_RATE_LOW  16000
+#define CD_SAMPLE_RATE_BASIC 8000
+#define CD_SAMPLE_RATE_DEFAULT 44100
+
+extern NSString * const kCDN_BadAlContext;
+extern NSString * const kCDN_AsynchLoadComplete;
+
+extern float const kCD_PitchDefault;
+extern float const kCD_PitchLowerOneOctave;
+extern float const kCD_PitchHigherOneOctave;
+extern float const kCD_PanDefault;
+extern float const kCD_PanFullLeft;
+extern float const kCD_PanFullRight;
+extern float const kCD_GainDefault;
+
+enum bufferState {
+       CD_BS_EMPTY = 0,
+       CD_BS_LOADED = 1,
+       CD_BS_FAILED = 2
+};
+
+typedef struct _sourceGroup {
+       int startIndex;
+       int currentIndex;
+       int totalSources;
+       bool enabled;
+       bool nonInterruptible;
+       int *sourceStatuses;//pointer into array of source status information
+} sourceGroup;
+
+typedef struct _bufferInfo {
+       ALuint bufferId;
+       int bufferState;
+       void* bufferData;
+       ALenum format;
+       ALsizei sizeInBytes;
+       ALsizei frequencyInHertz;
+} bufferInfo;  
+
+typedef struct _sourceInfo {
+       bool usable;
+       ALuint sourceId;
+       ALuint attachedBufferId;
+} sourceInfo;  
+
+#pragma mark CDAudioTransportProtocol
+
+@protocol CDAudioTransportProtocol <NSObject>
+/** Play the audio */
+-(BOOL) play;
+/** Pause the audio, retain resources */
+-(BOOL) pause;
+/** Stop the audio, release resources */
+-(BOOL) stop;
+/** Return playback to beginning */
+-(BOOL) rewind;
+@end
+
+#pragma mark CDAudioInterruptProtocol
+
+@protocol CDAudioInterruptProtocol <NSObject>
+/** Is audio mute */
+-(BOOL) mute;
+/** If YES then audio is silenced but not stopped, calls to start new audio will proceed but silently */
+-(void) setMute:(BOOL) muteValue;
+/** Is audio enabled */
+-(BOOL) enabled;
+/** If NO then all audio is stopped and any calls to start new audio will be ignored */
+-(void) setEnabled:(BOOL) enabledValue;
+@end
+
+#pragma mark CDUtilities
+/**
+ Collection of utilities required by CocosDenshion
+ */
+@interface CDUtilities : NSObject
+{
+}      
+
+/** Fundamentally the same as the corresponding method is CCFileUtils but added to break binding to cocos2d */
++(NSString*) fullPathFromRelativePath:(NSString*) relPath;
+
+@end
+
+
+#pragma mark CDSoundEngine
+
+/** CDSoundEngine is built upon OpenAL and works with SDK 2.0.
+ CDSoundEngine is a sound engine built upon OpenAL and derived from Apple's oalTouch 
+ example. It can playback up to 32 sounds simultaneously with control over pitch, pan
+ and gain.  It can be set up to handle audio session interruption automatically.  You 
+ may decide to use CDSoundEngine directly instead of CDAudioManager or SimpleAudioEngine
+ because you require OS 2.0 compatibility.
+ Requirements:
+ - Firmware: OS 2.0 or greater 
+ - Files: CocosDenshion.*
+ - Frameworks: OpenAL, AudioToolbox
+ @since v0.8
+ */
+@class CDSoundSource;
+@interface CDSoundEngine : NSObject <CDAudioInterruptProtocol> {
+       
+       bufferInfo              *_buffers;
+       sourceInfo              *_sources;
+       sourceGroup         *_sourceGroups;
+       ALCcontext              *context;
+       NSUInteger              _sourceGroupTotal;
+       UInt32                  _audioSessionCategory;
+       BOOL                    _handleAudioSession;
+       ALfloat                 _preMuteGain;
+       NSObject        *_mutexBufferLoad;
+       BOOL                    mute_;
+       BOOL                    enabled_;
+
+       ALenum                  lastErrorCode_;
+       BOOL                    functioning_;
+       float                   asynchLoadProgress_;
+       BOOL                    getGainWorks_;
+       
+       //For managing dynamic allocation of sources and buffers
+       int sourceTotal_;
+       int bufferTotal;
+        
+}
+
+@property (readwrite, nonatomic) ALfloat masterGain;
+@property (readonly)  ALenum lastErrorCode;//Last OpenAL error code that was generated
+@property (readonly)  BOOL functioning;//Is the sound engine functioning
+@property (readwrite) float asynchLoadProgress;
+@property (readonly)  BOOL getGainWorks;//Does getting the gain for a source work
+/** Total number of sources available */
+@property (readonly) int sourceTotal;
+/** Total number of source groups that have been defined */
+@property (readonly) NSUInteger sourceGroupTotal;
+
+/** Sets the sample rate for the audio mixer. For best performance this should match the sample rate of your audio content */
++(void) setMixerSampleRate:(Float32) sampleRate;
+
+/** Initializes the engine with a group definition and a total number of groups */
+-(id)init;
+
+/** Plays a sound in a channel group with a pitch, pan and gain. The sound could played looped or not */
+-(ALuint) playSound:(int) soundId sourceGroupId:(int)sourceGroupId pitch:(float) pitch pan:(float) pan gain:(float) gain loop:(BOOL) loop;
+
+/** Creates and returns a sound source object for the specified sound within the specified source group.
+ */
+-(CDSoundSource *) soundSourceForSound:(int) soundId sourceGroupId:(int) sourceGroupId;
+
+/** Stops playing a sound */
+- (void) stopSound:(ALuint) sourceId;
+/** Stops playing a source group */
+- (void) stopSourceGroup:(int) sourceGroupId;
+/** Stops all playing sounds */
+-(void) stopAllSounds;
+-(void) defineSourceGroups:(NSArray*) sourceGroupDefinitions;
+-(void) defineSourceGroups:(int[]) sourceGroupDefinitions total:(NSUInteger) total;
+-(void) setSourceGroupNonInterruptible:(int) sourceGroupId isNonInterruptible:(BOOL) isNonInterruptible;
+-(void) setSourceGroupEnabled:(int) sourceGroupId enabled:(BOOL) enabled;
+-(BOOL) sourceGroupEnabled:(int) sourceGroupId;
+-(BOOL) loadBufferFromData:(int) soundId soundData:(ALvoid*) soundData format:(ALenum) format size:(ALsizei) size freq:(ALsizei) freq;
+-(BOOL) loadBuffer:(int) soundId filePath:(NSString*) filePath;
+-(void) loadBuffersAsynchronously:(NSArray *) loadRequests;
+-(BOOL) unloadBuffer:(int) soundId;
+-(ALCcontext *) openALContext;
+
+/** Returns the duration of the buffer in seconds or a negative value if the buffer id is invalid */
+-(float) bufferDurationInSeconds:(int) soundId;
+/** Returns the size of the buffer in bytes or a negative value if the buffer id is invalid */
+-(ALsizei) bufferSizeInBytes:(int) soundId;
+/** Returns the sampling frequency of the buffer in hertz or a negative value if the buffer id is invalid */
+-(ALsizei) bufferFrequencyInHertz:(int) soundId;
+
+/** Used internally, never call unless you know what you are doing */
+-(void) _soundSourcePreRelease:(CDSoundSource *) soundSource;
+
+@end
+
+#pragma mark CDSoundSource
+/** CDSoundSource is a wrapper around an OpenAL sound source.
+ It allows you to manipulate properties such as pitch, gain, pan and looping while the 
+ sound is playing. CDSoundSource is based on the old CDSourceWrapper class but with much
+ added functionality.
+ @since v1.0
+ */
+@interface CDSoundSource : NSObject <CDAudioTransportProtocol, CDAudioInterruptProtocol> {
+       ALenum lastError;
+@public
+       ALuint _sourceId;
+       ALuint _sourceIndex;
+       CDSoundEngine* _engine;
+       int _soundId;
+       float _preMuteGain;
+       BOOL enabled_;
+       BOOL mute_;
+}
+@property (readwrite, nonatomic) float pitch;
+@property (readwrite, nonatomic) float gain;
+@property (readwrite, nonatomic) float pan;
+@property (readwrite, nonatomic) BOOL looping;
+@property (readonly)  BOOL isPlaying;
+@property (readwrite, nonatomic) int soundId;
+/** Returns the duration of the attached buffer in seconds or a negative value if the buffer is invalid */
+@property (readonly) float durationInSeconds;
+
+/** Stores the last error code that occurred. Check against AL_NO_ERROR */
+@property (readonly) ALenum lastError;
+/** Do not init yourself, get an instance from the sourceForSound factory method on CDSoundEngine */
+-(id)init:(ALuint) theSourceId sourceIndex:(int) index soundEngine:(CDSoundEngine*) engine;
+
+@end
+
+#pragma mark CDAudioInterruptTargetGroup
+
+/** Container for objects that implement audio interrupt protocol i.e. they can be muted and enabled.
+ Setting mute and enabled for the group propagates to all children. 
+ Designed to be used with your CDSoundSource objects to get them to comply with global enabled and mute settings
+ if that is what you want to do.*/
+@interface CDAudioInterruptTargetGroup : NSObject <CDAudioInterruptProtocol> {
+       BOOL mute_;
+       BOOL enabled_;
+       NSMutableArray *children_;
+}
+-(void) addAudioInterruptTarget:(NSObject<CDAudioInterruptProtocol>*) interruptibleTarget;
+@end
+
+#pragma mark CDAsynchBufferLoader
+
+/** CDAsynchBufferLoader
+ TODO
+ */
+@interface CDAsynchBufferLoader : NSOperation {
+       NSArray *_loadRequests;
+       CDSoundEngine *_soundEngine;
+}      
+
+-(id) init:(NSArray *)loadRequests soundEngine:(CDSoundEngine *) theSoundEngine;
+
+@end
+
+#pragma mark CDBufferLoadRequest
+
+/** CDBufferLoadRequest */
+@interface CDBufferLoadRequest: NSObject
+{
+       NSString *filePath;
+       int              soundId;
+       //id       loader;
+}
+
+@property (readonly) NSString *filePath;
+@property (readonly) int soundId;
+
+- (id)init:(int) theSoundId filePath:(const NSString *) theFilePath;
+@end
+
+/** Interpolation type */
+typedef enum {
+       kIT_Linear,                     //!Straight linear interpolation fade
+       kIT_SCurve,                     //!S curved interpolation
+       kIT_Exponential         //!Exponential interpolation
+} tCDInterpolationType;
+
+#pragma mark CDFloatInterpolator
+@interface CDFloatInterpolator: NSObject
+{
+       float start;
+       float end;
+       float lastValue;
+       tCDInterpolationType interpolationType;
+}
+@property (readwrite, nonatomic) float start;
+@property (readwrite, nonatomic) float end;
+@property (readwrite, nonatomic) tCDInterpolationType interpolationType;
+
+/** Return a value between min and max based on t which represents fractional progress where 0 is the start
+ and 1 is the end */
+-(float) interpolate:(float) t;
+-(id) init:(tCDInterpolationType) type startVal:(float) startVal endVal:(float) endVal;
+
+@end
+
+#pragma mark CDPropertyModifier
+
+/** Base class for classes that modify properties such as pitch, pan and gain */
+@interface CDPropertyModifier: NSObject
+{
+       CDFloatInterpolator *interpolator;
+       float startValue;
+       float endValue;
+       id target;
+       BOOL stopTargetWhenComplete;
+       
+}
+@property (readwrite, nonatomic) BOOL stopTargetWhenComplete;
+@property (readwrite, nonatomic) float startValue;
+@property (readwrite, nonatomic) float endValue;
+@property (readwrite, nonatomic) tCDInterpolationType interpolationType;
+
+-(id) init:(id) theTarget interpolationType:(tCDInterpolationType) type startVal:(float) startVal endVal:(float) endVal;
+/** Set to a fractional value between 0 and 1 where 0 equals the start and 1 equals the end*/
+-(void) modify:(float) t;
+
+-(void) _setTargetProperty:(float) newVal;
+-(float) _getTargetProperty;
+-(void) _stopTarget;
+-(Class) _allowableType;
+
+@end
+
+#pragma mark CDSoundSourceFader
+
+/** Fader for CDSoundSource objects */
+@interface CDSoundSourceFader : CDPropertyModifier{}
+@end
+
+#pragma mark CDSoundSourcePanner
+
+/** Panner for CDSoundSource objects */
+@interface CDSoundSourcePanner : CDPropertyModifier{}
+@end
+
+#pragma mark CDSoundSourcePitchBender
+
+/** Pitch bender for CDSoundSource objects */
+@interface CDSoundSourcePitchBender : CDPropertyModifier{}
+@end
+
+#pragma mark CDSoundEngineFader
+
+/** Fader for CDSoundEngine objects */
+@interface CDSoundEngineFader : CDPropertyModifier{}
+@end
+
+
+
+
diff --git a/libs/CocosDenshion/CocosDenshion.m b/libs/CocosDenshion/CocosDenshion.m
new file mode 100644 (file)
index 0000000..8d94116
--- /dev/null
@@ -0,0 +1,1598 @@
+/*
+ Copyright (c) 2010 Steve Oldmeadow
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ THE SOFTWARE.
+ $Id$
+ */
+
+#import "CocosDenshion.h"
+
+typedef ALvoid AL_APIENTRY     (*alBufferDataStaticProcPtr) (const ALint bid, ALenum format, ALvoid* data, ALsizei size, ALsizei freq);
+ALvoid  alBufferDataStaticProc(const ALint bid, ALenum format, ALvoid* data, ALsizei size, ALsizei freq)
+{
+       static  alBufferDataStaticProcPtr       proc = NULL;
+    
+    if (proc == NULL) {
+        proc = (alBufferDataStaticProcPtr) alcGetProcAddress(NULL, (const ALCchar*) "alBufferDataStatic");
+    }
+    
+    if (proc)
+        proc(bid, format, data, size, freq);
+       
+    return;
+}
+
+typedef ALvoid AL_APIENTRY     (*alcMacOSXMixerOutputRateProcPtr) (const ALdouble value);
+ALvoid  alcMacOSXMixerOutputRateProc(const ALdouble value)
+{
+       static  alcMacOSXMixerOutputRateProcPtr proc = NULL;
+    
+    if (proc == NULL) {
+        proc = (alcMacOSXMixerOutputRateProcPtr) alcGetProcAddress(NULL, (const ALCchar*) "alcMacOSXMixerOutputRate");
+    }
+    
+    if (proc)
+        proc(value);
+       
+    return;
+}
+
+NSString * const kCDN_BadAlContext = @"kCDN_BadAlContext";
+NSString * const kCDN_AsynchLoadComplete = @"kCDN_AsynchLoadComplete";
+float const kCD_PitchDefault = 1.0f;
+float const kCD_PitchLowerOneOctave = 0.5f;
+float const kCD_PitchHigherOneOctave = 2.0f;
+float const kCD_PanDefault = 0.0f;
+float const kCD_PanFullLeft = -1.0f;
+float const kCD_PanFullRight = 1.0f;
+float const kCD_GainDefault = 1.0f;
+
+@interface CDSoundEngine (PrivateMethods)
+-(BOOL) _initOpenAL;
+-(void) _testGetGain;
+-(void) _dumpSourceGroupsInfo;
+-(void) _getSourceIndexForSourceGroup;
+-(void) _freeSourceGroups;
+-(BOOL) _setUpSourceGroups:(int[]) definitions total:(NSUInteger) total; 
+@end
+
+#pragma mark -
+#pragma mark CDUtilities
+
+@implementation CDUtilities
+
++(NSString*) fullPathFromRelativePath:(NSString*) relPath
+{
+       // do not convert an absolute path (starting with '/')
+       if(([relPath length] > 0) && ([relPath characterAtIndex:0] == '/'))
+       {
+               return relPath;
+       }
+       
+       NSMutableArray *imagePathComponents = [NSMutableArray arrayWithArray:[relPath pathComponents]];
+       NSString *file = [imagePathComponents lastObject];
+       
+       [imagePathComponents removeLastObject];
+       NSString *imageDirectory = [NSString pathWithComponents:imagePathComponents];
+       
+       NSString *fullpath = [[NSBundle mainBundle] pathForResource:file ofType:nil inDirectory:imageDirectory];
+       if (fullpath == nil)
+               fullpath = relPath;
+       
+       return fullpath;        
+}
+
+@end
+
+#pragma mark -
+#pragma mark CDSoundEngine
+
+@implementation CDSoundEngine
+
+static Float32 _mixerSampleRate;
+static BOOL _mixerRateSet = NO;
+
+@synthesize lastErrorCode = lastErrorCode_;
+@synthesize functioning = functioning_;
+@synthesize asynchLoadProgress = asynchLoadProgress_;
+@synthesize getGainWorks = getGainWorks_;
+@synthesize sourceTotal = sourceTotal_;
+
++ (void) setMixerSampleRate:(Float32) sampleRate {
+       _mixerRateSet = YES;
+       _mixerSampleRate = sampleRate;
+}      
+
+- (void) _testGetGain {
+       float testValue = 0.7f;
+       ALuint testSourceId = _sources[0].sourceId;
+       alSourcef(testSourceId, AL_GAIN, 0.0f);//Start from know value
+       alSourcef(testSourceId, AL_GAIN, testValue);
+       ALfloat gainVal;
+       alGetSourcef(testSourceId, AL_GAIN, &gainVal);
+       getGainWorks_ = (gainVal == testValue);
+}
+
+//Generate sources one at a time until we fail
+-(void) _generateSources {
+       
+       _sources = (sourceInfo*)malloc( sizeof(_sources[0]) * CD_SOURCE_LIMIT);
+       BOOL hasFailed = NO;
+       sourceTotal_ = 0;
+       alGetError();//Clear error
+       while (!hasFailed && sourceTotal_ < CD_SOURCE_LIMIT) {
+               alGenSources(1, &(_sources[sourceTotal_].sourceId));
+               if (alGetError() == AL_NO_ERROR) {
+                       //Now try attaching source to null buffer
+                       alSourcei(_sources[sourceTotal_].sourceId, AL_BUFFER, 0);
+                       if (alGetError() == AL_NO_ERROR) {
+                               _sources[sourceTotal_].usable = true;   
+                               sourceTotal_++;
+                       } else {
+                               hasFailed = YES;
+                       }       
+               } else {
+                       _sources[sourceTotal_].usable = false;
+                       hasFailed = YES;
+               }       
+       }
+       //Mark the rest of the sources as not usable
+       for (int i=sourceTotal_; i < CD_SOURCE_LIMIT; i++) {
+               _sources[i].usable = false;
+       }       
+}      
+
+-(void) _generateBuffers:(int) startIndex endIndex:(int) endIndex {
+       if (_buffers) {
+               alGetError();
+               for (int i=startIndex; i <= endIndex; i++) {
+                       alGenBuffers(1, &_buffers[i].bufferId);
+                       _buffers[i].bufferData = NULL;
+                       if (alGetError() == AL_NO_ERROR) {
+                               _buffers[i].bufferState = CD_BS_EMPTY;
+                       } else {
+                               _buffers[i].bufferState = CD_BS_FAILED;
+                               CDLOG(@"Denshion::CDSoundEngine - buffer creation failed %i",i);
+                       }       
+               }
+       }       
+}
+
+/**
+ * Internal method called during init
+ */
+- (BOOL) _initOpenAL
+{
+       //ALenum                        error;
+       context = NULL;
+       ALCdevice               *newDevice = NULL;
+
+       //Set the mixer rate for the audio mixer
+       if (!_mixerRateSet) {
+               _mixerSampleRate = CD_SAMPLE_RATE_DEFAULT;
+       }
+       alcMacOSXMixerOutputRateProc(_mixerSampleRate);
+       CDLOGINFO(@"Denshion::CDSoundEngine - mixer output rate set to %0.2f",_mixerSampleRate);
+       
+       // Create a new OpenAL Device
+       // Pass NULL to specify the system's default output device
+       newDevice = alcOpenDevice(NULL);
+       if (newDevice != NULL)
+       {
+               // Create a new OpenAL Context
+               // The new context will render to the OpenAL Device just created 
+               context = alcCreateContext(newDevice, 0);
+               if (context != NULL)
+               {
+                       // Make the new context the Current OpenAL Context
+                       alcMakeContextCurrent(context);
+                       
+                       // Create some OpenAL Buffer Objects
+                       [self _generateBuffers:0 endIndex:bufferTotal-1];
+                       
+                       // Create some OpenAL Source Objects
+                       [self _generateSources];
+                       
+               }
+       } else {
+               return FALSE;//No device
+       }       
+       alGetError();//Clear error
+       return TRUE;
+}
+
+- (void) dealloc {
+       
+       ALCcontext      *currentContext = NULL;
+    ALCdevice  *device = NULL;
+       
+       [self stopAllSounds];
+
+       CDLOGINFO(@"Denshion::CDSoundEngine - Deallocing sound engine.");
+       [self _freeSourceGroups];
+       
+       // Delete the Sources
+       CDLOGINFO(@"Denshion::CDSoundEngine - deleting sources.");
+       for (int i=0; i < sourceTotal_; i++) {
+               alSourcei(_sources[i].sourceId, AL_BUFFER, 0);//Detach from current buffer
+           alDeleteSources(1, &(_sources[i].sourceId));
+               if((lastErrorCode_ = alGetError()) != AL_NO_ERROR) {
+                       CDLOG(@"Denshion::CDSoundEngine - Error deleting source! %x\n", lastErrorCode_);
+               } 
+       }       
+
+       // Delete the Buffers
+       CDLOGINFO(@"Denshion::CDSoundEngine - deleting buffers.");
+       for (int i=0; i < bufferTotal; i++) {
+               alDeleteBuffers(1, &_buffers[i].bufferId);
+#ifdef CD_USE_STATIC_BUFFERS
+               if (_buffers[i].bufferData) {
+                       free(_buffers[i].bufferData);
+               }       
+#endif         
+       }       
+       CDLOGINFO(@"Denshion::CDSoundEngine - free buffers.");
+       free(_buffers);
+    currentContext = alcGetCurrentContext();
+    //Get device for active context
+    device = alcGetContextsDevice(currentContext);
+    //Release context
+       CDLOGINFO(@"Denshion::CDSoundEngine - destroy context.");
+    alcDestroyContext(currentContext);
+    //Close device
+       CDLOGINFO(@"Denshion::CDSoundEngine - close device.");
+    alcCloseDevice(device);
+       CDLOGINFO(@"Denshion::CDSoundEngine - free sources.");
+       free(_sources);
+       
+       //Release mutexes
+       [_mutexBufferLoad release];
+       
+       [super dealloc];
+}      
+
+-(NSUInteger) sourceGroupTotal {
+       return _sourceGroupTotal;
+}      
+
+-(void) _freeSourceGroups 
+{
+       CDLOGINFO(@"Denshion::CDSoundEngine freeing source groups");
+       if(_sourceGroups) {
+               for (int i=0; i < _sourceGroupTotal; i++) {
+                       if (_sourceGroups[i].sourceStatuses) {
+                               free(_sourceGroups[i].sourceStatuses);
+                               CDLOGINFO(@"Denshion::CDSoundEngine freed source statuses %i",i);
+                       }       
+               }
+               free(_sourceGroups);
+       }       
+}      
+
+-(BOOL) _redefineSourceGroups:(int[]) definitions total:(NSUInteger) total
+{
+       if (_sourceGroups) {
+               //Stop all sounds
+               [self stopAllSounds];
+               //Need to free source groups
+               [self _freeSourceGroups];
+       }
+       return [self _setUpSourceGroups:definitions total:total];
+}      
+
+-(BOOL) _setUpSourceGroups:(int[]) definitions total:(NSUInteger) total 
+{
+       _sourceGroups = (sourceGroup *)malloc( sizeof(_sourceGroups[0]) * total);
+       if(!_sourceGroups) {
+               CDLOG(@"Denshion::CDSoundEngine - source groups memory allocation failed");
+               return NO;
+       }
+       
+       _sourceGroupTotal = total;
+       int sourceCount = 0;
+       for (int i=0; i < _sourceGroupTotal; i++) {
+               
+               _sourceGroups[i].startIndex = 0;
+               _sourceGroups[i].currentIndex = _sourceGroups[i].startIndex;
+               _sourceGroups[i].enabled = false;
+               _sourceGroups[i].nonInterruptible = false;
+               _sourceGroups[i].totalSources = definitions[i];
+               _sourceGroups[i].sourceStatuses = malloc(sizeof(_sourceGroups[i].sourceStatuses[0]) * _sourceGroups[i].totalSources);
+               if (_sourceGroups[i].sourceStatuses) {
+                       for (int j=0; j < _sourceGroups[i].totalSources; j++) {
+                               //First bit is used to indicate whether source is locked, index is shifted back 1 bit
+                               _sourceGroups[i].sourceStatuses[j] = (sourceCount + j) << 1;    
+                       }       
+               }       
+               sourceCount += definitions[i];
+       }
+       return YES;
+}
+
+-(void) defineSourceGroups:(int[]) sourceGroupDefinitions total:(NSUInteger) total {
+       [self _redefineSourceGroups:sourceGroupDefinitions total:total];
+}
+
+-(void) defineSourceGroups:(NSArray*) sourceGroupDefinitions {
+       CDLOGINFO(@"Denshion::CDSoundEngine - source groups defined by NSArray.");
+       NSUInteger totalDefs = [sourceGroupDefinitions count];
+       int* defs = (int *)malloc( sizeof(int) * totalDefs);
+       int currentIndex = 0;
+       for (id currentDef in sourceGroupDefinitions) {
+               if ([currentDef isKindOfClass:[NSNumber class]]) {
+                       defs[currentIndex] = (int)[(NSNumber*)currentDef integerValue];
+                       CDLOGINFO(@"Denshion::CDSoundEngine - found definition %i.",defs[currentIndex]);
+               } else {
+                       CDLOG(@"Denshion::CDSoundEngine - warning, did not understand source definition.");
+                       defs[currentIndex] = 0;
+               }       
+               currentIndex++;
+       }
+       [self _redefineSourceGroups:defs total:totalDefs];
+       free(defs);
+}      
+
+- (id)init
+{      
+       if ((self = [super init])) {
+               
+               //Create mutexes
+               _mutexBufferLoad = [[NSObject alloc] init];
+               
+               asynchLoadProgress_ = 0.0f;
+               
+               bufferTotal = CD_BUFFERS_START;
+               _buffers = (bufferInfo *)malloc( sizeof(_buffers[0]) * bufferTotal);
+       
+               // Initialize our OpenAL environment
+               if ([self _initOpenAL]) {
+                       //Set up the default source group - a single group that contains all the sources
+                       int sourceDefs[1];
+                       sourceDefs[0] = self.sourceTotal;
+                       [self _setUpSourceGroups:sourceDefs total:1];
+
+                       functioning_ = YES;
+                       //Synchronize premute gain
+                       _preMuteGain = self.masterGain;
+                       mute_ = NO;
+                       enabled_ = YES;
+                       //Test whether get gain works for sources
+                       [self _testGetGain];
+               } else {
+                       //Something went wrong with OpenAL
+                       functioning_ = NO;
+               }
+       }
+       
+       return self;
+}
+
+/**
+ * Delete the buffer identified by soundId
+ * @return true if buffer deleted successfully, otherwise false
+ */
+- (BOOL) unloadBuffer:(int) soundId 
+{
+       //Ensure soundId is within array bounds otherwise memory corruption will occur
+       if (soundId < 0 || soundId >= bufferTotal) {
+               CDLOG(@"Denshion::CDSoundEngine - soundId is outside array bounds, maybe you need to increase CD_MAX_BUFFERS");
+               return FALSE;
+       }       
+       
+       //Before a buffer can be deleted any sources that are attached to it must be stopped
+       for (int i=0; i < sourceTotal_; i++) {
+               //Note: tried getting the AL_BUFFER attribute of the source instead but doesn't
+               //appear to work on a device - just returned zero.
+               if (_buffers[soundId].bufferId == _sources[i].attachedBufferId) {
+                       
+                       CDLOG(@"Denshion::CDSoundEngine - Found attached source %i %i %i",i,_buffers[soundId].bufferId,_sources[i].sourceId);
+#ifdef CD_USE_STATIC_BUFFERS
+                       //When using static buffers a crash may occur if a source is playing with a buffer that is about
+                       //to be deleted even though we stop the source and successfully delete the buffer. Crash is confirmed
+                       //on 2.2.1 and 3.1.2, however, it will only occur if a source is used rapidly after having its prior
+                       //data deleted. To avoid any possibility of the crash we wait for the source to finish playing.
+                       ALint state;
+                       
+                       alGetSourcei(_sources[i].sourceId, AL_SOURCE_STATE, &state);
+                       
+                       if (state == AL_PLAYING) {
+                               CDLOG(@"Denshion::CDSoundEngine - waiting for source to complete playing before removing buffer data"); 
+                               alSourcei(_sources[i].sourceId, AL_LOOPING, FALSE);//Turn off looping otherwise loops will never end
+                               while (state == AL_PLAYING) {
+                                       alGetSourcei(_sources[i].sourceId, AL_SOURCE_STATE, &state);
+                                       usleep(10000);
+                               }
+                       }
+#endif                 
+                       //Stop source and detach
+                       alSourceStop(_sources[i].sourceId);     
+                       if((lastErrorCode_ = alGetError()) != AL_NO_ERROR) {
+                               CDLOG(@"Denshion::CDSoundEngine - error stopping source: %x\n", lastErrorCode_);
+                       }       
+                       
+                       alSourcei(_sources[i].sourceId, AL_BUFFER, 0);//Attach to "NULL" buffer to detach
+                       if((lastErrorCode_ = alGetError()) != AL_NO_ERROR) {
+                               CDLOG(@"Denshion::CDSoundEngine - error detaching buffer: %x\n", lastErrorCode_);
+                       } else {
+                               //Record that source is now attached to nothing
+                               _sources[i].attachedBufferId = 0;
+                       }       
+               }       
+       }       
+       
+       alDeleteBuffers(1, &_buffers[soundId].bufferId);
+       if((lastErrorCode_ = alGetError()) != AL_NO_ERROR) {
+               CDLOG(@"Denshion::CDSoundEngine - error deleting buffer: %x\n", lastErrorCode_);
+               _buffers[soundId].bufferState = CD_BS_FAILED;
+               return FALSE;
+       } else {
+#ifdef CD_USE_STATIC_BUFFERS
+               //Free previous data, if alDeleteBuffer has returned without error then no 
+               if (_buffers[soundId].bufferData) {
+                       CDLOGINFO(@"Denshion::CDSoundEngine - freeing static data for soundId %i @ %i",soundId,_buffers[soundId].bufferData);
+                       free(_buffers[soundId].bufferData);//Free the old data
+                       _buffers[soundId].bufferData = NULL;
+               }
+#endif         
+       }       
+       
+       alGenBuffers(1, &_buffers[soundId].bufferId);
+       if((lastErrorCode_ = alGetError()) != AL_NO_ERROR) {
+               CDLOG(@"Denshion::CDSoundEngine - error regenerating buffer: %x\n", lastErrorCode_);
+               _buffers[soundId].bufferState = CD_BS_FAILED;
+               return FALSE;
+       } else {
+               //We now have an empty buffer
+               _buffers[soundId].bufferState = CD_BS_EMPTY;
+               CDLOGINFO(@"Denshion::CDSoundEngine - buffer %i successfully unloaded\n",soundId);
+               return TRUE;
+       }       
+}      
+
+/**
+ * Load buffers asynchronously 
+ * Check asynchLoadProgress for progress. asynchLoadProgress represents fraction of completion. When it equals 1.0 loading
+ * is complete. NB: asynchLoadProgress is simply based on the number of load requests, it does not take into account
+ * file sizes.
+ * @param An array of CDBufferLoadRequest objects
+ */
+- (void) loadBuffersAsynchronously:(NSArray *) loadRequests {
+       @synchronized(self) {
+               asynchLoadProgress_ = 0.0f;
+               CDAsynchBufferLoader *loaderOp = [[[CDAsynchBufferLoader alloc] init:loadRequests soundEngine:self] autorelease];
+               NSOperationQueue *opQ = [[[NSOperationQueue alloc] init] autorelease];
+               [opQ addOperation:loaderOp];
+       }
+}      
+
+-(BOOL) _resizeBuffers:(int) increment {
+       
+       void * tmpBufferInfos = realloc( _buffers, sizeof(_buffers[0]) * (bufferTotal + increment) );
+       
+       if(!tmpBufferInfos) {
+               free(tmpBufferInfos);
+               return NO;
+       } else {
+               _buffers = tmpBufferInfos;
+               int oldBufferTotal = bufferTotal;
+               bufferTotal = bufferTotal + increment;
+               [self _generateBuffers:oldBufferTotal endIndex:bufferTotal-1];
+               return YES;
+       }       
+}      
+
+-(BOOL) loadBufferFromData:(int) soundId soundData:(ALvoid*) soundData format:(ALenum) format size:(ALsizei) size freq:(ALsizei) freq {
+
+       @synchronized(_mutexBufferLoad) {
+               
+               if (!functioning_) {
+                       //OpenAL initialisation has previously failed
+                       CDLOG(@"Denshion::CDSoundEngine - Loading buffer failed because sound engine state != functioning");
+                       return FALSE;
+               }
+               
+               //Ensure soundId is within array bounds otherwise memory corruption will occur
+               if (soundId < 0) {
+                       CDLOG(@"Denshion::CDSoundEngine - soundId is negative");
+                       return FALSE;
+               }
+               
+               if (soundId >= bufferTotal) {
+                       //Need to resize the buffers
+                       int requiredIncrement = CD_BUFFERS_INCREMENT;
+                       while (bufferTotal + requiredIncrement < soundId) {
+                               requiredIncrement += CD_BUFFERS_INCREMENT;
+                       }
+                       CDLOGINFO(@"Denshion::CDSoundEngine - attempting to resize buffers by %i for sound %i",requiredIncrement,soundId);
+                       if (![self _resizeBuffers:requiredIncrement]) {
+                               CDLOG(@"Denshion::CDSoundEngine - buffer resize failed");
+                               return FALSE;
+                       }       
+               }       
+               
+               if (soundData)
+               {
+                       if (_buffers[soundId].bufferState != CD_BS_EMPTY) {
+                               CDLOGINFO(@"Denshion::CDSoundEngine - non empty buffer, regenerating");
+                               if (![self unloadBuffer:soundId]) {
+                                       //Deletion of buffer failed, delete buffer routine has set buffer state and lastErrorCode
+                                       return NO;
+                               }       
+                       }       
+                       
+#ifdef CD_DEBUG
+                       //Check that sample rate matches mixer rate and warn if they do not
+                       if (freq != (int)_mixerSampleRate) {
+                               CDLOGINFO(@"Denshion::CDSoundEngine - WARNING sample rate does not match mixer sample rate performance may not be optimal.");
+                       }       
+#endif         
+                       
+#ifdef CD_USE_STATIC_BUFFERS
+                       alBufferDataStaticProc(_buffers[soundId].bufferId, format, soundData, size, freq);
+                       _buffers[soundId].bufferData = data;//Save the pointer to the new data
+#else          
+                       alBufferData(_buffers[soundId].bufferId, format, soundData, size, freq);
+#endif
+                       if((lastErrorCode_ = alGetError()) != AL_NO_ERROR) {
+                               CDLOG(@"Denshion::CDSoundEngine -  error attaching audio to buffer: %x", lastErrorCode_);
+                               _buffers[soundId].bufferState = CD_BS_FAILED;
+                               return FALSE;
+                       } 
+               } else {
+                       CDLOG(@"Denshion::CDSoundEngine Buffer data is null!");
+                       _buffers[soundId].bufferState = CD_BS_FAILED;
+                       return FALSE;
+               }       
+               
+               _buffers[soundId].format = format;
+               _buffers[soundId].sizeInBytes = size;
+               _buffers[soundId].frequencyInHertz = freq;
+               _buffers[soundId].bufferState = CD_BS_LOADED;
+               CDLOGINFO(@"Denshion::CDSoundEngine Buffer %i loaded format:%i freq:%i size:%i",soundId,format,freq,size);
+               return TRUE;
+       }//end mutex
+}      
+
+/**
+ * Load sound data for later play back.
+ * @return TRUE if buffer loaded okay for play back otherwise false
+ */
+- (BOOL) loadBuffer:(int) soundId filePath:(NSString*) filePath
+{
+
+       ALvoid* data;
+       ALenum  format;
+       ALsizei size;
+       ALsizei freq;
+       
+       CDLOGINFO(@"Denshion::CDSoundEngine - Loading openAL buffer %i %@", soundId, filePath);
+       
+       CFURLRef fileURL = nil;
+       NSString *path = [CDUtilities fullPathFromRelativePath:filePath];
+       if (path) {
+               fileURL = (CFURLRef)[[NSURL fileURLWithPath:path] retain];
+       }
+
+       if (fileURL)
+       {
+               data = CDGetOpenALAudioData(fileURL, &size, &format, &freq);
+               CFRelease(fileURL);
+               BOOL result = [self loadBufferFromData:soundId soundData:data format:format size:size freq:freq];
+#ifndef CD_USE_STATIC_BUFFERS
+               free(data);//Data can be freed here because alBufferData performs a memcpy              
+#endif
+               return result;
+       } else {
+               CDLOG(@"Denshion::CDSoundEngine Could not find file!\n");
+               //Don't change buffer state here as it will be the same as before method was called     
+               return FALSE;
+       }       
+}
+
+-(BOOL) validateBufferId:(int) soundId {
+       if (soundId < 0 || soundId >= bufferTotal) {
+               CDLOGINFO(@"Denshion::CDSoundEngine - validateBufferId buffer outside range %i",soundId);
+               return NO;
+       } else if (_buffers[soundId].bufferState != CD_BS_LOADED) {
+               CDLOGINFO(@"Denshion::CDSoundEngine - validateBufferId invalide buffer state %i",soundId);
+               return NO;
+       } else {
+               return YES;
+       }       
+}      
+
+-(float) bufferDurationInSeconds:(int) soundId {
+       if ([self validateBufferId:soundId]) {
+               float factor = 0.0f;
+               switch (_buffers[soundId].format) {
+                       case AL_FORMAT_MONO8:
+                               factor = 1.0f;
+                               break;
+                       case AL_FORMAT_MONO16:
+                               factor = 0.5f;
+                               break;
+                       case AL_FORMAT_STEREO8:
+                               factor = 0.5f;
+                               break;
+                       case AL_FORMAT_STEREO16:
+                               factor = 0.25f;
+                               break;
+               }       
+               return (float)_buffers[soundId].sizeInBytes/(float)_buffers[soundId].frequencyInHertz * factor;
+       } else {
+               return -1.0f;
+       }       
+}      
+
+-(ALsizei) bufferSizeInBytes:(int) soundId {
+       if ([self validateBufferId:soundId]) {
+               return _buffers[soundId].sizeInBytes;
+       } else {
+               return -1.0f;
+       }       
+}      
+
+-(ALsizei) bufferFrequencyInHertz:(int) soundId {
+       if ([self validateBufferId:soundId]) {
+               return _buffers[soundId].frequencyInHertz;
+       } else {
+               return -1.0f;
+       }       
+}      
+
+- (ALfloat) masterGain {
+       if (mute_) {
+               //When mute the real gain will always be 0 therefore return the preMuteGain value
+               return _preMuteGain;
+       } else {        
+               ALfloat gain;
+               alGetListenerf(AL_GAIN, &gain);
+               return gain;
+       }       
+}      
+
+/**
+ * Overall gain setting multiplier. e.g 0.5 is half the gain.
+ */
+- (void) setMasterGain:(ALfloat) newGainValue {
+       if (mute_) {
+               _preMuteGain = newGainValue;
+       } else {        
+               alListenerf(AL_GAIN, newGainValue);
+       }       
+}
+
+#pragma mark CDSoundEngine AudioInterrupt protocol
+- (BOOL) mute {
+       return mute_;
+}      
+
+/**
+ * Setting mute silences all sounds but playing sounds continue to advance playback
+ */
+- (void) setMute:(BOOL) newMuteValue {
+       
+       if (newMuteValue == mute_) {
+               return;
+       }
+       
+       mute_ = newMuteValue;
+       if (mute_) {
+               //Remember what the gain was
+               _preMuteGain = self.masterGain;
+               //Set gain to 0 - do not use the property as this will adjust preMuteGain when muted
+               alListenerf(AL_GAIN, 0.0f);
+       } else {
+               //Restore gain to what it was before being muted
+               self.masterGain = _preMuteGain;
+       }       
+}
+
+- (BOOL) enabled {
+       return enabled_;
+}
+
+- (void) setEnabled:(BOOL)enabledValue
+{
+       if (enabled_ == enabledValue) {
+               return;
+       }       
+       enabled_ = enabledValue;
+       if (enabled_ == NO) {
+               [self stopAllSounds];
+       }       
+}      
+
+-(void) _lockSource:(int) sourceIndex lock:(BOOL) lock {
+       BOOL found = NO;
+       for (int i=0; i < _sourceGroupTotal && !found; i++) {
+               if (_sourceGroups[i].sourceStatuses) {
+                       for (int j=0; j < _sourceGroups[i].totalSources && !found; j++) {
+                               //First bit is used to indicate whether source is locked, index is shifted back 1 bit
+                               if((_sourceGroups[i].sourceStatuses[j] >> 1)==sourceIndex) {
+                                       if (lock) {
+                                               //Set first bit to lock this source
+                                               _sourceGroups[i].sourceStatuses[j] |= 1;
+                                       } else {
+                                               //Unset first bit to unlock this source
+                                               _sourceGroups[i].sourceStatuses[j] &= ~1; 
+                                       }       
+                                       found = YES;
+                               }       
+                       }       
+               }       
+       }
+}      
+
+-(int) _getSourceIndexForSourceGroup:(int)sourceGroupId 
+{
+       //Ensure source group id is valid to prevent memory corruption
+       if (sourceGroupId < 0 || sourceGroupId >= _sourceGroupTotal) {
+               CDLOG(@"Denshion::CDSoundEngine invalid source group id %i",sourceGroupId);
+               return CD_NO_SOURCE;
+       }       
+
+       int sourceIndex = -1;//Using -1 to indicate no source found
+       BOOL complete = NO;
+       ALint sourceState = 0;
+       sourceGroup *thisSourceGroup = &_sourceGroups[sourceGroupId];
+       thisSourceGroup->currentIndex = thisSourceGroup->startIndex;
+       while (!complete) {
+               //Iterate over sources looking for one that is not locked, first bit indicates if source is locked
+               if ((thisSourceGroup->sourceStatuses[thisSourceGroup->currentIndex] & 1) == 0) {
+                       //This source is not locked
+                       sourceIndex = thisSourceGroup->sourceStatuses[thisSourceGroup->currentIndex] >> 1;//shift back to get the index
+                       if (thisSourceGroup->nonInterruptible) {
+                               //Check if this source is playing, if so it can't be interrupted
+                               alGetSourcei(_sources[sourceIndex].sourceId, AL_SOURCE_STATE, &sourceState);
+                               if (sourceState != AL_PLAYING) {
+                                       //complete = YES;
+                                       //Set start index so next search starts at the next position
+                                       thisSourceGroup->startIndex = thisSourceGroup->currentIndex + 1;
+                                       break;
+                               } else {
+                                       sourceIndex = -1;//The source index was no good because the source was playing
+                               }       
+                       } else {        
+                               //complete = YES;
+                               //Set start index so next search starts at the next position
+                               thisSourceGroup->startIndex = thisSourceGroup->currentIndex + 1;
+                               break;
+                       }       
+               }
+               thisSourceGroup->currentIndex++;
+               if (thisSourceGroup->currentIndex >= thisSourceGroup->totalSources) {
+                       //Reset to the beginning
+                       thisSourceGroup->currentIndex = 0;      
+               }       
+               if (thisSourceGroup->currentIndex == thisSourceGroup->startIndex) {
+                       //We have looped around and got back to the start
+                       complete = YES;
+               }       
+       }
+
+       //Reset start index to beginning if beyond bounds
+       if (thisSourceGroup->startIndex >= thisSourceGroup->totalSources) {
+               thisSourceGroup->startIndex = 0;        
+       }       
+       
+       if (sourceIndex >= 0) {
+               return sourceIndex;
+       } else {        
+               return CD_NO_SOURCE;
+       }       
+       
+}      
+
+/**
+ * Play a sound.
+ * @param soundId the id of the sound to play (buffer id).
+ * @param SourceGroupId the source group that will be used to play the sound.
+ * @param pitch pitch multiplier. e.g 1.0 is unaltered, 0.5 is 1 octave lower. 
+ * @param pan stereo position. -1 is fully left, 0 is centre and 1 is fully right.
+ * @param gain gain multiplier. e.g. 1.0 is unaltered, 0.5 is half the gain
+ * @param loop should the sound be looped or one shot.
+ * @return the id of the source being used to play the sound or CD_MUTE if the sound engine is muted or non functioning 
+ * or CD_NO_SOURCE if a problem occurs setting up the source
+ * 
+ */
+- (ALuint)playSound:(int) soundId sourceGroupId:(int)sourceGroupId pitch:(float) pitch pan:(float) pan gain:(float) gain loop:(BOOL) loop {
+
+#ifdef CD_DEBUG
+       //Sanity check parameters - only in DEBUG
+       NSAssert(soundId >= 0, @"soundId can not be negative");
+       NSAssert(soundId < bufferTotal, @"soundId exceeds limit");
+       NSAssert(sourceGroupId >= 0, @"sourceGroupId can not be negative");
+       NSAssert(sourceGroupId < _sourceGroupTotal, @"sourceGroupId exceeds limit");
+       NSAssert(pitch > 0, @"pitch must be greater than zero");
+       NSAssert(pan >= -1 && pan <= 1, @"pan must be between -1 and 1");
+       NSAssert(gain >= 0, @"gain can not be negative");
+#endif
+       //If mute or initialisation has failed or buffer is not loaded then do nothing
+       if (!enabled_ || !functioning_ || _buffers[soundId].bufferState != CD_BS_LOADED || _sourceGroups[sourceGroupId].enabled) {
+#ifdef CD_DEBUG
+               if (!functioning_) {
+                       CDLOGINFO(@"Denshion::CDSoundEngine - sound playback aborted because sound engine is not functioning");
+               } else if (_buffers[soundId].bufferState != CD_BS_LOADED) {
+                       CDLOGINFO(@"Denshion::CDSoundEngine - sound playback aborted because buffer %i is not loaded", soundId);
+               }       
+#endif         
+               return CD_MUTE;
+       }       
+
+       int sourceIndex = [self _getSourceIndexForSourceGroup:sourceGroupId];//This method ensures sourceIndex is valid
+       
+       if (sourceIndex != CD_NO_SOURCE) {
+               ALint state;
+               ALuint source = _sources[sourceIndex].sourceId;
+               ALuint buffer = _buffers[soundId].bufferId;
+               alGetError();//Clear the error code     
+               alGetSourcei(source, AL_SOURCE_STATE, &state);
+               if (state == AL_PLAYING) {
+                       alSourceStop(source);
+               }       
+               alSourcei(source, AL_BUFFER, buffer);//Attach to sound
+               alSourcef(source, AL_PITCH, pitch);//Set pitch
+               alSourcei(source, AL_LOOPING, loop);//Set looping
+               alSourcef(source, AL_GAIN, gain);//Set gain/volume
+               float sourcePosAL[] = {pan, 0.0f, 0.0f};//Set position - just using left and right panning
+               alSourcefv(source, AL_POSITION, sourcePosAL);
+               alGetError();//Clear the error code
+               alSourcePlay(source);
+               if((lastErrorCode_ = alGetError()) == AL_NO_ERROR) {
+                       //Everything was okay
+                       _sources[sourceIndex].attachedBufferId = buffer;
+                       return source;
+               } else {
+                       if (alcGetCurrentContext() == NULL) {
+                               CDLOGINFO(@"Denshion::CDSoundEngine - posting bad OpenAL context message");
+                               [[NSNotificationCenter defaultCenter] postNotificationName:kCDN_BadAlContext object:nil];
+                       }                               
+                       return CD_NO_SOURCE;
+               }       
+       } else {        
+               return CD_NO_SOURCE;
+       }       
+}
+
+-(BOOL) _soundSourceAttachToBuffer:(CDSoundSource*) soundSource soundId:(int) soundId  {
+       //Attach the source to the buffer
+       ALint state;
+       ALuint source = soundSource->_sourceId;
+       ALuint buffer = _buffers[soundId].bufferId;
+       alGetSourcei(source, AL_SOURCE_STATE, &state);
+       if (state == AL_PLAYING) {
+               alSourceStop(source);
+       }
+       alGetError();//Clear the error code     
+       alSourcei(source, AL_BUFFER, buffer);//Attach to sound data
+       if((lastErrorCode_ = alGetError()) == AL_NO_ERROR) {
+               _sources[soundSource->_sourceIndex].attachedBufferId = buffer;
+               //_sourceBufferAttachments[soundSource->_sourceIndex] = buffer;//Keep track of which
+               soundSource->_soundId = soundId;
+               return YES;
+       } else {
+               return NO;
+       }       
+}      
+
+/**
+ * Get a sound source for the specified sound in the specified source group
+ */
+-(CDSoundSource *) soundSourceForSound:(int) soundId sourceGroupId:(int) sourceGroupId 
+{
+       if (!functioning_) {
+               return nil;
+       }       
+       //Check if a source is available
+       int sourceIndex = [self _getSourceIndexForSourceGroup:sourceGroupId];
+       if (sourceIndex != CD_NO_SOURCE) { 
+               CDSoundSource *result = [[CDSoundSource alloc] init:_sources[sourceIndex].sourceId sourceIndex:sourceIndex soundEngine:self];
+               [self _lockSource:sourceIndex lock:YES];
+               //Try to attach to the buffer
+               if ([self _soundSourceAttachToBuffer:result soundId:soundId]) {
+                       //Set to a known state
+                       result.pitch = 1.0f;
+                       result.pan = 0.0f;
+                       result.gain = 1.0f;
+                       result.looping = NO;
+                       return [result autorelease];
+               } else {
+                       //Release the sound source we just created, this will also unlock the source
+                       [result release];
+                       return nil;
+               }       
+       } else {
+               //No available source within that source group
+               return nil;
+       }
+}      
+
+-(void) _soundSourcePreRelease:(CDSoundSource *) soundSource {
+       CDLOGINFO(@"Denshion::CDSoundEngine _soundSourcePreRelease %i",soundSource->_sourceIndex);
+       //Unlock the sound source's source
+       [self _lockSource:soundSource->_sourceIndex lock:NO];
+}      
+
+/**
+ * Stop all sounds playing within a source group
+ */
+- (void) stopSourceGroup:(int) sourceGroupId {
+       
+       if (!functioning_ || sourceGroupId >= _sourceGroupTotal || sourceGroupId < 0) {
+               return;
+       }       
+       int sourceCount = _sourceGroups[sourceGroupId].totalSources;
+       for (int i=0; i < sourceCount; i++) {
+               int sourceIndex = _sourceGroups[sourceGroupId].sourceStatuses[i] >> 1;
+               alSourceStop(_sources[sourceIndex].sourceId);
+       }
+       alGetError();//Clear error in case we stopped any sounds that couldn't be stopped
+}      
+
+/**
+ * Stop a sound playing.
+ * @param sourceId an OpenAL source identifier i.e. the return value of playSound
+ */
+- (void)stopSound:(ALuint) sourceId {
+       if (!functioning_) {
+               return;
+       }       
+       alSourceStop(sourceId);
+       alGetError();//Clear error in case we stopped any sounds that couldn't be stopped
+}
+
+- (void) stopAllSounds {
+       for (int i=0; i < sourceTotal_; i++) {
+               alSourceStop(_sources[i].sourceId);
+       }       
+       alGetError();//Clear error in case we stopped any sounds that couldn't be stopped
+}      
+
+/**
+ * Set a source group as non interruptible.  Default is that source groups are interruptible.
+ * Non interruptible means that if a request to play a sound is made for a source group and there are
+ * no free sources available then the play request will be ignored and CD_NO_SOURCE will be returned.
+ */
+- (void) setSourceGroupNonInterruptible:(int) sourceGroupId isNonInterruptible:(BOOL) isNonInterruptible {
+       //Ensure source group id is valid to prevent memory corruption
+       if (sourceGroupId < 0 || sourceGroupId >= _sourceGroupTotal) {
+               CDLOG(@"Denshion::CDSoundEngine setSourceGroupNonInterruptible invalid source group id %i",sourceGroupId);
+               return;
+       }       
+       
+       if (isNonInterruptible) {
+               _sourceGroups[sourceGroupId].nonInterruptible = true;
+       } else {
+               _sourceGroups[sourceGroupId].nonInterruptible = false;
+       }       
+}
+
+/**
+ * Set the mute property for a source group. If mute is turned on any sounds in that source group
+ * will be stopped and further sounds in that source group will play. However, turning mute off
+ * will not restart any sounds that were playing when mute was turned on. Also the mute setting 
+ * for the sound engine must be taken into account. If the sound engine is mute no sounds will play
+ * no matter what the source group mute setting is.
+ */
+- (void) setSourceGroupEnabled:(int) sourceGroupId enabled:(BOOL) enabled {
+       //Ensure source group id is valid to prevent memory corruption
+       if (sourceGroupId < 0 || sourceGroupId >= _sourceGroupTotal) {
+               CDLOG(@"Denshion::CDSoundEngine setSourceGroupEnabled invalid source group id %i",sourceGroupId);
+               return;
+       }       
+       
+       if (enabled) {
+               _sourceGroups[sourceGroupId].enabled = true;
+               [self stopSourceGroup:sourceGroupId];
+       } else {
+               _sourceGroups[sourceGroupId].enabled = false;   
+       }       
+}
+
+/**
+ * Return the mute property for the source group identified by sourceGroupId
+ */
+- (BOOL) sourceGroupEnabled:(int) sourceGroupId {
+       return _sourceGroups[sourceGroupId].enabled;
+}
+
+-(ALCcontext *) openALContext {
+       return context;
+}      
+
+- (void) _dumpSourceGroupsInfo {
+#ifdef CD_DEBUG        
+       CDLOGINFO(@"-------------- source Group Info --------------");
+       for (int i=0; i < _sourceGroupTotal; i++) {
+               CDLOGINFO(@"Group: %i start:%i total:%i",i,_sourceGroups[i].startIndex, _sourceGroups[i].totalSources);
+               CDLOGINFO(@"----- mute:%i nonInterruptible:%i",_sourceGroups[i].enabled, _sourceGroups[i].nonInterruptible);
+               CDLOGINFO(@"----- Source statuses ----");
+               for (int j=0; j < _sourceGroups[i].totalSources; j++) {
+                       CDLOGINFO(@"Source status:%i index=%i locked=%i",j,_sourceGroups[i].sourceStatuses[j] >> 1, _sourceGroups[i].sourceStatuses[j] & 1);
+               }       
+       }       
+#endif 
+}      
+
+@end
+
+///////////////////////////////////////////////////////////////////////////////////////
+@implementation CDSoundSource
+
+@synthesize lastError;
+
+//Macro for handling the al error code
+#define CDSOUNDSOURCE_UPDATE_LAST_ERROR (lastError = alGetError())
+#define CDSOUNDSOURCE_ERROR_HANDLER ( CDSOUNDSOURCE_UPDATE_LAST_ERROR == AL_NO_ERROR)
+
+-(id)init:(ALuint) theSourceId sourceIndex:(int) index soundEngine:(CDSoundEngine*) engine {
+       if ((self = [super init])) {
+               _sourceId = theSourceId;
+               _engine = engine;
+               _sourceIndex = index;
+               enabled_ = YES;
+               mute_ = NO;
+               _preMuteGain = self.gain;
+       } 
+       return self;
+}
+
+-(void) dealloc
+{
+       CDLOGINFO(@"Denshion::CDSoundSource deallocated %i",self->_sourceIndex);
+
+       //Notify sound engine we are about to release
+       [_engine _soundSourcePreRelease:self];
+       [super dealloc];
+}      
+
+- (void) setPitch:(float) newPitchValue {
+       alSourcef(_sourceId, AL_PITCH, newPitchValue);
+       CDSOUNDSOURCE_UPDATE_LAST_ERROR;
+}      
+
+- (void) setGain:(float) newGainValue {
+       if (!mute_) {
+               alSourcef(_sourceId, AL_GAIN, newGainValue);    
+       } else {
+               _preMuteGain = newGainValue;
+       }       
+       CDSOUNDSOURCE_UPDATE_LAST_ERROR;
+}
+
+- (void) setPan:(float) newPanValue {
+       float sourcePosAL[] = {newPanValue, 0.0f, 0.0f};//Set position - just using left and right panning
+       alSourcefv(_sourceId, AL_POSITION, sourcePosAL);
+       CDSOUNDSOURCE_UPDATE_LAST_ERROR;
+
+}
+
+- (void) setLooping:(BOOL) newLoopingValue {
+       alSourcei(_sourceId, AL_LOOPING, newLoopingValue);
+       CDSOUNDSOURCE_UPDATE_LAST_ERROR;
+
+}
+
+- (BOOL) isPlaying {
+       ALint state;
+       alGetSourcei(_sourceId, AL_SOURCE_STATE, &state);
+       CDSOUNDSOURCE_UPDATE_LAST_ERROR;
+       return (state == AL_PLAYING);
+}      
+
+- (float) pitch {
+       ALfloat pitchVal;
+       alGetSourcef(_sourceId, AL_PITCH, &pitchVal);
+       CDSOUNDSOURCE_UPDATE_LAST_ERROR;
+       return pitchVal;
+}
+
+- (float) pan {
+       ALfloat sourcePosAL[] = {0.0f,0.0f,0.0f};
+       alGetSourcefv(_sourceId, AL_POSITION, sourcePosAL);
+       CDSOUNDSOURCE_UPDATE_LAST_ERROR;
+       return sourcePosAL[0];
+}
+
+- (float) gain {
+       if (!mute_) {
+               ALfloat val;
+               alGetSourcef(_sourceId, AL_GAIN, &val);
+               CDSOUNDSOURCE_UPDATE_LAST_ERROR;
+               return val;
+       } else {
+               return _preMuteGain;
+       }       
+}      
+
+- (BOOL) looping {
+       ALfloat val;
+       alGetSourcef(_sourceId, AL_LOOPING, &val);
+       CDSOUNDSOURCE_UPDATE_LAST_ERROR;
+       return val;
+}
+
+-(BOOL) stop {
+       alSourceStop(_sourceId);
+       return CDSOUNDSOURCE_ERROR_HANDLER;
+}      
+
+-(BOOL) play {
+       if (enabled_) {
+               alSourcePlay(_sourceId);
+               CDSOUNDSOURCE_UPDATE_LAST_ERROR;
+               if (lastError != AL_NO_ERROR) {
+                       if (alcGetCurrentContext() == NULL) {
+                               CDLOGINFO(@"Denshion::CDSoundSource - posting bad OpenAL context message");
+                               [[NSNotificationCenter defaultCenter] postNotificationName:kCDN_BadAlContext object:nil];
+                       }       
+                       return NO;
+               } else {
+                       return YES;
+               }       
+       } else {
+               return NO;
+       }
+}      
+
+-(BOOL) pause {
+       alSourcePause(_sourceId);
+       return CDSOUNDSOURCE_ERROR_HANDLER;
+}
+
+-(BOOL) rewind {
+       alSourceRewind(_sourceId);
+       return CDSOUNDSOURCE_ERROR_HANDLER;
+}
+
+-(void) setSoundId:(int) soundId {
+       [_engine _soundSourceAttachToBuffer:self soundId:soundId];
+}
+
+-(int) soundId {
+       return _soundId;
+}      
+
+-(float) durationInSeconds {
+       return [_engine bufferDurationInSeconds:_soundId];
+}      
+
+#pragma mark CDSoundSource AudioInterrupt protocol
+- (BOOL) mute {
+       return mute_;
+}      
+
+/**
+ * Setting mute silences all sounds but playing sounds continue to advance playback
+ */
+- (void) setMute:(BOOL) newMuteValue {
+       
+       if (newMuteValue == mute_) {
+               return;
+       }
+       
+       if (newMuteValue) {
+               //Remember what the gain was
+               _preMuteGain = self.gain;
+               self.gain = 0.0f;
+               mute_ = newMuteValue;//Make sure this is done after setting the gain property as the setter behaves differently depending on mute value
+       } else {
+               //Restore gain to what it was before being muted
+               mute_ = newMuteValue;
+               self.gain = _preMuteGain;
+       }       
+}
+
+- (BOOL) enabled {
+       return enabled_;
+}
+
+- (void) setEnabled:(BOOL)enabledValue
+{
+       if (enabled_ == enabledValue) {
+               return;
+       }       
+       enabled_ = enabledValue;
+       if (enabled_ == NO) {
+               [self stop];
+       }       
+}      
+
+@end
+
+////////////////////////////////////////////////////////////////////////////
+#pragma mark -
+#pragma mark CDAudioInterruptTargetGroup
+
+@implementation CDAudioInterruptTargetGroup
+
+-(id) init {
+       if ((self = [super init])) {
+               children_ = [[NSMutableArray alloc] initWithCapacity:32];
+               enabled_ = YES;
+               mute_ = NO;
+       }
+       return self;
+}      
+
+-(void) addAudioInterruptTarget:(NSObject<CDAudioInterruptProtocol>*) interruptibleTarget {
+       //Synchronize child with group settings;
+       [interruptibleTarget setMute:mute_];
+       [interruptibleTarget setEnabled:enabled_];
+       [children_ addObject:interruptibleTarget];
+}      
+
+-(void) removeAudioInterruptTarget:(NSObject<CDAudioInterruptProtocol>*) interruptibleTarget {
+       [children_ removeObjectIdenticalTo:interruptibleTarget];
+}      
+
+- (BOOL) mute {
+       return mute_;
+}      
+
+/**
+ * Setting mute silences all sounds but playing sounds continue to advance playback
+ */
+- (void) setMute:(BOOL) newMuteValue {
+       
+       if (newMuteValue == mute_) {
+               return;
+       }
+       
+       for (NSObject<CDAudioInterruptProtocol>* target in children_) {
+               [target setMute:newMuteValue];
+       }       
+}
+
+- (BOOL) enabled {
+       return enabled_;
+}
+
+- (void) setEnabled:(BOOL)enabledValue
+{
+       if (enabledValue == enabled_) {
+               return;
+       }
+       
+       for (NSObject<CDAudioInterruptProtocol>* target in children_) {
+               [target setEnabled:enabledValue];
+       }       
+}      
+
+@end
+
+
+
+////////////////////////////////////////////////////////////////////////////
+
+#pragma mark -
+#pragma mark CDAsynchBufferLoader
+
+@implementation CDAsynchBufferLoader
+
+-(id) init:(NSArray *)loadRequests soundEngine:(CDSoundEngine *) theSoundEngine {
+       if ((self = [super init])) {
+               _loadRequests = loadRequests;
+               [_loadRequests retain];
+               _soundEngine = theSoundEngine;
+               [_soundEngine retain];
+       } 
+       return self;
+}      
+
+-(void) main {
+       CDLOGINFO(@"Denshion::CDAsynchBufferLoader - loading buffers");
+       [super main];
+       _soundEngine.asynchLoadProgress = 0.0f;
+
+       if ([_loadRequests count] > 0) {
+               float increment = 1.0f / [_loadRequests count];
+               //Iterate over load request and load
+               for (CDBufferLoadRequest *loadRequest in _loadRequests) {
+                       [_soundEngine loadBuffer:loadRequest.soundId filePath:loadRequest.filePath];
+                       _soundEngine.asynchLoadProgress += increment;
+               }       
+       }       
+       
+       //Completed
+       _soundEngine.asynchLoadProgress = 1.0f;
+       [[NSNotificationCenter defaultCenter] postNotificationName:kCDN_AsynchLoadComplete object:nil];
+       
+}      
+
+-(void) dealloc {
+       [_loadRequests release];
+       [_soundEngine release];
+       [super dealloc];
+}      
+
+@end
+
+
+///////////////////////////////////////////////////////////////////////////////////////
+#pragma mark -
+#pragma mark CDBufferLoadRequest
+
+@implementation CDBufferLoadRequest
+
+@synthesize filePath, soundId;
+
+-(id) init:(int) theSoundId filePath:(const NSString *) theFilePath {
+       if ((self = [super init])) {
+               soundId = theSoundId;
+               filePath = [theFilePath copy];//TODO: is retain necessary or does copy set retain count
+               [filePath retain];
+       } 
+       return self;
+}
+
+-(void) dealloc {
+       [filePath release];
+       [super dealloc];
+}
+
+@end
+
+///////////////////////////////////////////////////////////////////////////////////////
+#pragma mark -
+#pragma mark CDFloatInterpolator
+
+@implementation CDFloatInterpolator
+@synthesize start,end,interpolationType;
+
+-(float) interpolate:(float) t {
+       
+       if (t < 1.0f) {
+               switch (interpolationType) {
+                       case kIT_Linear:
+                               //Linear interpolation
+                               return ((end - start) * t) + start;
+                               
+                       case kIT_SCurve:
+                               //Cubic s curve t^2 * (3 - 2t)
+                               return ((float)(t * t * (3.0 - (2.0 * t))) * (end - start)) + start;
+                               
+                       case kIT_Exponential:   
+                               //Formulas taken from EaseAction
+                               if (end > start) {
+                                       //Fade in
+                                       float logDelta = (t==0) ? 0 : powf(2, 10 * (t/1 - 1)) - 1 * 0.001f;
+                                       return ((end - start) * logDelta) + start;
+                               } else {
+                                       //Fade Out
+                                       float logDelta = (-powf(2, -10 * t/1) + 1);
+                                       return ((end - start) * logDelta) + start;
+                               }
+                       default:
+                               return 0.0f;
+               }
+       } else {
+               return end;
+       } 
+}
+
+-(id) init:(tCDInterpolationType) type startVal:(float) startVal endVal:(float) endVal {
+       if ((self = [super init])) {
+               start = startVal;
+               end = endVal;
+               interpolationType = type;
+       } 
+       return self;
+}
+
+@end
+
+///////////////////////////////////////////////////////////////////////////////////////
+#pragma mark -
+#pragma mark CDPropertyModifier
+
+@implementation CDPropertyModifier
+
+@synthesize stopTargetWhenComplete;
+
+-(id) init:(id) theTarget interpolationType:(tCDInterpolationType) type startVal:(float) startVal endVal:(float) endVal {
+       if ((self = [super init])) {
+               if (target) {
+                       //Release the previous target if there is one
+                       [target release];
+               }       
+               target = theTarget;
+#if CD_DEBUG
+               //Check target is of the required type
+               if (![theTarget isMemberOfClass:[self _allowableType]] ) {
+                       CDLOG(@"Denshion::CDPropertyModifier target is not of type %@",[self _allowableType]);
+                       NSAssert([theTarget isKindOfClass:[CDSoundEngine class]], @"CDPropertyModifier target not of required type");
+               }
+#endif         
+               [target retain];
+               startValue = startVal;
+               endValue = endVal;
+               if (interpolator) {
+                       //Release previous interpolator if there is one
+                       [interpolator release];
+               }       
+               interpolator = [[CDFloatInterpolator alloc] init:type startVal:startVal endVal:endVal];
+               stopTargetWhenComplete = NO;
+       }
+       return self;
+}      
+
+-(void) dealloc {
+       CDLOGINFO(@"Denshion::CDPropertyModifier deallocated %@",self);
+       [target release];
+       [interpolator release];
+       [super dealloc];
+}      
+
+-(void) modify:(float) t {
+       if (t < 1.0) {
+               [self _setTargetProperty:[interpolator interpolate:t]];
+       } else {
+               //At the end
+               [self _setTargetProperty:endValue];
+               if (stopTargetWhenComplete) {
+                       [self _stopTarget];
+               }       
+       }       
+}      
+
+-(float) startValue {
+       return startValue;
+}
+
+-(void) setStartValue:(float) startVal
+{
+       startValue = startVal;
+       interpolator.start = startVal;
+}      
+
+-(float) endValue {
+       return startValue;
+}
+
+-(void) setEndValue:(float) endVal
+{
+       endValue = endVal;
+       interpolator.end = endVal;
+}      
+
+-(tCDInterpolationType) interpolationType {
+       return interpolator.interpolationType;
+}
+
+-(void) setInterpolationType:(tCDInterpolationType) interpolationType {
+       interpolator.interpolationType = interpolationType;
+}      
+
+-(void) _setTargetProperty:(float) newVal {
+
+}      
+
+-(float) _getTargetProperty {
+       return 0.0f;
+}      
+
+-(void) _stopTarget {
+
+}      
+
+-(Class) _allowableType {
+       return [NSObject class];
+}      
+@end
+
+///////////////////////////////////////////////////////////////////////////////////////
+#pragma mark -
+#pragma mark CDSoundSourceFader
+
+@implementation CDSoundSourceFader
+
+-(void) _setTargetProperty:(float) newVal {
+       ((CDSoundSource*)target).gain = newVal;
+}      
+
+-(float) _getTargetProperty {
+       return ((CDSoundSource*)target).gain;
+}
+
+-(void) _stopTarget {
+       [((CDSoundSource*)target) stop];
+}
+
+-(Class) _allowableType {
+       return [CDSoundSource class];
+}      
+
+@end
+
+///////////////////////////////////////////////////////////////////////////////////////
+#pragma mark -
+#pragma mark CDSoundSourcePanner
+
+@implementation CDSoundSourcePanner
+
+-(void) _setTargetProperty:(float) newVal {
+       ((CDSoundSource*)target).pan = newVal;
+}      
+
+-(float) _getTargetProperty {
+       return ((CDSoundSource*)target).pan;
+}
+
+-(void) _stopTarget {
+       [((CDSoundSource*)target) stop];
+}
+
+-(Class) _allowableType {
+       return [CDSoundSource class];
+}      
+
+@end
+
+///////////////////////////////////////////////////////////////////////////////////////
+#pragma mark -
+#pragma mark CDSoundSourcePitchBender
+
+@implementation CDSoundSourcePitchBender
+
+-(void) _setTargetProperty:(float) newVal {
+       ((CDSoundSource*)target).pitch = newVal;
+}      
+
+-(float) _getTargetProperty {
+       return ((CDSoundSource*)target).pitch;
+}
+
+-(void) _stopTarget {
+       [((CDSoundSource*)target) stop];
+}
+
+-(Class) _allowableType {
+       return [CDSoundSource class];
+}      
+
+@end
+
+///////////////////////////////////////////////////////////////////////////////////////
+#pragma mark -
+#pragma mark CDSoundEngineFader
+
+@implementation CDSoundEngineFader
+
+-(void) _setTargetProperty:(float) newVal {
+       ((CDSoundEngine*)target).masterGain = newVal;
+}      
+
+-(float) _getTargetProperty {
+       return ((CDSoundEngine*)target).masterGain;
+}
+
+-(void) _stopTarget {
+       [((CDSoundEngine*)target) stopAllSounds];
+}
+
+-(Class) _allowableType {
+       return [CDSoundEngine class];
+}      
+
+@end
+
+
diff --git a/libs/CocosDenshion/SimpleAudioEngine.h b/libs/CocosDenshion/SimpleAudioEngine.h
new file mode 100644 (file)
index 0000000..35396c6
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+ Copyright (c) 2010 Steve Oldmeadow
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ THE SOFTWARE.
+ $Id$
+ */
+
+
+#import "CDAudioManager.h"
+
+/**
+ A wrapper to the CDAudioManager object.
+ This is recommended for basic audio requirements. If you just want to play some sound fx
+ and some background music and have no interest in learning the lower level workings then
+ this is the interface to use.
+ Requirements:
+ - Firmware: OS 2.2 or greater 
+ - Files: SimpleAudioEngine.*, CocosDenshion.*
+ - Frameworks: OpenAL, AudioToolbox, AVFoundation
+ @since v0.8
+ */
+@interface SimpleAudioEngine : NSObject <CDAudioInterruptProtocol> {
+       
+       BOOL    mute_;
+       BOOL    enabled_;
+}
+
+/** Background music volume. Range is 0.0f to 1.0f. This will only have an effect if willPlayBackgroundMusic returns YES */
+@property (readwrite) float backgroundMusicVolume;
+/** Effects volume. Range is 0.0f to 1.0f */
+@property (readwrite) float effectsVolume;
+/** If NO it indicates background music will not be played either because no background music is loaded or the audio session does not permit it.*/
+@property (readonly) BOOL willPlayBackgroundMusic;
+
+/** returns the shared instance of the SimpleAudioEngine object */
++ (SimpleAudioEngine*) sharedEngine;
+
+/** Preloads a music file so it will be ready to play as background music */
+-(void) preloadBackgroundMusic:(NSString*) filePath;
+
+/** plays background music in a loop*/
+-(void) playBackgroundMusic:(NSString*) filePath;
+/** plays background music, if loop is true the music will repeat otherwise it will be played once */
+-(void) playBackgroundMusic:(NSString*) filePath loop:(BOOL) loop;
+/** stops playing background music */
+-(void) stopBackgroundMusic;
+/** pauses the background music */
+-(void) pauseBackgroundMusic;
+/** resume background music that has been paused */
+-(void) resumeBackgroundMusic;
+/** rewind the background music */
+-(void) rewindBackgroundMusic;
+/** returns whether or not the background music is playing */
+-(BOOL) isBackgroundMusicPlaying;
+
+/** plays an audio effect with a file path*/
+-(ALuint) playEffect:(NSString*) filePath;
+/** stop a sound that is playing, note you must pass in the soundId that is returned when you started playing the sound with playEffect */
+-(void) stopEffect:(ALuint) soundId;
+/** plays an audio effect with a file path, pitch, pan and gain */
+-(ALuint) playEffect:(NSString*) filePath pitch:(Float32) pitch pan:(Float32) pan gain:(Float32) gain;
+/** preloads an audio effect */
+-(void) preloadEffect:(NSString*) filePath;
+/** unloads an audio effect from memory */
+-(void) unloadEffect:(NSString*) filePath;
+/** Gets a CDSoundSource object set up to play the specified file. */
+-(CDSoundSource *) soundSourceForFile:(NSString*) filePath;
+
+/** Shuts down the shared audio engine instance so that it can be reinitialised */
++(void) end;
+
+@end
diff --git a/libs/CocosDenshion/SimpleAudioEngine.m b/libs/CocosDenshion/SimpleAudioEngine.m
new file mode 100644 (file)
index 0000000..cdff26c
--- /dev/null
@@ -0,0 +1,220 @@
+/*
+ Copyright (c) 2010 Steve Oldmeadow
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ THE SOFTWARE.
+ $Id$
+ */
+
+#import "SimpleAudioEngine.h"
+
+@implementation SimpleAudioEngine
+
+static SimpleAudioEngine *sharedEngine = nil;
+static CDSoundEngine* soundEngine = nil;
+static CDAudioManager *am = nil;
+static CDBufferManager *bufferManager = nil;
+
+// Init
++ (SimpleAudioEngine *) sharedEngine
+{
+       @synchronized(self)     {
+               if (!sharedEngine)
+                       sharedEngine = [[SimpleAudioEngine alloc] init];
+       }
+       return sharedEngine;
+}
+
++ (id) alloc
+{
+       @synchronized(self)     {
+               NSAssert(sharedEngine == nil, @"Attempted to allocate a second instance of a singleton.");
+               return [super alloc];
+       }
+       return nil;
+}
+
+-(id) init
+{
+       if((self=[super init])) {
+               am = [CDAudioManager sharedManager];
+               soundEngine = am.soundEngine;
+               bufferManager = [[CDBufferManager alloc] initWithEngine:soundEngine];
+               mute_ = NO;
+               enabled_ = YES;
+       }
+       return self;
+}
+
+// Memory
+- (void) dealloc
+{
+       am = nil;
+       soundEngine = nil;
+       bufferManager = nil;
+       [super dealloc];
+}
+
++(void) end 
+{
+       am = nil;
+       [CDAudioManager end];
+       [bufferManager release];
+       [sharedEngine release];
+       sharedEngine = nil;
+}      
+
+#pragma mark SimpleAudioEngine - background music
+
+-(void) preloadBackgroundMusic:(NSString*) filePath {
+       [am preloadBackgroundMusic:filePath];
+}
+
+-(void) playBackgroundMusic:(NSString*) filePath
+{
+       [am playBackgroundMusic:filePath loop:TRUE];
+}
+
+-(void) playBackgroundMusic:(NSString*) filePath loop:(BOOL) loop
+{
+       [am playBackgroundMusic:filePath loop:loop];
+}
+
+-(void) stopBackgroundMusic
+{
+       [am stopBackgroundMusic];
+}
+
+-(void) pauseBackgroundMusic {
+       [am pauseBackgroundMusic];
+}      
+
+-(void) resumeBackgroundMusic {
+       [am resumeBackgroundMusic];
+}      
+
+-(void) rewindBackgroundMusic {
+       [am rewindBackgroundMusic];
+}
+
+-(BOOL) isBackgroundMusicPlaying {
+       return [am isBackgroundMusicPlaying];
+}      
+
+-(BOOL) willPlayBackgroundMusic {
+       return [am willPlayBackgroundMusic];
+}
+
+#pragma mark SimpleAudioEngine - sound effects
+
+-(ALuint) playEffect:(NSString*) filePath
+{
+       return [self playEffect:filePath pitch:1.0f pan:0.0f gain:1.0f];
+}
+
+-(ALuint) playEffect:(NSString*) filePath pitch:(Float32) pitch pan:(Float32) pan gain:(Float32) gain
+{
+       int soundId = [bufferManager bufferForFile:filePath create:YES];
+       if (soundId != kCDNoBuffer) {
+               return [soundEngine playSound:soundId sourceGroupId:0 pitch:pitch pan:pan gain:gain loop:false];
+       } else {
+               return CD_MUTE;
+       }       
+}
+
+-(void) stopEffect:(ALuint) soundId {
+       [soundEngine stopSound:soundId];
+}      
+
+-(void) preloadEffect:(NSString*) filePath
+{
+       int soundId = [bufferManager bufferForFile:filePath create:YES];
+       if (soundId == kCDNoBuffer) {
+               CDLOG(@"Denshion::SimpleAudioEngine sound failed to preload %@",filePath);
+       }
+}
+
+-(void) unloadEffect:(NSString*) filePath
+{
+       CDLOGINFO(@"Denshion::SimpleAudioEngine unloadedEffect %@",filePath);
+       [bufferManager releaseBufferForFile:filePath];
+}
+
+#pragma mark Audio Interrupt Protocol
+-(BOOL) mute
+{
+       return mute_;
+}
+
+-(void) setMute:(BOOL) muteValue
+{
+       if (mute_ != muteValue) {
+               mute_ = muteValue;
+               am.mute = mute_;
+       }       
+}
+
+-(BOOL) enabled
+{
+       return enabled_;
+}
+
+-(void) setEnabled:(BOOL) enabledValue
+{
+       if (enabled_ != enabledValue) {
+               enabled_ = enabledValue;
+               am.enabled = enabled_;
+       }       
+}
+
+
+#pragma mark SimpleAudioEngine - BackgroundMusicVolume
+-(float) backgroundMusicVolume
+{
+       return am.backgroundMusic.volume;
+}      
+
+-(void) setBackgroundMusicVolume:(float) volume
+{
+       am.backgroundMusic.volume = volume;
+}      
+
+#pragma mark SimpleAudioEngine - EffectsVolume
+-(float) effectsVolume
+{
+       return am.soundEngine.masterGain;
+}      
+
+-(void) setEffectsVolume:(float) volume
+{
+       am.soundEngine.masterGain = volume;
+}      
+
+-(CDSoundSource *) soundSourceForFile:(NSString*) filePath {
+       int soundId = [bufferManager bufferForFile:filePath create:YES];
+       if (soundId != kCDNoBuffer) {
+               CDSoundSource *result = [soundEngine soundSourceForSound:soundId sourceGroupId:0];
+               CDLOGINFO(@"Denshion::SimpleAudioEngine sound source created for %@",filePath);
+               return result;
+       } else {
+               return nil;
+       }       
+}      
+
+@end 
diff --git a/libs/FontLabel/FontLabel.h b/libs/FontLabel/FontLabel.h
new file mode 100644 (file)
index 0000000..6de9c2c
--- /dev/null
@@ -0,0 +1,44 @@
+//
+//  FontLabel.h
+//  FontLabel
+//
+//  Created by Kevin Ballard on 5/8/09.
+//  Copyright © 2009 Zynga Game Networks
+//
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#import <Foundation/Foundation.h>
+#import <UIKit/UIKit.h>
+
+@class ZFont;
+@class ZAttributedString;
+
+@interface FontLabel : UILabel {
+       void *reserved; // works around a bug in UILabel
+       ZFont *zFont;
+       ZAttributedString *zAttributedText;
+}
+@property (nonatomic, setter=setCGFont:) CGFontRef cgFont __AVAILABILITY_INTERNAL_DEPRECATED;
+@property (nonatomic, assign) CGFloat pointSize __AVAILABILITY_INTERNAL_DEPRECATED;
+@property (nonatomic, retain, setter=setZFont:) ZFont *zFont;
+// if attributedText is nil, fall back on using the inherited UILabel properties
+// if attributedText is non-nil, the font/text/textColor
+// in addition, adjustsFontSizeToFitWidth does not work with attributed text
+@property (nonatomic, copy) ZAttributedString *zAttributedText;
+// -initWithFrame:fontName:pointSize: uses FontManager to look up the font name
+- (id)initWithFrame:(CGRect)frame fontName:(NSString *)fontName pointSize:(CGFloat)pointSize;
+- (id)initWithFrame:(CGRect)frame zFont:(ZFont *)font;
+- (id)initWithFrame:(CGRect)frame font:(CGFontRef)font pointSize:(CGFloat)pointSize __AVAILABILITY_INTERNAL_DEPRECATED;
+@end
diff --git a/libs/FontLabel/FontLabel.m b/libs/FontLabel/FontLabel.m
new file mode 100644 (file)
index 0000000..58975b1
--- /dev/null
@@ -0,0 +1,195 @@
+//
+//  FontLabel.m
+//  FontLabel
+//
+//  Created by Kevin Ballard on 5/8/09.
+//  Copyright © 2009 Zynga Game Networks
+//
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#import "FontLabel.h"
+#import "FontManager.h"
+#import "FontLabelStringDrawing.h"
+#import "ZFont.h"
+
+@interface ZFont (ZFontPrivate)
+@property (nonatomic, readonly) CGFloat ratio;
+@end
+
+@implementation FontLabel
+@synthesize zFont;
+@synthesize zAttributedText;
+
+- (id)initWithFrame:(CGRect)frame fontName:(NSString *)fontName pointSize:(CGFloat)pointSize {
+       return [self initWithFrame:frame zFont:[[FontManager sharedManager] zFontWithName:fontName pointSize:pointSize]];
+}
+
+- (id)initWithFrame:(CGRect)frame zFont:(ZFont *)font {
+       if ((self = [super initWithFrame:frame])) {
+               zFont = [font retain];
+       }
+       return self;
+}
+
+- (id)initWithFrame:(CGRect)frame font:(CGFontRef)font pointSize:(CGFloat)pointSize {
+       return [self initWithFrame:frame zFont:[ZFont fontWithCGFont:font size:pointSize]];
+}
+
+- (CGFontRef)cgFont {
+       return self.zFont.cgFont;
+}
+
+- (void)setCGFont:(CGFontRef)font {
+       if (self.zFont.cgFont != font) {
+               self.zFont = [ZFont fontWithCGFont:font size:self.zFont.pointSize];
+       }
+}
+
+- (CGFloat)pointSize {
+       return self.zFont.pointSize;
+}
+
+- (void)setPointSize:(CGFloat)pointSize {
+       if (self.zFont.pointSize != pointSize) {
+               self.zFont = [ZFont fontWithCGFont:self.zFont.cgFont size:pointSize];
+       }
+}
+
+- (void)setZAttributedText:(ZAttributedString *)attStr {
+       if (zAttributedText != attStr) {
+               [zAttributedText release];
+               zAttributedText = [attStr copy];
+               [self setNeedsDisplay];
+       }
+}
+
+- (void)drawTextInRect:(CGRect)rect {
+       if (self.zFont == NULL && self.zAttributedText == nil) {
+               [super drawTextInRect:rect];
+               return;
+       }
+       
+       if (self.zAttributedText == nil) {
+               // this method is documented as setting the text color for us, but that doesn't appear to be the case
+               if (self.highlighted) {
+                       [(self.highlightedTextColor ?: [UIColor whiteColor]) setFill];
+               } else {
+                       [(self.textColor ?: [UIColor blackColor]) setFill];
+               }
+               
+               ZFont *actualFont = self.zFont;
+               CGSize origSize = rect.size;
+               if (self.numberOfLines == 1) {
+                       origSize.height = actualFont.leading;
+                       CGPoint point = CGPointMake(rect.origin.x,
+                                                                               rect.origin.y + roundf(((rect.size.height - actualFont.leading) / 2.0f)));
+                       CGSize size = [self.text sizeWithZFont:actualFont];
+                       if (self.adjustsFontSizeToFitWidth && self.minimumFontSize < actualFont.pointSize) {
+                               if (size.width > origSize.width) {
+                                       CGFloat desiredRatio = (origSize.width * actualFont.ratio) / size.width;
+                                       CGFloat desiredPointSize = desiredRatio * actualFont.pointSize / actualFont.ratio;
+                                       actualFont = [actualFont fontWithSize:MAX(MAX(desiredPointSize, self.minimumFontSize), 1.0f)];
+                                       size = [self.text sizeWithZFont:actualFont];
+                               }
+                               if (!CGSizeEqualToSize(origSize, size)) {
+                                       switch (self.baselineAdjustment) {
+                                               case UIBaselineAdjustmentAlignCenters:
+                                                       point.y += roundf((origSize.height - size.height) / 2.0f);
+                                                       break;
+                                               case UIBaselineAdjustmentAlignBaselines:
+                                                       point.y += (self.zFont.ascender - actualFont.ascender);
+                                                       break;
+                                               case UIBaselineAdjustmentNone:
+                                                       break;
+                                       }
+                               }
+                       }
+                       size.width = MIN(size.width, origSize.width);
+                       // adjust the point for alignment
+                       switch (self.textAlignment) {
+                               case UITextAlignmentLeft:
+                                       break;
+                               case UITextAlignmentCenter:
+                                       point.x += (origSize.width - size.width) / 2.0f;
+                                       break;
+                               case UITextAlignmentRight:
+                                       point.x += origSize.width - size.width;
+                                       break;
+                       }
+                       [self.text drawAtPoint:point forWidth:size.width withZFont:actualFont lineBreakMode:self.lineBreakMode];
+               } else {
+                       CGSize size = [self.text sizeWithZFont:actualFont constrainedToSize:origSize lineBreakMode:self.lineBreakMode numberOfLines:self.numberOfLines];
+                       CGPoint point = rect.origin;
+                       point.y += roundf((rect.size.height - size.height) / 2.0f);
+                       rect = (CGRect){point, CGSizeMake(rect.size.width, size.height)};
+                       [self.text drawInRect:rect withZFont:actualFont lineBreakMode:self.lineBreakMode alignment:self.textAlignment numberOfLines:self.numberOfLines];
+               }
+       } else {
+               ZAttributedString *attStr = self.zAttributedText;
+               if (self.highlighted) {
+                       // modify the string to change the base color
+                       ZMutableAttributedString *mutStr = [[attStr mutableCopy] autorelease];
+                       NSRange activeRange = NSMakeRange(0, attStr.length);
+                       while (activeRange.length > 0) {
+                               NSRange effective;
+                               UIColor *color = [attStr attribute:ZForegroundColorAttributeName atIndex:activeRange.location
+                                                        longestEffectiveRange:&effective inRange:activeRange];
+                               if (color == nil) {
+                                       [mutStr addAttribute:ZForegroundColorAttributeName value:[UIColor whiteColor] range:effective];
+                               }
+                               activeRange.location += effective.length, activeRange.length -= effective.length;
+                       }
+                       attStr = mutStr;
+               }
+               CGSize size = [attStr sizeConstrainedToSize:rect.size lineBreakMode:self.lineBreakMode numberOfLines:self.numberOfLines];
+               CGPoint point = rect.origin;
+               point.y += roundf((rect.size.height - size.height) / 2.0f);
+               rect = (CGRect){point, CGSizeMake(rect.size.width, size.height)};
+               [attStr drawInRect:rect withLineBreakMode:self.lineBreakMode alignment:self.textAlignment numberOfLines:self.numberOfLines];
+       }
+}
+
+- (CGRect)textRectForBounds:(CGRect)bounds limitedToNumberOfLines:(NSInteger)numberOfLines {
+       if (self.zFont == NULL && self.zAttributedText == nil) {
+               return [super textRectForBounds:bounds limitedToNumberOfLines:numberOfLines];
+       }
+       
+       if (numberOfLines == 1) {
+               // if numberOfLines == 1 we need to use the version that converts spaces
+               CGSize size;
+               if (self.zAttributedText == nil) {
+                       size = [self.text sizeWithZFont:self.zFont];
+               } else {
+                       size = [self.zAttributedText size];
+               }
+               bounds.size.width = MIN(bounds.size.width, size.width);
+               bounds.size.height = MIN(bounds.size.height, size.height);
+       } else {
+               if (numberOfLines > 0) bounds.size.height = MIN(bounds.size.height, self.zFont.leading * numberOfLines);
+               if (self.zAttributedText == nil) {
+                       bounds.size = [self.text sizeWithZFont:self.zFont constrainedToSize:bounds.size lineBreakMode:self.lineBreakMode];
+               } else {
+                       bounds.size = [self.zAttributedText sizeConstrainedToSize:bounds.size lineBreakMode:self.lineBreakMode];
+               }
+       }
+       return bounds;
+}
+
+- (void)dealloc {
+       [zFont release];
+       [zAttributedText release];
+       [super dealloc];
+}
+@end
diff --git a/libs/FontLabel/FontLabelStringDrawing.h b/libs/FontLabel/FontLabelStringDrawing.h
new file mode 100644 (file)
index 0000000..821da22
--- /dev/null
@@ -0,0 +1,69 @@
+//
+//  FontLabelStringDrawing.h
+//  FontLabel
+//
+//  Created by Kevin Ballard on 5/5/09.
+//  Copyright © 2009 Zynga Game Networks
+//
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#import <UIKit/UIKit.h>
+#import "ZAttributedString.h"
+
+@class ZFont;
+
+@interface NSString (FontLabelStringDrawing)
+// CGFontRef-based methods
+- (CGSize)sizeWithCGFont:(CGFontRef)font pointSize:(CGFloat)pointSize __AVAILABILITY_INTERNAL_DEPRECATED;
+- (CGSize)sizeWithCGFont:(CGFontRef)font pointSize:(CGFloat)pointSize constrainedToSize:(CGSize)size __AVAILABILITY_INTERNAL_DEPRECATED;
+- (CGSize)sizeWithCGFont:(CGFontRef)font pointSize:(CGFloat)pointSize constrainedToSize:(CGSize)size
+                  lineBreakMode:(UILineBreakMode)lineBreakMode __AVAILABILITY_INTERNAL_DEPRECATED;
+- (CGSize)drawAtPoint:(CGPoint)point withCGFont:(CGFontRef)font pointSize:(CGFloat)pointSize __AVAILABILITY_INTERNAL_DEPRECATED;
+- (CGSize)drawInRect:(CGRect)rect withCGFont:(CGFontRef)font pointSize:(CGFloat)pointSize __AVAILABILITY_INTERNAL_DEPRECATED;
+- (CGSize)drawInRect:(CGRect)rect withCGFont:(CGFontRef)font pointSize:(CGFloat)pointSize
+          lineBreakMode:(UILineBreakMode)lineBreakMode __AVAILABILITY_INTERNAL_DEPRECATED;
+- (CGSize)drawInRect:(CGRect)rect withCGFont:(CGFontRef)font pointSize:(CGFloat)pointSize
+          lineBreakMode:(UILineBreakMode)lineBreakMode alignment:(UITextAlignment)alignment __AVAILABILITY_INTERNAL_DEPRECATED;
+
+// ZFont-based methods
+- (CGSize)sizeWithZFont:(ZFont *)font;
+- (CGSize)sizeWithZFont:(ZFont *)font constrainedToSize:(CGSize)size;
+- (CGSize)sizeWithZFont:(ZFont *)font constrainedToSize:(CGSize)size lineBreakMode:(UILineBreakMode)lineBreakMode;
+- (CGSize)sizeWithZFont:(ZFont *)font constrainedToSize:(CGSize)size lineBreakMode:(UILineBreakMode)lineBreakMode
+                 numberOfLines:(NSUInteger)numberOfLines;
+- (CGSize)drawAtPoint:(CGPoint)point withZFont:(ZFont *)font;
+- (CGSize)drawAtPoint:(CGPoint)point forWidth:(CGFloat)width withZFont:(ZFont *)font lineBreakMode:(UILineBreakMode)lineBreakMode;
+- (CGSize)drawInRect:(CGRect)rect withZFont:(ZFont *)font;
+- (CGSize)drawInRect:(CGRect)rect withZFont:(ZFont *)font lineBreakMode:(UILineBreakMode)lineBreakMode;
+- (CGSize)drawInRect:(CGRect)rect withZFont:(ZFont *)font lineBreakMode:(UILineBreakMode)lineBreakMode
+                  alignment:(UITextAlignment)alignment;
+- (CGSize)drawInRect:(CGRect)rect withZFont:(ZFont *)font lineBreakMode:(UILineBreakMode)lineBreakMode
+                  alignment:(UITextAlignment)alignment numberOfLines:(NSUInteger)numberOfLines;
+@end
+
+@interface ZAttributedString (ZAttributedStringDrawing)
+- (CGSize)size;
+- (CGSize)sizeConstrainedToSize:(CGSize)size;
+- (CGSize)sizeConstrainedToSize:(CGSize)size lineBreakMode:(UILineBreakMode)lineBreakMode;
+- (CGSize)sizeConstrainedToSize:(CGSize)size lineBreakMode:(UILineBreakMode)lineBreakMode
+                                 numberOfLines:(NSUInteger)numberOfLines;
+- (CGSize)drawAtPoint:(CGPoint)point;
+- (CGSize)drawAtPoint:(CGPoint)point forWidth:(CGFloat)width lineBreakMode:(UILineBreakMode)lineBreakMode;
+- (CGSize)drawInRect:(CGRect)rect;
+- (CGSize)drawInRect:(CGRect)rect withLineBreakMode:(UILineBreakMode)lineBreakMode;
+- (CGSize)drawInRect:(CGRect)rect withLineBreakMode:(UILineBreakMode)lineBreakMode alignment:(UITextAlignment)alignment;
+- (CGSize)drawInRect:(CGRect)rect withLineBreakMode:(UILineBreakMode)lineBreakMode alignment:(UITextAlignment)alignment
+          numberOfLines:(NSUInteger)numberOfLines;
+@end
diff --git a/libs/FontLabel/FontLabelStringDrawing.m b/libs/FontLabel/FontLabelStringDrawing.m
new file mode 100644 (file)
index 0000000..2907372
--- /dev/null
@@ -0,0 +1,892 @@
+//
+//  FontLabelStringDrawing.m
+//  FontLabel
+//
+//  Created by Kevin Ballard on 5/5/09.
+//  Copyright © 2009 Zynga Game Networks
+//
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#import "FontLabelStringDrawing.h"
+#import "ZFont.h"
+#import "ZAttributedStringPrivate.h"
+
+@interface ZFont (ZFontPrivate)
+@property (nonatomic, readonly) CGFloat ratio;
+@end
+
+#define kUnicodeHighSurrogateStart 0xD800
+#define kUnicodeHighSurrogateEnd 0xDBFF
+#define kUnicodeHighSurrogateMask kUnicodeHighSurrogateStart
+#define kUnicodeLowSurrogateStart 0xDC00
+#define kUnicodeLowSurrogateEnd 0xDFFF
+#define kUnicodeLowSurrogateMask kUnicodeLowSurrogateStart
+#define kUnicodeSurrogateTypeMask 0xFC00
+#define UnicharIsHighSurrogate(c) ((c & kUnicodeSurrogateTypeMask) == kUnicodeHighSurrogateMask)
+#define UnicharIsLowSurrogate(c) ((c & kUnicodeSurrogateTypeMask) == kUnicodeLowSurrogateMask)
+#define ConvertSurrogatePairToUTF32(high, low) ((UInt32)((high - 0xD800) * 0x400 + (low - 0xDC00) + 0x10000))
+
+typedef enum {
+       kFontTableFormat4 = 4,
+       kFontTableFormat12 = 12,
+} FontTableFormat;
+
+typedef struct fontTable {
+       NSUInteger retainCount;
+       CFDataRef cmapTable;
+       FontTableFormat format;
+       union {
+               struct {
+                       UInt16 segCountX2;
+                       UInt16 *endCodes;
+                       UInt16 *startCodes;
+                       UInt16 *idDeltas;
+                       UInt16 *idRangeOffsets;
+               } format4;
+               struct {
+                       UInt32 nGroups;
+                       struct {
+                               UInt32 startCharCode;
+                               UInt32 endCharCode;
+                               UInt32 startGlyphCode;
+                       } *groups;
+               } format12;
+       } cmap;
+} fontTable;
+
+static FontTableFormat supportedFormats[] = { kFontTableFormat4, kFontTableFormat12 };
+static size_t supportedFormatsCount = sizeof(supportedFormats) / sizeof(FontTableFormat);
+
+static fontTable *newFontTable(CFDataRef cmapTable, FontTableFormat format) {
+       fontTable *table = (struct fontTable *)malloc(sizeof(struct fontTable));
+       table->retainCount = 1;
+       table->cmapTable = CFRetain(cmapTable);
+       table->format = format;
+       return table;
+}
+
+static fontTable *retainFontTable(fontTable *table) {
+       if (table != NULL) {
+               table->retainCount++;
+       }
+       return table;
+}
+
+static void releaseFontTable(fontTable *table) {
+       if (table != NULL) {
+               if (table->retainCount <= 1) {
+                       CFRelease(table->cmapTable);
+                       free(table);
+               } else {
+                       table->retainCount--;
+               }
+       }
+}
+
+static const void *fontTableRetainCallback(CFAllocatorRef allocator, const void *value) {
+       return retainFontTable((fontTable *)value);
+}
+
+static void fontTableReleaseCallback(CFAllocatorRef allocator, const void *value) {
+       releaseFontTable((fontTable *)value);
+}
+
+static const CFDictionaryValueCallBacks kFontTableDictionaryValueCallBacks = {
+       .version = 0,
+       .retain = &fontTableRetainCallback,
+       .release = &fontTableReleaseCallback,
+       .copyDescription = NULL,
+       .equal = NULL
+};
+
+// read the cmap table from the font
+// we only know how to understand some of the table formats at the moment
+static fontTable *readFontTableFromCGFont(CGFontRef font) {
+       CFDataRef cmapTable = CGFontCopyTableForTag(font, 'cmap');
+       NSCAssert1(cmapTable != NULL, @"CGFontCopyTableForTag returned NULL for 'cmap' tag in font %@",
+                          (font ? [(id)CFCopyDescription(font) autorelease] : @"(null)"));
+       const UInt8 * const bytes = CFDataGetBytePtr(cmapTable);
+       NSCAssert1(OSReadBigInt16(bytes, 0) == 0, @"cmap table for font %@ has bad version number",
+                          (font ? [(id)CFCopyDescription(font) autorelease] : @"(null)"));
+       UInt16 numberOfSubtables = OSReadBigInt16(bytes, 2);
+       const UInt8 *unicodeSubtable = NULL;
+       //UInt16 unicodeSubtablePlatformID;
+       UInt16 unicodeSubtablePlatformSpecificID;
+       FontTableFormat unicodeSubtableFormat;
+       const UInt8 * const encodingSubtables = &bytes[4];
+       for (UInt16 i = 0; i < numberOfSubtables; i++) {
+               const UInt8 * const encodingSubtable = &encodingSubtables[8 * i];
+               UInt16 platformID = OSReadBigInt16(encodingSubtable, 0);
+               UInt16 platformSpecificID = OSReadBigInt16(encodingSubtable, 2);
+               // find the best subtable
+               // best is defined by a combination of encoding and format
+               // At the moment we only support format 4, so ignore all other format tables
+               // We prefer platformID == 0, but we will also accept Microsoft's unicode format
+               if (platformID == 0 || (platformID == 3 && platformSpecificID == 1)) {
+                       BOOL preferred = NO;
+                       if (unicodeSubtable == NULL) {
+                               preferred = YES;
+                       } else if (platformID == 0 && platformSpecificID > unicodeSubtablePlatformSpecificID) {
+                               preferred = YES;
+                       }
+                       if (preferred) {
+                               UInt32 offset = OSReadBigInt32(encodingSubtable, 4);
+                               const UInt8 *subtable = &bytes[offset];
+                               UInt16 format = OSReadBigInt16(subtable, 0);
+                               for (size_t i = 0; i < supportedFormatsCount; i++) {
+                                       if (format == supportedFormats[i]) {
+                                               if (format >= 8) {
+                                                       // the version is a fixed-point
+                                                       UInt16 formatFrac = OSReadBigInt16(subtable, 2);
+                                                       if (formatFrac != 0) {
+                                                               // all the current formats with a Fixed version are always *.0
+                                                               continue;
+                                                       }
+                                               }
+                                               unicodeSubtable = subtable;
+                                               //unicodeSubtablePlatformID = platformID;
+                                               unicodeSubtablePlatformSpecificID = platformSpecificID;
+                                               unicodeSubtableFormat = format;
+                                               break;
+                                       }
+                               }
+                       }
+               }
+       }
+       fontTable *table = NULL;
+       if (unicodeSubtable != NULL) {
+               table = newFontTable(cmapTable, unicodeSubtableFormat);
+               switch (unicodeSubtableFormat) {
+                       case kFontTableFormat4:
+                               // subtable format 4
+                               //UInt16 length = OSReadBigInt16(unicodeSubtable, 2);
+                               //UInt16 language = OSReadBigInt16(unicodeSubtable, 4);
+                               table->cmap.format4.segCountX2 = OSReadBigInt16(unicodeSubtable, 6);
+                               //UInt16 searchRange = OSReadBigInt16(unicodeSubtable, 8);
+                               //UInt16 entrySelector = OSReadBigInt16(unicodeSubtable, 10);
+                               //UInt16 rangeShift = OSReadBigInt16(unicodeSubtable, 12);
+                               table->cmap.format4.endCodes = (UInt16*)&unicodeSubtable[14];
+                               table->cmap.format4.startCodes = (UInt16*)&((UInt8*)table->cmap.format4.endCodes)[table->cmap.format4.segCountX2+2];
+                               table->cmap.format4.idDeltas = (UInt16*)&((UInt8*)table->cmap.format4.startCodes)[table->cmap.format4.segCountX2];
+                               table->cmap.format4.idRangeOffsets = (UInt16*)&((UInt8*)table->cmap.format4.idDeltas)[table->cmap.format4.segCountX2];
+                               //UInt16 *glyphIndexArray = &idRangeOffsets[segCountX2];
+                               break;
+                       case kFontTableFormat12:
+                               table->cmap.format12.nGroups = OSReadBigInt32(unicodeSubtable, 12);
+                               table->cmap.format12.groups = (void *)&unicodeSubtable[16];
+                               break;
+                       default:
+                               releaseFontTable(table);
+                               table = NULL;
+               }
+       }
+       CFRelease(cmapTable);
+       return table;
+}
+
+// outGlyphs must be at least size n
+static void mapCharactersToGlyphsInFont(const fontTable *table, unichar characters[], size_t charLen, CGGlyph outGlyphs[], size_t *outGlyphLen) {
+       if (table != NULL) {
+               NSUInteger j = 0;
+               switch (table->format) {
+                       case kFontTableFormat4: {
+                               for (NSUInteger i = 0; i < charLen; i++, j++) {
+                                       unichar c = characters[i];
+                                       UInt16 segOffset;
+                                       BOOL foundSegment = NO;
+                                       for (segOffset = 0; segOffset < table->cmap.format4.segCountX2; segOffset += 2) {
+                                               UInt16 endCode = OSReadBigInt16(table->cmap.format4.endCodes, segOffset);
+                                               if (endCode >= c) {
+                                                       foundSegment = YES;
+                                                       break;
+                                               }
+                                       }
+                                       if (!foundSegment) {
+                                               // no segment
+                                               // this is an invalid font
+                                               outGlyphs[j] = 0;
+                                       } else {
+                                               UInt16 startCode = OSReadBigInt16(table->cmap.format4.startCodes, segOffset);
+                                               if (!(startCode <= c)) {
+                                                       // the code falls in a hole between segments
+                                                       outGlyphs[j] = 0;
+                                               } else {
+                                                       UInt16 idRangeOffset = OSReadBigInt16(table->cmap.format4.idRangeOffsets, segOffset);
+                                                       if (idRangeOffset == 0) {
+                                                               UInt16 idDelta = OSReadBigInt16(table->cmap.format4.idDeltas, segOffset);
+                                                               outGlyphs[j] = (c + idDelta) % 65536;
+                                                       } else {
+                                                               // use the glyphIndexArray
+                                                               UInt16 glyphOffset = idRangeOffset + 2 * (c - startCode);
+                                                               outGlyphs[j] = OSReadBigInt16(&((UInt8*)table->cmap.format4.idRangeOffsets)[segOffset], glyphOffset);
+                                                       }
+                                               }
+                                       }
+                               }
+                               break;
+                       }
+                       case kFontTableFormat12: {
+                               UInt32 lastSegment = UINT32_MAX;
+                               for (NSUInteger i = 0; i < charLen; i++, j++) {
+                                       unichar c = characters[i];
+                                       UInt32 c32 = c;
+                                       if (UnicharIsHighSurrogate(c)) {
+                                               if (i+1 < charLen) { // do we have another character after this one?
+                                                       unichar cc = characters[i+1];
+                                                       if (UnicharIsLowSurrogate(cc)) {
+                                                               c32 = ConvertSurrogatePairToUTF32(c, cc);
+                                                               i++;
+                                                       }
+                                               }
+                                       }
+                                       // Start the heuristic search
+                                       // If this is an ASCII char, just do a linear search
+                                       // Otherwise do a hinted, modified binary search
+                                       // Start the first pivot at the last range found
+                                       // And when moving the pivot, limit the movement by increasing
+                                       // powers of two. This should help with locality
+                                       __typeof__(table->cmap.format12.groups[0]) *foundGroup = NULL;
+                                       if (c32 <= 0x7F) {
+                                               // ASCII
+                                               for (UInt32 idx = 0; idx < table->cmap.format12.nGroups; idx++) {
+                                                       __typeof__(table->cmap.format12.groups[idx]) *group = &table->cmap.format12.groups[idx];
+                                                       if (c32 < OSSwapBigToHostInt32(group->startCharCode)) {
+                                                               // we've fallen into a hole
+                                                               break;
+                                                       } else if (c32 <= OSSwapBigToHostInt32(group->endCharCode)) {
+                                                               // this is the range
+                                                               foundGroup = group;
+                                                               break;
+                                                       }
+                                               }
+                                       } else {
+                                               // heuristic search
+                                               UInt32 maxJump = (lastSegment == UINT32_MAX ? UINT32_MAX / 2 : 8);
+                                               UInt32 lowIdx = 0, highIdx = table->cmap.format12.nGroups; // highIdx is the first invalid idx
+                                               UInt32 pivot = (lastSegment == UINT32_MAX ? lowIdx + (highIdx - lowIdx) / 2 : lastSegment);
+                                               while (highIdx > lowIdx) {
+                                                       __typeof__(table->cmap.format12.groups[pivot]) *group = &table->cmap.format12.groups[pivot];
+                                                       if (c32 < OSSwapBigToHostInt32(group->startCharCode)) {
+                                                               highIdx = pivot;
+                                                       } else if (c32 > OSSwapBigToHostInt32(group->endCharCode)) {
+                                                               lowIdx = pivot + 1;
+                                                       } else {
+                                                               // we've hit the range
+                                                               foundGroup = group;
+                                                               break;
+                                                       }
+                                                       if (highIdx - lowIdx > maxJump * 2) {
+                                                               if (highIdx == pivot) {
+                                                                       pivot -= maxJump;
+                                                               } else {
+                                                                       pivot += maxJump;
+                                                               }
+                                                               maxJump *= 2;
+                                                       } else {
+                                                               pivot = lowIdx + (highIdx - lowIdx) / 2;
+                                                       }
+                                               }
+                                               if (foundGroup != NULL) lastSegment = pivot;
+                                       }
+                                       if (foundGroup == NULL) {
+                                               outGlyphs[j] = 0;
+                                       } else {
+                                               outGlyphs[j] = (CGGlyph)(OSSwapBigToHostInt32(foundGroup->startGlyphCode) +
+                                                                                                (c32 - OSSwapBigToHostInt32(foundGroup->startCharCode)));
+                                       }
+                               }
+                               break;
+                       }
+               }
+               if (outGlyphLen != NULL) *outGlyphLen = j;
+       } else {
+               // we have no table, so just null out the glyphs
+               bzero(outGlyphs, charLen*sizeof(CGGlyph));
+               if (outGlyphLen != NULL) *outGlyphLen = 0;
+       }
+}
+
+static BOOL mapGlyphsToAdvancesInFont(ZFont *font, size_t n, CGGlyph glyphs[], CGFloat outAdvances[]) {
+       int advances[n];
+       if (CGFontGetGlyphAdvances(font.cgFont, glyphs, n, advances)) {
+               CGFloat ratio = font.ratio;
+               
+               for (size_t i = 0; i < n; i++) {
+                       outAdvances[i] = advances[i]*ratio;
+               }
+               return YES;
+       } else {
+               bzero(outAdvances, n*sizeof(CGFloat));
+       }
+       return NO;
+}
+
+static id getValueOrDefaultForRun(ZAttributeRun *run, NSString *key) {
+       id value = [run.attributes objectForKey:key];
+       if (value == nil) {
+               static NSDictionary *defaultValues = nil;
+               if (defaultValues == nil) {
+                       defaultValues = [[NSDictionary alloc] initWithObjectsAndKeys:
+                                                        [ZFont fontWithUIFont:[UIFont systemFontOfSize:12]], ZFontAttributeName,
+                                                        [UIColor blackColor], ZForegroundColorAttributeName,
+                                                        [UIColor clearColor], ZBackgroundColorAttributeName,
+                                                        [NSNumber numberWithInt:ZUnderlineStyleNone], ZUnderlineStyleAttributeName,
+                                                        nil];
+               }
+               value = [defaultValues objectForKey:key];
+       }
+       return value;
+}
+
+static void readRunInformation(NSArray *attributes, NSUInteger len, CFMutableDictionaryRef fontTableMap,
+                                                          NSUInteger index, ZAttributeRun **currentRun, NSUInteger *nextRunStart,
+                                                          ZFont **currentFont, fontTable **currentTable) {
+       *currentRun = [attributes objectAtIndex:index];
+       *nextRunStart = ([attributes count] > index+1 ? [[attributes objectAtIndex:index+1] index] : len);
+       *currentFont = getValueOrDefaultForRun(*currentRun, ZFontAttributeName);
+       if (!CFDictionaryGetValueIfPresent(fontTableMap, (*currentFont).cgFont, (const void **)currentTable)) {
+               *currentTable = readFontTableFromCGFont((*currentFont).cgFont);
+               CFDictionarySetValue(fontTableMap, (*currentFont).cgFont, *currentTable);
+               releaseFontTable(*currentTable);
+       }
+}
+
+static CGSize drawOrSizeTextConstrainedToSize(BOOL performDraw, NSString *string, NSArray *attributes, CGSize constrainedSize, NSUInteger maxLines,
+                                                                                         UILineBreakMode lineBreakMode, UITextAlignment alignment, BOOL ignoreColor) {
+       NSUInteger len = [string length];
+       NSUInteger idx = 0;
+       CGPoint drawPoint = CGPointZero;
+       CGSize retValue = CGSizeZero;
+       CGContextRef ctx = (performDraw ? UIGraphicsGetCurrentContext() : NULL);
+       
+       BOOL convertNewlines = (maxLines == 1);
+       
+       // Extract the characters from the string
+       // Convert newlines to spaces if necessary
+       unichar *characters = (unichar *)malloc(sizeof(unichar) * len);
+       if (convertNewlines) {
+               NSCharacterSet *charset = [NSCharacterSet newlineCharacterSet];
+               NSRange range = NSMakeRange(0, len);
+               size_t cIdx = 0;
+               while (range.length > 0) {
+                       NSRange newlineRange = [string rangeOfCharacterFromSet:charset options:0 range:range];
+                       if (newlineRange.location == NSNotFound) {
+                               [string getCharacters:&characters[cIdx] range:range];
+                               cIdx += range.length;
+                               break;
+                       } else {
+                               NSUInteger delta = newlineRange.location - range.location;
+                               if (newlineRange.location > range.location) {
+                                       [string getCharacters:&characters[cIdx] range:NSMakeRange(range.location, delta)];
+                               }
+                               cIdx += delta;
+                               characters[cIdx] = (unichar)' ';
+                               cIdx++;
+                               delta += newlineRange.length;
+                               range.location += delta, range.length -= delta;
+                               if (newlineRange.length == 1 && range.length >= 1 &&
+                                       [string characterAtIndex:newlineRange.location] == (unichar)'\r' &&
+                                       [string characterAtIndex:range.location] == (unichar)'\n') {
+                                       // CRLF sequence, skip the LF
+                                       range.location += 1, range.length -= 1;
+                               }
+                       }
+               }
+               len = cIdx;
+       } else {
+               [string getCharacters:characters range:NSMakeRange(0, len)];
+       }
+       
+       // Create storage for glyphs and advances
+       CGGlyph *glyphs;
+       CGFloat *advances;
+       {
+               NSUInteger maxRunLength = 0;
+               ZAttributeRun *a = [attributes objectAtIndex:0];
+               for (NSUInteger i = 1; i < [attributes count]; i++) {
+                       ZAttributeRun *b = [attributes objectAtIndex:i];
+                       maxRunLength = MAX(maxRunLength, b.index - a.index);
+                       a = b;
+               }
+               maxRunLength = MAX(maxRunLength, len - a.index);
+               maxRunLength++; // for a potential ellipsis
+               glyphs = (CGGlyph *)malloc(sizeof(CGGlyph) * maxRunLength);
+               advances = (CGFloat *)malloc(sizeof(CGFloat) * maxRunLength);
+       }
+       
+       // Use this table to cache all fontTable objects
+       CFMutableDictionaryRef fontTableMap = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks,
+                                                                                                                                        &kFontTableDictionaryValueCallBacks);
+       
+       // Fetch initial style values
+       NSUInteger currentRunIdx = 0;
+       ZAttributeRun *currentRun;
+       NSUInteger nextRunStart;
+       ZFont *currentFont;
+       fontTable *currentTable;
+       
+#define READ_RUN() readRunInformation(attributes, len, fontTableMap, \
+                                                                         currentRunIdx, &currentRun, &nextRunStart, \
+                                                                         &currentFont, &currentTable)
+       
+       READ_RUN();
+       
+       // fetch the glyphs for the first run
+       size_t glyphCount;
+       NSUInteger glyphIdx;
+       
+#define READ_GLYPHS() do { \
+               mapCharactersToGlyphsInFont(currentTable, &characters[currentRun.index], (nextRunStart - currentRun.index), glyphs, &glyphCount); \
+               mapGlyphsToAdvancesInFont(currentFont, (nextRunStart - currentRun.index), glyphs, advances); \
+               glyphIdx = 0; \
+       } while (0)
+       
+       READ_GLYPHS();
+       
+       NSMutableCharacterSet *alphaCharset = [NSMutableCharacterSet alphanumericCharacterSet];
+       [alphaCharset addCharactersInString:@"([{'\"\u2019\u02BC"];
+       
+       // scan left-to-right looking for newlines or until we hit the width constraint
+       // When we hit a wrapping point, calculate truncation as follows:
+       // If we have room to draw at least one more character on the next line, no truncation
+       // Otherwise apply the truncation algorithm to the current line.
+       // After calculating any truncation, draw.
+       // Each time we hit the end of an attribute run, calculate the new font and make sure
+       // it fits (vertically) within the size constraint. If not, truncate this line.
+       // When we draw, iterate over the attribute runs for this line and draw each run separately
+       BOOL lastLine = NO; // used to indicate truncation and to stop the iterating
+       NSUInteger lineCount = 1;
+       while (idx < len && !lastLine) {
+               if (maxLines > 0 && lineCount == maxLines) {
+                       lastLine = YES;
+               }
+               // scan left-to-right
+               struct {
+                       NSUInteger index;
+                       NSUInteger glyphIndex;
+                       NSUInteger currentRunIdx;
+               } indexCache = { idx, glyphIdx, currentRunIdx };
+               CGSize lineSize = CGSizeMake(0, currentFont.leading);
+               CGFloat lineAscender = currentFont.ascender;
+               struct {
+                       NSUInteger index;
+                       NSUInteger glyphIndex;
+                       NSUInteger currentRunIdx;
+                       CGSize lineSize;
+               } lastWrapCache = {0, 0, 0, CGSizeZero};
+               BOOL inAlpha = NO; // used for calculating wrap points
+               
+               BOOL finishLine = NO;
+               for (;idx <= len && !finishLine;) {
+                       NSUInteger skipCount = 0;
+                       if (idx == len) {
+                               finishLine = YES;
+                               lastLine = YES;
+                       } else {
+                               if (idx >= nextRunStart) {
+                                       // cycle the font and table and grab the next set of glyphs
+                                       do {
+                                               currentRunIdx++;
+                                               READ_RUN();
+                                       } while (idx >= nextRunStart);
+                                       READ_GLYPHS();
+                                       // re-scan the characters to synchronize the glyph index
+                                       for (NSUInteger j = currentRun.index; j < idx; j++) {
+                                               if (UnicharIsHighSurrogate(characters[j]) && j+1<len && UnicharIsLowSurrogate(characters[j+1])) {
+                                                       j++;
+                                               }
+                                               glyphIdx++;
+                                       }
+                                       if (currentFont.leading > lineSize.height) {
+                                               lineSize.height = currentFont.leading;
+                                               if (retValue.height + currentFont.ascender > constrainedSize.height) {
+                                                       lastLine = YES;
+                                                       finishLine = YES;
+                                               }
+                                       }
+                                       lineAscender = MAX(lineAscender, currentFont.ascender);
+                               }
+                               unichar c = characters[idx];
+                               // Mark a wrap point before spaces and after any stretch of non-alpha characters
+                               BOOL markWrap = NO;
+                               if (c == (unichar)' ') {
+                                       markWrap = YES;
+                               } else if ([alphaCharset characterIsMember:c]) {
+                                       if (!inAlpha) {
+                                               markWrap = YES;
+                                               inAlpha = YES;
+                                       }
+                               } else {
+                                       inAlpha = NO;
+                               }
+                               if (markWrap) {
+                                       lastWrapCache = (__typeof__(lastWrapCache)){
+                                               .index = idx,
+                                               .glyphIndex = glyphIdx,
+                                               .currentRunIdx = currentRunIdx,
+                                               .lineSize = lineSize
+                                       };
+                               }
+                               // process the line
+                               if (c == (unichar)'\n' || c == 0x0085) { // U+0085 is the NEXT_LINE unicode character
+                                       finishLine = YES;
+                                       skipCount = 1;
+                               } else if (c == (unichar)'\r') {
+                                       finishLine = YES;
+                                       // check for CRLF
+                                       if (idx+1 < len && characters[idx+1] == (unichar)'\n') {
+                                               skipCount = 2;
+                                       } else {
+                                               skipCount = 1;
+                                       }
+                               } else if (lineSize.width + advances[glyphIdx] > constrainedSize.width) {
+                                       finishLine = YES;
+                                       if (retValue.height + lineSize.height + currentFont.ascender > constrainedSize.height) {
+                                               lastLine = YES;
+                                       }
+                                       // walk backwards if wrapping is necessary
+                                       if (lastWrapCache.index > indexCache.index && lineBreakMode != UILineBreakModeCharacterWrap &&
+                                               (!lastLine || lineBreakMode != UILineBreakModeClip)) {
+                                               // we're doing some sort of word wrapping
+                                               idx = lastWrapCache.index;
+                                               lineSize = lastWrapCache.lineSize;
+                                               if (!lastLine) {
+                                                       // re-check if this is the last line
+                                                       if (lastWrapCache.currentRunIdx != currentRunIdx) {
+                                                               currentRunIdx = lastWrapCache.currentRunIdx;
+                                                               READ_RUN();
+                                                               READ_GLYPHS();
+                                                       }
+                                                       if (retValue.height + lineSize.height + currentFont.ascender > constrainedSize.height) {
+                                                               lastLine = YES;
+                                                       }
+                                               }
+                                               glyphIdx = lastWrapCache.glyphIndex;
+                                               // skip any spaces
+                                               for (NSUInteger j = idx; j < len && characters[j] == (unichar)' '; j++) {
+                                                       skipCount++;
+                                               }
+                                       }
+                               }
+                       }
+                       if (finishLine) {
+                               // TODO: support head/middle truncation
+                               if (lastLine && idx < len && lineBreakMode == UILineBreakModeTailTruncation) {
+                                       // truncate
+                                       unichar ellipsis = 0x2026; // ellipsis (…)
+                                       CGGlyph ellipsisGlyph;
+                                       mapCharactersToGlyphsInFont(currentTable, &ellipsis, 1, &ellipsisGlyph, NULL);
+                                       CGFloat ellipsisWidth;
+                                       mapGlyphsToAdvancesInFont(currentFont, 1, &ellipsisGlyph, &ellipsisWidth);
+                                       while ((idx - indexCache.index) > 1 && lineSize.width + ellipsisWidth > constrainedSize.width) {
+                                               // we have more than 1 character and we're too wide, so back up
+                                               idx--;
+                                               if (UnicharIsHighSurrogate(characters[idx]) && UnicharIsLowSurrogate(characters[idx+1])) {
+                                                       idx--;
+                                               }
+                                               if (idx < currentRun.index) {
+                                                       ZFont *oldFont = currentFont;
+                                                       do {
+                                                               currentRunIdx--;
+                                                               READ_RUN();
+                                                       } while (idx < currentRun.index);
+                                                       READ_GLYPHS();
+                                                       glyphIdx = glyphCount-1;
+                                                       if (oldFont != currentFont) {
+                                                               mapCharactersToGlyphsInFont(currentTable, &ellipsis, 1, &ellipsisGlyph, NULL);
+                                                               mapGlyphsToAdvancesInFont(currentFont, 1, &ellipsisGlyph, &ellipsisWidth);
+                                                       }
+                                               } else {
+                                                       glyphIdx--;
+                                               }
+                                               lineSize.width -= advances[glyphIdx];
+                                       }
+                                       // skip any spaces before truncating
+                                       while ((idx - indexCache.index) > 1 && characters[idx-1] == (unichar)' ') {
+                                               idx--;
+                                               if (idx < currentRun.index) {
+                                                       currentRunIdx--;
+                                                       READ_RUN();
+                                                       READ_GLYPHS();
+                                                       glyphIdx = glyphCount-1;
+                                               } else {
+                                                       glyphIdx--;
+                                               }
+                                               lineSize.width -= advances[glyphIdx];
+                                       }
+                                       lineSize.width += ellipsisWidth;
+                                       glyphs[glyphIdx] = ellipsisGlyph;
+                                       idx++;
+                                       glyphIdx++;
+                               }
+                               retValue.width = MAX(retValue.width, lineSize.width);
+                               retValue.height += lineSize.height;
+                               
+                               // draw
+                               if (performDraw) {
+                                       switch (alignment) {
+                                               case UITextAlignmentLeft:
+                                                       drawPoint.x = 0;
+                                                       break;
+                                               case UITextAlignmentCenter:
+                                                       drawPoint.x = (constrainedSize.width - lineSize.width) / 2.0f;
+                                                       break;
+                                               case UITextAlignmentRight:
+                                                       drawPoint.x = constrainedSize.width - lineSize.width;
+                                                       break;
+                                       }
+                                       NSUInteger stopGlyphIdx = glyphIdx;
+                                       NSUInteger lastRunIdx = currentRunIdx;
+                                       NSUInteger stopCharIdx = idx;
+                                       idx = indexCache.index;
+                                       if (currentRunIdx != indexCache.currentRunIdx) {
+                                               currentRunIdx = indexCache.currentRunIdx;
+                                               READ_RUN();
+                                               READ_GLYPHS();
+                                       }
+                                       glyphIdx = indexCache.glyphIndex;
+                                       for (NSUInteger drawIdx = currentRunIdx; drawIdx <= lastRunIdx; drawIdx++) {
+                                               if (drawIdx != currentRunIdx) {
+                                                       currentRunIdx = drawIdx;
+                                                       READ_RUN();
+                                                       READ_GLYPHS();
+                                               }
+                                               NSUInteger numGlyphs;
+                                               if (drawIdx == lastRunIdx) {
+                                                       numGlyphs = stopGlyphIdx - glyphIdx;
+                                                       idx = stopCharIdx;
+                                               } else {
+                                                       numGlyphs = glyphCount - glyphIdx;
+                                                       idx = nextRunStart;
+                                               }
+                                               CGContextSetFont(ctx, currentFont.cgFont);
+                                               CGContextSetFontSize(ctx, currentFont.pointSize);
+                                               // calculate the fragment size
+                                               CGFloat fragmentWidth = 0;
+                                               for (NSUInteger g = 0; g < numGlyphs; g++) {
+                                                       fragmentWidth += advances[glyphIdx + g];
+                                               }
+                                               
+                                               if (!ignoreColor) {
+                                                       UIColor *foregroundColor = getValueOrDefaultForRun(currentRun, ZForegroundColorAttributeName);
+                                                       UIColor *backgroundColor = getValueOrDefaultForRun(currentRun, ZBackgroundColorAttributeName);
+                                                       if (backgroundColor != nil && ![backgroundColor isEqual:[UIColor clearColor]]) {
+                                                               [backgroundColor setFill];
+                                                               UIRectFillUsingBlendMode((CGRect){ drawPoint, { fragmentWidth, lineSize.height } }, kCGBlendModeNormal);
+                                                       }
+                                                       [foregroundColor setFill];
+                                               }
+                                               
+                                               CGContextShowGlyphsAtPoint(ctx, drawPoint.x, drawPoint.y + lineAscender, &glyphs[glyphIdx], numGlyphs);
+                                               NSNumber *underlineStyle = getValueOrDefaultForRun(currentRun, ZUnderlineStyleAttributeName);
+                                               if ([underlineStyle     integerValue] & ZUnderlineStyleMask) {
+                                                       // we only support single for the time being
+                                                       UIRectFill(CGRectMake(drawPoint.x, drawPoint.y + lineAscender, fragmentWidth, 1));
+                                               }
+                                               drawPoint.x += fragmentWidth;
+                                               glyphIdx += numGlyphs;
+                                       }
+                                       drawPoint.y += lineSize.height;
+                               }
+                               idx += skipCount;
+                               glyphIdx += skipCount;
+                               lineCount++;
+                       } else {
+                               lineSize.width += advances[glyphIdx];
+                               glyphIdx++;
+                               idx++;
+                               if (idx < len && UnicharIsHighSurrogate(characters[idx-1]) && UnicharIsLowSurrogate(characters[idx])) {
+                                       // skip the second half of the surrogate pair
+                                       idx++;
+                               }
+                       }
+               }
+       }
+       CFRelease(fontTableMap);
+       free(glyphs);
+       free(advances);
+       free(characters);
+       
+#undef READ_GLYPHS
+#undef READ_RUN
+       
+       return retValue;
+}
+
+static NSArray *attributeRunForFont(ZFont *font) {
+       return [NSArray arrayWithObject:[ZAttributeRun attributeRunWithIndex:0
+                                                                                                                         attributes:[NSDictionary dictionaryWithObject:font
+                                                                                                                                                                                                        forKey:ZFontAttributeName]]];
+}
+
+static CGSize drawTextInRect(CGRect rect, NSString *text, NSArray *attributes, UILineBreakMode lineBreakMode,
+                                                        UITextAlignment alignment, NSUInteger numberOfLines, BOOL ignoreColor) {
+       CGContextRef ctx = UIGraphicsGetCurrentContext();
+       
+       CGContextSaveGState(ctx);
+       
+       // flip it upside-down because our 0,0 is upper-left, whereas ttfs are for screens where 0,0 is lower-left
+       CGAffineTransform textTransform = CGAffineTransformMake(1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f);
+       CGContextSetTextMatrix(ctx, textTransform);
+       
+       CGContextTranslateCTM(ctx, rect.origin.x, rect.origin.y);
+       
+       CGContextSetTextDrawingMode(ctx, kCGTextFill);
+       CGSize size = drawOrSizeTextConstrainedToSize(YES, text, attributes, rect.size, numberOfLines, lineBreakMode, alignment, ignoreColor);
+       
+       CGContextRestoreGState(ctx);
+       
+       return size;
+}
+
+@implementation NSString (FontLabelStringDrawing)
+// CGFontRef-based methods
+- (CGSize)sizeWithCGFont:(CGFontRef)font pointSize:(CGFloat)pointSize {
+       return [self sizeWithZFont:[ZFont fontWithCGFont:font size:pointSize]];
+}
+
+- (CGSize)sizeWithCGFont:(CGFontRef)font pointSize:(CGFloat)pointSize constrainedToSize:(CGSize)size {
+       return [self sizeWithZFont:[ZFont fontWithCGFont:font size:pointSize] constrainedToSize:size];
+}
+
+- (CGSize)sizeWithCGFont:(CGFontRef)font pointSize:(CGFloat)pointSize constrainedToSize:(CGSize)size
+                  lineBreakMode:(UILineBreakMode)lineBreakMode {
+       return [self sizeWithZFont:[ZFont fontWithCGFont:font size:pointSize] constrainedToSize:size lineBreakMode:lineBreakMode];
+}
+
+- (CGSize)drawAtPoint:(CGPoint)point withCGFont:(CGFontRef)font pointSize:(CGFloat)pointSize {
+       return [self drawAtPoint:point withZFont:[ZFont fontWithCGFont:font size:pointSize]];
+}
+
+- (CGSize)drawInRect:(CGRect)rect withCGFont:(CGFontRef)font pointSize:(CGFloat)pointSize {
+       return [self drawInRect:rect withZFont:[ZFont fontWithCGFont:font size:pointSize]];
+}
+
+- (CGSize)drawInRect:(CGRect)rect withCGFont:(CGFontRef)font pointSize:(CGFloat)pointSize lineBreakMode:(UILineBreakMode)lineBreakMode {
+       return [self drawInRect:rect withZFont:[ZFont fontWithCGFont:font size:pointSize] lineBreakMode:lineBreakMode];
+}
+
+- (CGSize)drawInRect:(CGRect)rect withCGFont:(CGFontRef)font pointSize:(CGFloat)pointSize
+          lineBreakMode:(UILineBreakMode)lineBreakMode alignment:(UITextAlignment)alignment {
+       return [self drawInRect:rect withZFont:[ZFont fontWithCGFont:font size:pointSize] lineBreakMode:lineBreakMode alignment:alignment];
+}
+
+// ZFont-based methods
+- (CGSize)sizeWithZFont:(ZFont *)font {
+       CGSize size = drawOrSizeTextConstrainedToSize(NO, self, attributeRunForFont(font), CGSizeMake(CGFLOAT_MAX, CGFLOAT_MAX), 1,
+                                                                                                 UILineBreakModeClip, UITextAlignmentLeft, YES);
+       return CGSizeMake(ceilf(size.width), ceilf(size.height));
+}
+
+- (CGSize)sizeWithZFont:(ZFont *)font constrainedToSize:(CGSize)size {
+       return [self sizeWithZFont:font constrainedToSize:size lineBreakMode:UILineBreakModeWordWrap];
+}
+
+/*
+ According to experimentation with UIStringDrawing, this can actually return a CGSize whose height is greater
+ than the one passed in. The two cases are as follows:
+ 1. If the given size parameter's height is smaller than a single line, the returned value will
+ be the height of one line.
+ 2. If the given size parameter's height falls between multiples of a line height, and the wrapped string
+ actually extends past the size.height, and the difference between size.height and the previous multiple
+ of a line height is >= the font's ascender, then the returned size's height is extended to the next line.
+ To put it simply, if the baseline point of a given line falls in the given size, the entire line will
+ be present in the output size.
+ */
+- (CGSize)sizeWithZFont:(ZFont *)font constrainedToSize:(CGSize)size lineBreakMode:(UILineBreakMode)lineBreakMode {
+       size = drawOrSizeTextConstrainedToSize(NO, self, attributeRunForFont(font), size, 0, lineBreakMode, UITextAlignmentLeft, YES);
+       return CGSizeMake(ceilf(size.width), ceilf(size.height));
+}
+
+- (CGSize)sizeWithZFont:(ZFont *)font constrainedToSize:(CGSize)size lineBreakMode:(UILineBreakMode)lineBreakMode
+                 numberOfLines:(NSUInteger)numberOfLines {
+       size = drawOrSizeTextConstrainedToSize(NO, self, attributeRunForFont(font), size, numberOfLines, lineBreakMode, UITextAlignmentLeft, YES);
+       return CGSizeMake(ceilf(size.width), ceilf(size.height));
+}
+
+- (CGSize)drawAtPoint:(CGPoint)point withZFont:(ZFont *)font {
+       return [self drawAtPoint:point forWidth:CGFLOAT_MAX withZFont:font lineBreakMode:UILineBreakModeClip];
+}
+
+- (CGSize)drawAtPoint:(CGPoint)point forWidth:(CGFloat)width withZFont:(ZFont *)font lineBreakMode:(UILineBreakMode)lineBreakMode {
+       return drawTextInRect((CGRect){ point, { width, CGFLOAT_MAX } }, self, attributeRunForFont(font), lineBreakMode, UITextAlignmentLeft, 1, YES);
+}
+
+- (CGSize)drawInRect:(CGRect)rect withZFont:(ZFont *)font {
+       return [self drawInRect:rect withZFont:font lineBreakMode:UILineBreakModeWordWrap];
+}
+
+- (CGSize)drawInRect:(CGRect)rect withZFont:(ZFont *)font lineBreakMode:(UILineBreakMode)lineBreakMode {
+       return [self drawInRect:rect withZFont:font lineBreakMode:lineBreakMode alignment:UITextAlignmentLeft];
+}
+
+- (CGSize)drawInRect:(CGRect)rect withZFont:(ZFont *)font lineBreakMode:(UILineBreakMode)lineBreakMode
+                  alignment:(UITextAlignment)alignment {
+       return drawTextInRect(rect, self, attributeRunForFont(font), lineBreakMode, alignment, 0, YES);
+}
+
+- (CGSize)drawInRect:(CGRect)rect withZFont:(ZFont *)font lineBreakMode:(UILineBreakMode)lineBreakMode
+                  alignment:(UITextAlignment)alignment numberOfLines:(NSUInteger)numberOfLines {
+       return drawTextInRect(rect, self, attributeRunForFont(font), lineBreakMode, alignment, numberOfLines, YES);
+}
+@end
+
+@implementation ZAttributedString (ZAttributedStringDrawing)
+- (CGSize)size {
+       CGSize size = drawOrSizeTextConstrainedToSize(NO, self.string, self.attributes, CGSizeMake(CGFLOAT_MAX, CGFLOAT_MAX), 1,
+                                                                                                 UILineBreakModeClip, UITextAlignmentLeft, NO);
+       return CGSizeMake(ceilf(size.width), ceilf(size.height));
+}
+
+- (CGSize)sizeConstrainedToSize:(CGSize)size {
+       return [self sizeConstrainedToSize:size lineBreakMode:UILineBreakModeWordWrap];
+}
+
+- (CGSize)sizeConstrainedToSize:(CGSize)size lineBreakMode:(UILineBreakMode)lineBreakMode {
+       size = drawOrSizeTextConstrainedToSize(NO, self.string, self.attributes, size, 0, lineBreakMode, UITextAlignmentLeft, NO);
+       return CGSizeMake(ceilf(size.width), ceilf(size.height));
+}
+
+- (CGSize)sizeConstrainedToSize:(CGSize)size lineBreakMode:(UILineBreakMode)lineBreakMode
+                                 numberOfLines:(NSUInteger)numberOfLines {
+       size = drawOrSizeTextConstrainedToSize(NO, self.string, self.attributes, size, numberOfLines, lineBreakMode, UITextAlignmentLeft, NO);
+       return CGSizeMake(ceilf(size.width), ceilf(size.height));
+}
+
+- (CGSize)drawAtPoint:(CGPoint)point {
+       return [self drawAtPoint:point forWidth:CGFLOAT_MAX lineBreakMode:UILineBreakModeClip];
+}
+
+- (CGSize)drawAtPoint:(CGPoint)point forWidth:(CGFloat)width lineBreakMode:(UILineBreakMode)lineBreakMode {
+       return drawTextInRect((CGRect){ point, { width, CGFLOAT_MAX } }, self.string, self.attributes, lineBreakMode, UITextAlignmentLeft, 1, NO);
+}
+
+- (CGSize)drawInRect:(CGRect)rect {
+       return [self drawInRect:rect withLineBreakMode:UILineBreakModeWordWrap];
+}
+
+- (CGSize)drawInRect:(CGRect)rect withLineBreakMode:(UILineBreakMode)lineBreakMode {
+       return [self drawInRect:rect withLineBreakMode:lineBreakMode alignment:UITextAlignmentLeft];
+}
+
+- (CGSize)drawInRect:(CGRect)rect withLineBreakMode:(UILineBreakMode)lineBreakMode alignment:(UITextAlignment)alignment {
+       return drawTextInRect(rect, self.string, self.attributes, lineBreakMode, alignment, 0, NO);
+}
+
+- (CGSize)drawInRect:(CGRect)rect withLineBreakMode:(UILineBreakMode)lineBreakMode alignment:(UITextAlignment)alignment
+          numberOfLines:(NSUInteger)numberOfLines {
+       return drawTextInRect(rect, self.string, self.attributes, lineBreakMode, alignment, numberOfLines, NO);
+}
+@end
diff --git a/libs/FontLabel/FontManager.h b/libs/FontLabel/FontManager.h
new file mode 100644 (file)
index 0000000..1592b8a
--- /dev/null
@@ -0,0 +1,85 @@
+//
+//  FontManager.h
+//  FontLabel
+//
+//  Created by Kevin Ballard on 5/5/09.
+//  Copyright © 2009 Zynga Game Networks
+//
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#import <Foundation/Foundation.h>
+#import <CoreGraphics/CoreGraphics.h>
+
+@class ZFont;
+
+@interface FontManager : NSObject {
+       CFMutableDictionaryRef fonts;
+       NSMutableDictionary *urls;
+}
++ (FontManager *)sharedManager;
+/*!
+    @method     
+    @abstract   Loads a TTF font from the main bundle
+       @param filename The name of the font file to load (with or without extension).
+       @return YES if the font was loaded, NO if an error occurred
+    @discussion If the font has already been loaded, this method does nothing and returns YES.
+                               This method first attempts to load the font by appending .ttf to the filename.
+                               If that file does not exist, it tries the filename exactly as given.
+*/
+- (BOOL)loadFont:(NSString *)filename;
+/*!
+       @method
+       @abstract       Loads a font from the given file URL
+       @param url A file URL that points to a font file
+       @return YES if the font was loaded, NO if an error occurred
+       @discussion If the font has already been loaded, this method does nothing and returns YES.
+*/
+- (BOOL)loadFontURL:(NSURL *)url;
+/*!
+    @method     
+    @abstract   Returns the loaded font with the given filename
+       @param filename The name of the font file that was given to -loadFont:
+       @return A CGFontRef, or NULL if the specified font cannot be found
+    @discussion If the font has not been loaded yet, -loadFont: will be
+                called with the given name first.
+*/
+- (CGFontRef)fontWithName:(NSString *)filename __AVAILABILITY_INTERNAL_DEPRECATED;
+/*!
+       @method
+       @abstract       Returns a ZFont object corresponding to the loaded font with the given filename and point size
+       @param filename The name of the font file that was given to -loadFont:
+       @param pointSize The point size of the font
+       @return A ZFont, or NULL if the specified font cannot be found
+       @discussion If the font has not been loaded yet, -loadFont: will be
+                               called with the given name first.
+*/
+- (ZFont *)zFontWithName:(NSString *)filename pointSize:(CGFloat)pointSize;
+/*!
+       @method
+       @abstract       Returns a ZFont object corresponding to the loaded font with the given file URL and point size
+       @param url A file URL that points to a font file
+       @param pointSize The point size of the font
+       @return A ZFont, or NULL if the specified font cannot be loaded
+       @discussion If the font has not been loaded yet, -loadFontURL: will be called with the given URL first.
+*/
+- (ZFont *)zFontWithURL:(NSURL *)url pointSize:(CGFloat)pointSize;
+/*!
+       @method
+       @abstract   Returns a CFArrayRef of all loaded CGFont objects
+       @return A CFArrayRef of all loaded CGFont objects
+       @description You are responsible for releasing the CFArrayRef
+*/
+- (CFArrayRef)copyAllFonts;
+@end
diff --git a/libs/FontLabel/FontManager.m b/libs/FontLabel/FontManager.m
new file mode 100644 (file)
index 0000000..12eac2d
--- /dev/null
@@ -0,0 +1,123 @@
+//
+//  FontManager.m
+//  FontLabel
+//
+//  Created by Kevin Ballard on 5/5/09.
+//  Copyright © 2009 Zynga Game Networks
+//
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#import "FontManager.h"
+#import "ZFont.h"
+
+static FontManager *sharedFontManager = nil;
+
+@implementation FontManager
++ (FontManager *)sharedManager {
+       @synchronized(self) {
+               if (sharedFontManager == nil) {
+                       sharedFontManager = [[self alloc] init];
+               }
+       }
+       return sharedFontManager;
+}
+
+- (id)init {
+       if ((self = [super init])) {
+               fonts = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
+               urls = [[NSMutableDictionary alloc] init];
+       }
+       return self;
+}
+
+- (BOOL)loadFont:(NSString *)filename {
+       NSString *fontPath = [[NSBundle mainBundle] pathForResource:filename ofType:@"ttf"];
+       if (fontPath == nil) {
+               fontPath = [[NSBundle mainBundle] pathForResource:filename ofType:nil];
+       }
+       if (fontPath == nil) return NO;
+       
+       NSURL *url = [NSURL fileURLWithPath:fontPath];
+       if ([self loadFontURL:url]) {
+               [urls setObject:url forKey:filename];
+               return YES;
+       }
+       return NO;
+}
+
+- (BOOL)loadFontURL:(NSURL *)url {
+       CGDataProviderRef fontDataProvider = CGDataProviderCreateWithURL((CFURLRef)url);
+       if (fontDataProvider == NULL) return NO;
+       CGFontRef newFont = CGFontCreateWithDataProvider(fontDataProvider); 
+       CGDataProviderRelease(fontDataProvider); 
+       if (newFont == NULL) return NO;
+       
+       CFDictionarySetValue(fonts, url, newFont);
+       CGFontRelease(newFont);
+       return YES;
+}
+
+- (CGFontRef)fontWithName:(NSString *)filename {
+       CGFontRef font = NULL;
+       NSURL *url = [urls objectForKey:filename];
+       if (url == nil && [self loadFont:filename]) {
+               url = [urls objectForKey:filename];
+       }
+       if (url != nil) {
+               font = (CGFontRef)CFDictionaryGetValue(fonts, url);
+       }
+       return font;
+}
+
+- (ZFont *)zFontWithName:(NSString *)filename pointSize:(CGFloat)pointSize {
+       NSURL *url = [urls objectForKey:filename];
+       if (url == nil && [self loadFont:filename]) {
+               url = [urls objectForKey:filename];
+       }
+       if (url != nil) {
+               CGFontRef cgFont = (CGFontRef)CFDictionaryGetValue(fonts, url);
+               if (cgFont != NULL) {
+                       return [ZFont fontWithCGFont:cgFont size:pointSize];
+               }
+       }
+       return nil;
+}
+
+- (ZFont *)zFontWithURL:(NSURL *)url pointSize:(CGFloat)pointSize {
+       CGFontRef cgFont = (CGFontRef)CFDictionaryGetValue(fonts, url);
+       if (cgFont == NULL && [self loadFontURL:url]) {
+               cgFont = (CGFontRef)CFDictionaryGetValue(fonts, url);
+       }
+       if (cgFont != NULL) {
+               return [ZFont fontWithCGFont:cgFont size:pointSize];
+       }
+       return nil;
+}
+
+- (CFArrayRef)copyAllFonts {
+       CFIndex count = CFDictionaryGetCount(fonts);
+       CGFontRef *values = (CGFontRef *)malloc(sizeof(CGFontRef) * count);
+       CFDictionaryGetKeysAndValues(fonts, NULL, (const void **)values);
+       CFArrayRef array = CFArrayCreate(NULL, (const void **)values, count, &kCFTypeArrayCallBacks);
+       free(values);
+       return array;
+}
+
+- (void)dealloc {
+       CFRelease(fonts);
+       [urls release];
+       [super dealloc];
+}
+@end
diff --git a/libs/FontLabel/ZAttributedString.h b/libs/FontLabel/ZAttributedString.h
new file mode 100644 (file)
index 0000000..e194c81
--- /dev/null
@@ -0,0 +1,77 @@
+//
+//  ZAttributedString.h
+//  FontLabel
+//
+//  Created by Kevin Ballard on 9/22/09.
+//  Copyright 2009 Zynga Game Networks. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+
+#if NS_BLOCKS_AVAILABLE
+#define Z_BLOCKS 1
+#else
+// set this to 1 if you are using PLBlocks
+#define Z_BLOCKS 0
+#endif
+
+#if Z_BLOCKS
+enum {
+       ZAttributedStringEnumerationReverse = (1UL << 1),
+       ZAttributedStringEnumerationLongestEffectiveRangeNotRequired = (1UL << 20)
+};
+typedef NSUInteger ZAttributedStringEnumerationOptions;
+#endif
+
+@interface ZAttributedString : NSObject <NSCoding, NSCopying, NSMutableCopying> {
+       NSMutableString *_buffer;
+       NSMutableArray *_attributes;
+}
+@property (nonatomic, readonly) NSUInteger length;
+@property (nonatomic, readonly) NSString *string;
+- (id)initWithAttributedString:(ZAttributedString *)attr;
+- (id)initWithString:(NSString *)str;
+- (id)initWithString:(NSString *)str attributes:(NSDictionary *)attributes;
+- (id)attribute:(NSString *)attributeName atIndex:(NSUInteger)index effectiveRange:(NSRangePointer)aRange;
+- (id)attribute:(NSString *)attributeName atIndex:(NSUInteger)index longestEffectiveRange:(NSRangePointer)aRange inRange:(NSRange)rangeLimit;
+- (ZAttributedString *)attributedSubstringFromRange:(NSRange)aRange;
+- (NSDictionary *)attributesAtIndex:(NSUInteger)index effectiveRange:(NSRangePointer)aRange;
+- (NSDictionary *)attributesAtIndex:(NSUInteger)index longestEffectiveRange:(NSRangePointer)aRange inRange:(NSRange)rangeLimit;
+#if Z_BLOCKS
+- (void)enumerateAttribute:(NSString *)attrName inRange:(NSRange)enumerationRange options:(ZAttributedStringEnumerationOptions)opts
+                               usingBlock:(void (^)(id value, NSRange range, BOOL *stop))block;
+- (void)enumerateAttributesInRange:(NSRange)enumerationRange options:(ZAttributedStringEnumerationOptions)opts
+                                               usingBlock:(void (^)(NSDictionary *attrs, NSRange range, BOOL *stop))block;
+#endif
+- (BOOL)isEqualToAttributedString:(ZAttributedString *)otherString;
+@end
+
+@interface ZMutableAttributedString : ZAttributedString {
+}
+- (void)addAttribute:(NSString *)name value:(id)value range:(NSRange)range;
+- (void)addAttributes:(NSDictionary *)attributes range:(NSRange)range;
+- (void)appendAttributedString:(ZAttributedString *)str;
+- (void)deleteCharactersInRange:(NSRange)range;
+- (void)insertAttributedString:(ZAttributedString *)str atIndex:(NSUInteger)idx;
+- (void)removeAttribute:(NSString *)name range:(NSRange)range;
+- (void)replaceCharactersInRange:(NSRange)range withAttributedString:(ZAttributedString *)str;
+- (void)replaceCharactersInRange:(NSRange)range withString:(NSString *)str;
+- (void)setAttributedString:(ZAttributedString *)str;
+- (void)setAttributes:(NSDictionary *)attributes range:(NSRange)range;
+@end
+
+extern NSString * const ZFontAttributeName;
+extern NSString * const ZForegroundColorAttributeName;
+extern NSString * const ZBackgroundColorAttributeName;
+extern NSString * const ZUnderlineStyleAttributeName;
+
+enum {
+       ZUnderlineStyleNone = 0x00,
+       ZUnderlineStyleSingle = 0x01
+};
+#define ZUnderlineStyleMask 0x00FF
+
+enum {
+       ZUnderlinePatternSolid = 0x0000
+};
+#define ZUnderlinePatternMask 0xFF00
diff --git a/libs/FontLabel/ZAttributedString.m b/libs/FontLabel/ZAttributedString.m
new file mode 100644 (file)
index 0000000..79f0323
--- /dev/null
@@ -0,0 +1,596 @@
+//
+//  ZAttributedString.m
+//  FontLabel
+//
+//  Created by Kevin Ballard on 9/22/09.
+//  Copyright 2009 Zynga Game Networks. All rights reserved.
+//
+
+#import "ZAttributedString.h"
+#import "ZAttributedStringPrivate.h"
+
+@interface ZAttributedString ()
+- (NSUInteger)indexOfEffectiveAttributeRunForIndex:(NSUInteger)index;
+- (NSDictionary *)attributesAtIndex:(NSUInteger)index effectiveRange:(NSRangePointer)aRange uniquingOnName:(NSString *)attributeName;
+- (NSDictionary *)attributesAtIndex:(NSUInteger)index longestEffectiveRange:(NSRangePointer)aRange
+                                                       inRange:(NSRange)rangeLimit uniquingOnName:(NSString *)attributeName;
+@end
+
+@interface ZAttributedString ()
+@property (nonatomic, readonly) NSArray *attributes;
+@end
+
+@implementation ZAttributedString
+@synthesize string = _buffer;
+@synthesize attributes = _attributes;
+
+- (id)initWithAttributedString:(ZAttributedString *)attr {
+       NSParameterAssert(attr != nil);
+       if ((self = [super init])) {
+               _buffer = [attr->_buffer mutableCopy];
+               _attributes = [[NSMutableArray alloc] initWithArray:attr->_attributes copyItems:YES];
+       }
+       return self;
+}
+
+- (id)initWithString:(NSString *)str {
+       return [self initWithString:str attributes:nil];
+}
+
+- (id)initWithString:(NSString *)str attributes:(NSDictionary *)attributes {
+       if ((self = [super init])) {
+               _buffer = [str mutableCopy];
+               _attributes = [[NSMutableArray alloc] initWithObjects:[ZAttributeRun attributeRunWithIndex:0 attributes:attributes], nil];
+       }
+       return self;
+}
+
+- (id)init {
+       return [self initWithString:@"" attributes:nil];
+}
+
+- (id)initWithCoder:(NSCoder *)decoder {
+       if ((self = [super init])) {
+               _buffer = [[decoder decodeObjectForKey:@"buffer"] mutableCopy];
+               _attributes = [[decoder decodeObjectForKey:@"attributes"] mutableCopy];
+       }
+       return self;
+}
+
+- (void)encodeWithCoder:(NSCoder *)aCoder {
+       [aCoder encodeObject:_buffer forKey:@"buffer"];
+       [aCoder encodeObject:_attributes forKey:@"attributes"];
+}
+
+- (id)copyWithZone:(NSZone *)zone {
+       return [self retain];
+}
+
+- (id)mutableCopyWithZone:(NSZone *)zone {
+       return [(ZMutableAttributedString *)[ZMutableAttributedString allocWithZone:zone] initWithAttributedString:self];
+}
+
+- (NSUInteger)length {
+       return [_buffer length];
+}
+
+- (NSString *)description {
+       NSMutableArray *components = [NSMutableArray arrayWithCapacity:[_attributes count]*2];
+       NSRange range = NSMakeRange(0, 0);
+       for (NSUInteger i = 0; i <= [_attributes count]; i++) {
+               range.location = NSMaxRange(range);
+               ZAttributeRun *run;
+               if (i < [_attributes count]) {
+                       run = [_attributes objectAtIndex:i];
+                       range.length = run.index - range.location;
+               } else {
+                       run = nil;
+                       range.length = [_buffer length] - range.location;
+               }
+               if (range.length > 0) {
+                       [components addObject:[NSString stringWithFormat:@"\"%@\"", [_buffer substringWithRange:range]]];
+               }
+               if (run != nil) {
+                       NSMutableArray *attrDesc = [NSMutableArray arrayWithCapacity:[run.attributes count]];
+                       for (id key in run.attributes) {
+                               [attrDesc addObject:[NSString stringWithFormat:@"%@: %@", key, [run.attributes objectForKey:key]]];
+                       }
+                       [components addObject:[NSString stringWithFormat:@"{%@}", [attrDesc componentsJoinedByString:@", "]]];
+               }
+       }
+       return [NSString stringWithFormat:@"%@", [components componentsJoinedByString:@" "]];
+}
+
+- (id)attribute:(NSString *)attributeName atIndex:(NSUInteger)index effectiveRange:(NSRangePointer)aRange {
+       NSParameterAssert(attributeName != nil);
+       return [[self attributesAtIndex:index effectiveRange:aRange uniquingOnName:attributeName] objectForKey:attributeName];
+}
+
+- (id)attribute:(NSString *)attributeName atIndex:(NSUInteger)index longestEffectiveRange:(NSRangePointer)aRange inRange:(NSRange)rangeLimit {
+       NSParameterAssert(attributeName != nil);
+       return [[self attributesAtIndex:index longestEffectiveRange:aRange inRange:rangeLimit uniquingOnName:attributeName] objectForKey:attributeName];
+}
+
+- (ZAttributedString *)attributedSubstringFromRange:(NSRange)aRange {
+       if (NSMaxRange(aRange) > [_buffer length]) {
+               @throw [NSException exceptionWithName:NSRangeException reason:@"range was outisde of the attributed string" userInfo:nil];
+       }
+       ZMutableAttributedString *newStr = [self mutableCopy];
+       if (aRange.location > 0) {
+               [newStr deleteCharactersInRange:NSMakeRange(0, aRange.location)];
+       }
+       if (NSMaxRange(aRange) < [_buffer length]) {
+               [newStr deleteCharactersInRange:NSMakeRange(aRange.length, [_buffer length] - NSMaxRange(aRange))];
+       }
+       return [newStr autorelease];
+}
+
+- (NSDictionary *)attributesAtIndex:(NSUInteger)index effectiveRange:(NSRangePointer)aRange {
+       return [NSDictionary dictionaryWithDictionary:[self attributesAtIndex:index effectiveRange:aRange uniquingOnName:nil]];
+}
+
+- (NSDictionary *)attributesAtIndex:(NSUInteger)index longestEffectiveRange:(NSRangePointer)aRange inRange:(NSRange)rangeLimit {
+       return [NSDictionary dictionaryWithDictionary:[self attributesAtIndex:index longestEffectiveRange:aRange inRange:rangeLimit uniquingOnName:nil]];
+}
+
+#if Z_BLOCKS
+// Warning: this code has not been tested. The only guarantee is that it compiles.
+- (void)enumerateAttribute:(NSString *)attrName inRange:(NSRange)enumerationRange options:(ZAttributedStringEnumerationOptions)opts
+                               usingBlock:(void (^)(id, NSRange, BOOL*))block {
+       if (opts & ZAttributedStringEnumerationLongestEffectiveRangeNotRequired) {
+               [self enumerateAttributesInRange:enumerationRange options:opts usingBlock:^(NSDictionary *attrs, NSRange range, BOOL *stop) {
+                       id value = [attrs objectForKey:attrName];
+                       if (value != nil) {
+                               block(value, range, stop);
+                       }
+               }];
+       } else {
+               __block id oldValue = nil;
+               __block NSRange effectiveRange = NSMakeRange(0, 0);
+               [self enumerateAttributesInRange:enumerationRange options:opts usingBlock:^(NSDictionary *attrs, NSRange range, BOOL *stop) {
+                       id value = [attrs objectForKey:attrName];
+                       if (oldValue == nil) {
+                               oldValue = value;
+                               effectiveRange = range;
+                       } else if (value != nil && [oldValue isEqual:value]) {
+                               // combine the attributes
+                               effectiveRange = NSUnionRange(effectiveRange, range);
+                       } else {
+                               BOOL innerStop = NO;
+                               block(oldValue, effectiveRange, &innerStop);
+                               if (innerStop) {
+                                       *stop = YES;
+                                       oldValue = nil;
+                               } else {
+                                       oldValue = value;
+                               }
+                       }
+               }];
+               if (oldValue != nil) {
+                       BOOL innerStop = NO; // necessary for the block, but unused
+                       block(oldValue, effectiveRange, &innerStop);
+               }
+       }
+}
+
+- (void)enumerateAttributesInRange:(NSRange)enumerationRange options:(ZAttributedStringEnumerationOptions)opts
+                                               usingBlock:(void (^)(NSDictionary*, NSRange, BOOL*))block {
+       // copy the attributes so we can mutate the string if necessary during enumeration
+       // also clip the array during copy to only the subarray of attributes that cover the requested range
+       NSArray *attrs;
+       if (NSEqualRanges(enumerationRange, NSMakeRange(0, 0))) {
+               attrs = [NSArray arrayWithArray:_attributes];
+       } else {
+               // in this binary search, last is the first run after the range
+               NSUInteger first = 0, last = [_attributes count];
+               while (last > first+1) {
+                       NSUInteger pivot = (last + first) / 2;
+                       ZAttributeRun *run = [_attributes objectAtIndex:pivot];
+                       if (run.index < enumerationRange.location) {
+                               first = pivot;
+                       } else if (run.index >= NSMaxRange(enumerationRange)) {
+                               last = pivot;
+                       }
+               }
+               attrs = [_attributes subarrayWithRange:NSMakeRange(first, last-first)];
+       }
+       if (opts & ZAttributedStringEnumerationReverse) {
+               NSUInteger end = [_buffer length];
+               for (ZAttributeRun *run in [attrs reverseObjectEnumerator]) {
+                       BOOL stop = NO;
+                       NSUInteger start = run.index;
+                       // clip to enumerationRange
+                       start = MAX(start, enumerationRange.location);
+                       end = MIN(end, NSMaxRange(enumerationRange));
+                       block(run.attributes, NSMakeRange(start, end - start), &stop);
+                       if (stop) break;
+                       end = run.index;
+               }
+       } else {
+               NSUInteger start = 0;
+               ZAttributeRun *run = [attrs objectAtIndex:0];
+               NSInteger offset = 0;
+               NSInteger oldLength = [_buffer length];
+               for (NSUInteger i = 1;;i++) {
+                       NSUInteger end;
+                       if (i >= [attrs count]) {
+                               end = oldLength;
+                       } else {
+                               end = [[attrs objectAtIndex:i] index];
+                       }
+                       BOOL stop = NO;
+                       NSUInteger clippedStart = MAX(start, enumerationRange.location);
+                       NSUInteger clippedEnd = MIN(end, NSMaxRange(enumerationRange));
+                       block(run.attributes, NSMakeRange(clippedStart + offset, clippedEnd - start), &stop);
+                       if (stop || i >= [attrs count]) break;
+                       start = end;
+                       NSUInteger newLength = [_buffer length];
+                       offset += (newLength - oldLength);
+                       oldLength = newLength;
+               }
+       }
+}
+#endif
+
+- (BOOL)isEqualToAttributedString:(ZAttributedString *)otherString {
+       return ([_buffer isEqualToString:otherString->_buffer] && [_attributes isEqualToArray:otherString->_attributes]);
+}
+
+- (BOOL)isEqual:(id)object {
+       return [object isKindOfClass:[ZAttributedString class]] && [self isEqualToAttributedString:(ZAttributedString *)object];
+}
+
+#pragma mark -
+
+- (NSUInteger)indexOfEffectiveAttributeRunForIndex:(NSUInteger)index {
+       NSUInteger first = 0, last = [_attributes count];
+       while (last > first + 1) {
+               NSUInteger pivot = (last + first) / 2;
+               ZAttributeRun *run = [_attributes objectAtIndex:pivot];
+               if (run.index > index) {
+                       last = pivot;
+               } else if (run.index < index) {
+                       first = pivot;
+               } else {
+                       first = pivot;
+                       break;
+               }
+       }
+       return first;
+}
+
+- (NSDictionary *)attributesAtIndex:(NSUInteger)index effectiveRange:(NSRangePointer)aRange uniquingOnName:(NSString *)attributeName {
+       if (index >= [_buffer length]) {
+               @throw [NSException exceptionWithName:NSRangeException reason:@"index beyond range of attributed string" userInfo:nil];
+       }
+       NSUInteger runIndex = [self indexOfEffectiveAttributeRunForIndex:index];
+       ZAttributeRun *run = [_attributes objectAtIndex:runIndex];
+       if (aRange != NULL) {
+               aRange->location = run.index;
+               runIndex++;
+               if (runIndex < [_attributes count]) {
+                       aRange->length = [[_attributes objectAtIndex:runIndex] index] - aRange->location;
+               } else {
+                       aRange->length = [_buffer length] - aRange->location;
+               }
+       }
+       return run.attributes;
+}
+- (NSDictionary *)attributesAtIndex:(NSUInteger)index longestEffectiveRange:(NSRangePointer)aRange
+                                                       inRange:(NSRange)rangeLimit uniquingOnName:(NSString *)attributeName {
+       if (index >= [_buffer length]) {
+               @throw [NSException exceptionWithName:NSRangeException reason:@"index beyond range of attributed string" userInfo:nil];
+       } else if (NSMaxRange(rangeLimit) > [_buffer length]) {
+               @throw [NSException exceptionWithName:NSRangeException reason:@"rangeLimit beyond range of attributed string" userInfo:nil];
+       }
+       NSUInteger runIndex = [self indexOfEffectiveAttributeRunForIndex:index];
+       ZAttributeRun *run = [_attributes objectAtIndex:runIndex];
+       if (aRange != NULL) {
+               if (attributeName != nil) {
+                       id value = [run.attributes objectForKey:attributeName];
+                       NSUInteger endRunIndex = runIndex+1;
+                       runIndex--;
+                       // search backwards
+                       while (1) {
+                               if (run.index <= rangeLimit.location) {
+                                       break;
+                               }
+                               ZAttributeRun *prevRun = [_attributes objectAtIndex:runIndex];
+                               id prevValue = [prevRun.attributes objectForKey:attributeName];
+                               if (prevValue == value || (value != nil && [prevValue isEqual:value])) {
+                                       runIndex--;
+                                       run = prevRun;
+                               } else {
+                                       break;
+                               }
+                       }
+                       // search forwards
+                       ZAttributeRun *endRun = nil;
+                       while (endRunIndex < [_attributes count]) {
+                               ZAttributeRun *nextRun = [_attributes objectAtIndex:endRunIndex];
+                               if (nextRun.index >= NSMaxRange(rangeLimit)) {
+                                       endRun = nextRun;
+                                       break;
+                               }
+                               id nextValue = [nextRun.attributes objectForKey:attributeName];
+                               if (nextValue == value || (value != nil && [nextValue isEqual:value])) {
+                                       endRunIndex++;
+                               } else {
+                                       endRun = nextRun;
+                                       break;
+                               }
+                       }
+                       aRange->location = MAX(run.index, rangeLimit.location);
+                       aRange->length = MIN((endRun ? endRun.index : [_buffer length]), NSMaxRange(rangeLimit)) - aRange->location;
+               } else {
+                       // with no attribute name, we don't need to do any real searching,
+                       // as we already guarantee each run has unique attributes.
+                       // just make sure to clip the range to the rangeLimit
+                       aRange->location = MAX(run.index, rangeLimit.location);
+                       ZAttributeRun *endRun = (runIndex+1 < [_attributes count] ? [_attributes objectAtIndex:runIndex+1] : nil);
+                       aRange->length = MIN((endRun ? endRun.index : [_buffer length]), NSMaxRange(rangeLimit)) - aRange->location;
+               }
+       }
+       return run.attributes;
+}
+
+- (void)dealloc {
+       [_buffer release];
+       [_attributes release];
+       [super dealloc];
+}
+@end
+
+@interface ZMutableAttributedString ()
+- (void)cleanupAttributesInRange:(NSRange)range;
+- (NSRange)rangeOfAttributeRunsForRange:(NSRange)range;
+- (void)offsetRunsInRange:(NSRange )range byOffset:(NSInteger)offset;
+@end
+
+@implementation ZMutableAttributedString
+- (id)copyWithZone:(NSZone *)zone {
+       return [(ZAttributedString *)[ZAttributedString allocWithZone:zone] initWithAttributedString:self];
+}
+
+- (void)addAttribute:(NSString *)name value:(id)value range:(NSRange)range {
+       range = [self rangeOfAttributeRunsForRange:range];
+       for (ZAttributeRun *run in [_attributes subarrayWithRange:range]) {
+               [run.attributes setObject:value forKey:name];
+       }
+       [self cleanupAttributesInRange:range];
+}
+
+- (void)addAttributes:(NSDictionary *)attributes range:(NSRange)range {
+       range = [self rangeOfAttributeRunsForRange:range];
+       for (ZAttributeRun *run in [_attributes subarrayWithRange:range]) {
+               [run.attributes addEntriesFromDictionary:attributes];
+       }
+       [self cleanupAttributesInRange:range];
+}
+
+- (void)appendAttributedString:(ZAttributedString *)str {
+       [self insertAttributedString:str atIndex:[_buffer length]];
+}
+
+- (void)deleteCharactersInRange:(NSRange)range {
+       NSRange runRange = [self rangeOfAttributeRunsForRange:range];
+       [_buffer replaceCharactersInRange:range withString:@""];
+       [_attributes removeObjectsInRange:runRange];
+       for (NSUInteger i = runRange.location; i < [_attributes count]; i++) {
+               ZAttributeRun *run = [_attributes objectAtIndex:i];
+               ZAttributeRun *newRun = [[ZAttributeRun alloc] initWithIndex:(run.index - range.length) attributes:run.attributes];
+               [_attributes replaceObjectAtIndex:i withObject:newRun];
+               [newRun release];
+       }
+       [self cleanupAttributesInRange:NSMakeRange(runRange.location, 0)];
+}
+
+- (void)insertAttributedString:(ZAttributedString *)str atIndex:(NSUInteger)idx {
+       [self replaceCharactersInRange:NSMakeRange(idx, 0) withAttributedString:str];
+}
+
+- (void)removeAttribute:(NSString *)name range:(NSRange)range {
+       range = [self rangeOfAttributeRunsForRange:range];
+       for (ZAttributeRun *run in [_attributes subarrayWithRange:range]) {
+               [run.attributes removeObjectForKey:name];
+       }
+       [self cleanupAttributesInRange:range];
+}
+
+- (void)replaceCharactersInRange:(NSRange)range withAttributedString:(ZAttributedString *)str {
+       NSRange replaceRange = [self rangeOfAttributeRunsForRange:range];
+       NSInteger offset = [str->_buffer length] - range.length;
+       [_buffer replaceCharactersInRange:range withString:str->_buffer];
+       [_attributes replaceObjectsInRange:replaceRange withObjectsFromArray:str->_attributes];
+       NSRange newRange = NSMakeRange(replaceRange.location, [str->_attributes count]);
+       [self offsetRunsInRange:newRange byOffset:range.location];
+       [self offsetRunsInRange:NSMakeRange(NSMaxRange(newRange), [_attributes count] - NSMaxRange(newRange)) byOffset:offset];
+       [self cleanupAttributesInRange:NSMakeRange(newRange.location, 0)];
+       [self cleanupAttributesInRange:NSMakeRange(NSMaxRange(newRange), 0)];
+}
+
+- (void)replaceCharactersInRange:(NSRange)range withString:(NSString *)str {
+       [self replaceCharactersInRange:range withAttributedString:[[[ZAttributedString alloc] initWithString:str] autorelease]];
+}
+
+- (void)setAttributedString:(ZAttributedString *)str {
+       [_buffer release], _buffer = [str->_buffer mutableCopy];
+       [_attributes release], _attributes = [str->_attributes mutableCopy];
+}
+
+- (void)setAttributes:(NSDictionary *)attributes range:(NSRange)range {
+       range = [self rangeOfAttributeRunsForRange:range];
+       for (ZAttributeRun *run in [_attributes subarrayWithRange:range]) {
+               [run.attributes setDictionary:attributes];
+       }
+       [self cleanupAttributesInRange:range];
+}
+
+#pragma mark -
+
+// splits the existing runs to provide one or more new runs for the given range
+- (NSRange)rangeOfAttributeRunsForRange:(NSRange)range {
+       NSParameterAssert(NSMaxRange(range) <= [_buffer length]);
+       
+       // find (or create) the first run
+       NSUInteger first = 0;
+       ZAttributeRun *lastRun = nil;
+       for (;;first++) {
+               if (first >= [_attributes count]) {
+                       // we didn't find a run
+                       first = [_attributes count];
+                       ZAttributeRun *newRun = [[ZAttributeRun alloc] initWithIndex:range.location attributes:lastRun.attributes];
+                       [_attributes addObject:newRun];
+                       [newRun release];
+                       break;
+               }
+               ZAttributeRun *run = [_attributes objectAtIndex:first];
+               if (run.index == range.location) {
+                       break;
+               } else if (run.index > range.location) {
+                       ZAttributeRun *newRun = [[ZAttributeRun alloc] initWithIndex:range.location attributes:lastRun.attributes];
+                       [_attributes insertObject:newRun atIndex:first];
+                       [newRun release];
+                       break;
+               }
+               lastRun = run;
+       }
+       
+       if (((ZAttributeRun *)[_attributes lastObject]).index < NSMaxRange(range)) {
+               NSRange subrange = NSMakeRange(first, [_attributes count] - first);
+               if (NSMaxRange(range) < [_buffer length]) {
+                       ZAttributeRun *newRun = [[ZAttributeRun alloc] initWithIndex:NSMaxRange(range) attributes:[[_attributes lastObject] attributes]];
+                       [_attributes addObject:newRun];
+                       [newRun release];
+               }
+               return subrange;
+       } else {
+               // find the last run within and the first run after the range
+               NSUInteger lastIn = first, firstAfter = [_attributes count]-1;
+               while (firstAfter > lastIn + 1) {
+                       NSUInteger idx = (firstAfter + lastIn) / 2;
+                       ZAttributeRun *run = [_attributes objectAtIndex:idx];
+                       if (run.index < range.location) {
+                               lastIn = idx;
+                       } else if (run.index > range.location) {
+                               firstAfter = idx;
+                       } else {
+                               // this is definitively the first run after the range
+                               firstAfter = idx;
+                               break;
+                       }
+               }
+               if ([[_attributes objectAtIndex:firstAfter] index] > NSMaxRange(range)) {
+                       // the first after is too far after, insert another run!
+                       ZAttributeRun *newRun = [[ZAttributeRun alloc] initWithIndex:NSMaxRange(range)
+                                                                                                                         attributes:[[_attributes objectAtIndex:firstAfter-1] attributes]];
+                       [_attributes insertObject:newRun atIndex:firstAfter];
+                       [newRun release];
+               }
+               return NSMakeRange(lastIn, firstAfter - lastIn);
+       }
+}
+
+- (void)cleanupAttributesInRange:(NSRange)range {
+       // expand the range to include one surrounding attribute on each side
+       if (range.location > 0) {
+               range.location -= 1;
+               range.length += 1;
+       }
+       if (NSMaxRange(range) < [_attributes count]) {
+               range.length += 1;
+       } else {
+               // make sure the range is capped to the attributes count
+               range.length = [_attributes count] - range.location;
+       }
+       if (range.length == 0) return;
+       ZAttributeRun *lastRun = [_attributes objectAtIndex:range.location];
+       for (NSUInteger i = range.location+1; i < NSMaxRange(range);) {
+               ZAttributeRun *run = [_attributes objectAtIndex:i];
+               if ([lastRun.attributes isEqualToDictionary:run.attributes]) {
+                       [_attributes removeObjectAtIndex:i];
+                       range.length -= 1;
+               } else {
+                       lastRun = run;
+                       i++;
+               }
+       }
+}
+
+- (void)offsetRunsInRange:(NSRange)range byOffset:(NSInteger)offset {
+       for (NSUInteger i = range.location; i < NSMaxRange(range); i++) {
+               ZAttributeRun *run = [_attributes objectAtIndex:i];
+               ZAttributeRun *newRun = [[ZAttributeRun alloc] initWithIndex:run.index + offset attributes:run.attributes];
+               [_attributes replaceObjectAtIndex:i withObject:newRun];
+               [newRun release];
+       }
+}
+@end
+
+@implementation ZAttributeRun
+@synthesize index = _index;
+@synthesize attributes = _attributes;
+
++ (id)attributeRunWithIndex:(NSUInteger)idx attributes:(NSDictionary *)attrs {
+       return [[[self alloc] initWithIndex:idx attributes:attrs] autorelease];
+}
+
+- (id)initWithIndex:(NSUInteger)idx attributes:(NSDictionary *)attrs {
+       NSParameterAssert(idx >= 0);
+       if ((self = [super init])) {
+               _index = idx;
+               if (attrs == nil) {
+                       _attributes = [[NSMutableDictionary alloc] init];
+               } else {
+                       _attributes = [attrs mutableCopy];
+               }
+       }
+       return self;
+}
+
+- (id)initWithCoder:(NSCoder *)decoder {
+       if ((self = [super init])) {
+               _index = [[decoder decodeObjectForKey:@"index"] unsignedIntegerValue];
+               _attributes = [[decoder decodeObjectForKey:@"attributes"] mutableCopy];
+       }
+       return self;
+}
+
+- (id)init {
+       return [self initWithIndex:0 attributes:[NSDictionary dictionary]];
+}
+
+- (id)copyWithZone:(NSZone *)zone {
+       return [[ZAttributeRun allocWithZone:zone] initWithIndex:_index attributes:_attributes];
+}
+
+- (void)encodeWithCoder:(NSCoder *)aCoder {
+       [aCoder encodeObject:[NSNumber numberWithUnsignedInteger:_index] forKey:@"index"];
+       [aCoder encodeObject:_attributes forKey:@"attributes"];
+}
+
+- (NSString *)description {
+       NSMutableArray *components = [NSMutableArray arrayWithCapacity:[_attributes count]];
+       for (id key in _attributes) {
+               [components addObject:[NSString stringWithFormat:@"%@=%@", key, [_attributes objectForKey:key]]];
+       }
+       return [NSString stringWithFormat:@"<%@: %p index=%lu attributes={%@}>",
+                       NSStringFromClass([self class]), self, (unsigned long)_index, [components componentsJoinedByString:@" "]];
+}
+
+- (BOOL)isEqual:(id)object {
+       if (![object isKindOfClass:[ZAttributeRun class]]) return NO;
+       ZAttributeRun *other = (ZAttributeRun *)object;
+       return _index == other->_index && [_attributes isEqualToDictionary:other->_attributes];
+}
+
+- (void)dealloc {
+       [_attributes release];
+       [super dealloc];
+}
+@end
+
+NSString * const ZFontAttributeName = @"ZFontAttributeName";
+NSString * const ZForegroundColorAttributeName = @"ZForegroundColorAttributeName";
+NSString * const ZBackgroundColorAttributeName = @"ZBackgroundColorAttributeName";
+NSString * const ZUnderlineStyleAttributeName = @"ZUnderlineStyleAttributeName";
diff --git a/libs/FontLabel/ZAttributedStringPrivate.h b/libs/FontLabel/ZAttributedStringPrivate.h
new file mode 100644 (file)
index 0000000..1021d7b
--- /dev/null
@@ -0,0 +1,24 @@
+//
+//  ZAttributedStringPrivate.h
+//  FontLabel
+//
+//  Created by Kevin Ballard on 9/23/09.
+//  Copyright 2009 Zynga Game Networks. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+#import "ZAttributedString.h"
+
+@interface ZAttributeRun : NSObject <NSCopying, NSCoding> {
+       NSUInteger _index;
+       NSMutableDictionary *_attributes;
+}
+@property (nonatomic, readonly) NSUInteger index;
+@property (nonatomic, readonly) NSMutableDictionary *attributes;
++ (id)attributeRunWithIndex:(NSUInteger)idx attributes:(NSDictionary *)attrs;
+- (id)initWithIndex:(NSUInteger)idx attributes:(NSDictionary *)attrs;
+@end
+
+@interface ZAttributedString (ZAttributedStringPrivate)
+@property (nonatomic, readonly) NSArray *attributes;
+@end
diff --git a/libs/FontLabel/ZFont.h b/libs/FontLabel/ZFont.h
new file mode 100644 (file)
index 0000000..05ae823
--- /dev/null
@@ -0,0 +1,47 @@
+//
+//  ZFont.h
+//  FontLabel
+//
+//  Created by Kevin Ballard on 7/2/09.
+//  Copyright © 2009 Zynga Game Networks
+//
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#import <Foundation/Foundation.h>
+#import <UIKit/UIKit.h>
+
+@interface ZFont : NSObject {
+       CGFontRef _cgFont;
+       CGFloat _pointSize;
+       CGFloat _ratio;
+       NSString *_familyName;
+       NSString *_fontName;
+       NSString *_postScriptName;
+}
+@property (nonatomic, readonly) CGFontRef cgFont;
+@property (nonatomic, readonly) CGFloat pointSize;
+@property (nonatomic, readonly) CGFloat ascender;
+@property (nonatomic, readonly) CGFloat descender;
+@property (nonatomic, readonly) CGFloat leading;
+@property (nonatomic, readonly) CGFloat xHeight;
+@property (nonatomic, readonly) CGFloat capHeight;
+@property (nonatomic, readonly) NSString *familyName;
+@property (nonatomic, readonly) NSString *fontName;
+@property (nonatomic, readonly) NSString *postScriptName;
++ (ZFont *)fontWithCGFont:(CGFontRef)cgFont size:(CGFloat)fontSize;
++ (ZFont *)fontWithUIFont:(UIFont *)uiFont;
+- (id)initWithCGFont:(CGFontRef)cgFont size:(CGFloat)fontSize;
+- (ZFont *)fontWithSize:(CGFloat)fontSize;
+@end
diff --git a/libs/FontLabel/ZFont.m b/libs/FontLabel/ZFont.m
new file mode 100644 (file)
index 0000000..793b13a
--- /dev/null
@@ -0,0 +1,170 @@
+//
+//  ZFont.m
+//  FontLabel
+//
+//  Created by Kevin Ballard on 7/2/09.
+//  Copyright © 2009 Zynga Game Networks
+//
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#import "ZFont.h"
+
+@interface ZFont ()
+@property (nonatomic, readonly) CGFloat ratio;
+- (NSString *)copyNameTableEntryForID:(UInt16)nameID;
+@end
+
+@implementation ZFont
+@synthesize cgFont=_cgFont, pointSize=_pointSize, ratio=_ratio;
+
++ (ZFont *)fontWithCGFont:(CGFontRef)cgFont size:(CGFloat)fontSize {
+       return [[[self alloc] initWithCGFont:cgFont size:fontSize] autorelease];
+}
+
++ (ZFont *)fontWithUIFont:(UIFont *)uiFont {
+       NSParameterAssert(uiFont != nil);
+       CGFontRef cgFont = CGFontCreateWithFontName((CFStringRef)uiFont.fontName);
+       ZFont *zFont = [[self alloc] initWithCGFont:cgFont size:uiFont.pointSize];
+       CGFontRelease(cgFont);
+       return [zFont autorelease];
+}
+
+- (id)initWithCGFont:(CGFontRef)cgFont size:(CGFloat)fontSize {
+       if ((self = [super init])) {
+               _cgFont = CGFontRetain(cgFont);
+               _pointSize = fontSize;
+               _ratio = fontSize/CGFontGetUnitsPerEm(cgFont);
+       }
+       return self;
+}
+
+- (id)init {
+       NSAssert(NO, @"-init is not valid for ZFont");
+       return nil;
+}
+
+- (CGFloat)ascender {
+       return ceilf(self.ratio * CGFontGetAscent(self.cgFont));
+}
+
+- (CGFloat)descender {
+       return floorf(self.ratio * CGFontGetDescent(self.cgFont));
+}
+
+- (CGFloat)leading {
+       return (self.ascender - self.descender);
+}
+
+- (CGFloat)capHeight {
+       return ceilf(self.ratio * CGFontGetCapHeight(self.cgFont));
+}
+
+- (CGFloat)xHeight {
+       return ceilf(self.ratio * CGFontGetXHeight(self.cgFont));
+}
+
+- (NSString *)familyName {
+       if (_familyName == nil) {
+               _familyName = [self copyNameTableEntryForID:1];
+       }
+       return _familyName;
+}
+
+- (NSString *)fontName {
+       if (_fontName == nil) {
+               _fontName = [self copyNameTableEntryForID:4];
+       }
+       return _fontName;
+}
+
+- (NSString *)postScriptName {
+       if (_postScriptName == nil) {
+               _postScriptName = [self copyNameTableEntryForID:6];
+       }
+       return _postScriptName;
+}
+
+- (ZFont *)fontWithSize:(CGFloat)fontSize {
+       if (fontSize == self.pointSize) return self;
+       NSParameterAssert(fontSize > 0.0);
+       return [[[ZFont alloc] initWithCGFont:self.cgFont size:fontSize] autorelease];
+}
+
+- (BOOL)isEqual:(id)object {
+       if (![object isKindOfClass:[ZFont class]]) return NO;
+       ZFont *font = (ZFont *)object;
+       return (font.cgFont == self.cgFont && font.pointSize == self.pointSize);
+}
+
+- (NSString *)copyNameTableEntryForID:(UInt16)aNameID {
+       CFDataRef nameTable = CGFontCopyTableForTag(self.cgFont, 'name');
+       NSAssert1(nameTable != NULL, @"CGFontCopyTableForTag returned NULL for 'name' tag in font %@",
+                          [(id)CFCopyDescription(self.cgFont) autorelease]);
+       const UInt8 * const bytes = CFDataGetBytePtr(nameTable);
+       NSAssert1(OSReadBigInt16(bytes, 0) == 0, @"name table for font %@ has bad version number",
+                          [(id)CFCopyDescription(self.cgFont) autorelease]);
+       const UInt16 count = OSReadBigInt16(bytes, 2);
+       const UInt16 stringOffset = OSReadBigInt16(bytes, 4);
+       const UInt8 * const nameRecords = &bytes[6];
+       UInt16 nameLength = 0;
+       UInt16 nameOffset = 0;
+       NSStringEncoding encoding = 0;
+       for (UInt16 idx = 0; idx < count; idx++) {
+               const uintptr_t recordOffset = 12 * idx;
+               const UInt16 nameID = OSReadBigInt16(nameRecords, recordOffset + 6);
+               if (nameID != aNameID) continue;
+               const UInt16 platformID = OSReadBigInt16(nameRecords, recordOffset + 0);
+               const UInt16 platformSpecificID = OSReadBigInt16(nameRecords, recordOffset + 2);
+               encoding = 0;
+               // for now, we only support a subset of encodings
+               switch (platformID) {
+                       case 0: // Unicode
+                               encoding = NSUTF16StringEncoding;
+                               break;
+                       case 1: // Macintosh
+                               switch (platformSpecificID) {
+                                       case 0:
+                                               encoding = NSMacOSRomanStringEncoding;
+                                               break;
+                               }
+                       case 3: // Microsoft
+                               switch (platformSpecificID) {
+                                       case 1:
+                                               encoding = NSUTF16StringEncoding;
+                                               break;
+                               }
+               }
+               if (encoding == 0) continue;
+               nameLength = OSReadBigInt16(nameRecords, recordOffset + 8);
+               nameOffset = OSReadBigInt16(nameRecords, recordOffset + 10);
+               break;
+       }
+       NSString *result = nil;
+       if (nameOffset > 0) {
+               const UInt8 *nameBytes = &bytes[stringOffset + nameOffset];
+               result = [[NSString alloc] initWithBytes:nameBytes length:nameLength encoding:encoding];
+       }
+       CFRelease(nameTable);
+       return result;
+}
+
+- (void)dealloc {
+       CGFontRelease(_cgFont);
+       [_familyName release];
+       [_fontName release];
+       [_postScriptName release];
+       [super dealloc];
+}
+@end
diff --git a/libs/TouchJSON/CDataScanner.h b/libs/TouchJSON/CDataScanner.h
new file mode 100644 (file)
index 0000000..41f68e8
--- /dev/null
@@ -0,0 +1,71 @@
+//
+//  CDataScanner.h
+//  TouchCode
+//
+//  Created by Jonathan Wight on 04/16/08.
+//  Copyright 2008 toxicsoftware.com. All rights reserved.
+//
+//  Permission is hereby granted, free of charge, to any person
+//  obtaining a copy of this software and associated documentation
+//  files (the "Software"), to deal in the Software without
+//  restriction, including without limitation the rights to use,
+//  copy, modify, merge, publish, distribute, sublicense, and/or sell
+//  copies of the Software, and to permit persons to whom the
+//  Software is furnished to do so, subject to the following
+//  conditions:
+//
+//  The above copyright notice and this permission notice shall be
+//  included in all copies or substantial portions of the Software.
+//
+//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+//  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+//  OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+//  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+//  HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+//  WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+//  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+//  OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#import <Foundation/Foundation.h>
+
+// NSScanner
+
+@interface CDataScanner : NSObject {
+       NSData *data;
+
+       u_int8_t *start;
+       u_int8_t *end;
+       u_int8_t *current;
+       NSUInteger length;
+}
+
+@property (readwrite, nonatomic, retain) NSData *data;
+@property (readwrite, nonatomic, assign) NSUInteger scanLocation;
+@property (readonly, nonatomic, assign) NSUInteger bytesRemaining;
+@property (readonly, nonatomic, assign) BOOL isAtEnd;
+
+- (id)initWithData:(NSData *)inData;
+
+- (unichar)currentCharacter;
+- (unichar)scanCharacter;
+- (BOOL)scanCharacter:(unichar)inCharacter;
+
+- (BOOL)scanUTF8String:(const char *)inString intoString:(NSString **)outValue;
+- (BOOL)scanString:(NSString *)inString intoString:(NSString **)outValue;
+- (BOOL)scanCharactersFromSet:(NSCharacterSet *)inSet intoString:(NSString **)outValue; // inSet must only contain 7-bit ASCII characters
+
+- (BOOL)scanUpToString:(NSString *)string intoString:(NSString **)outValue;
+- (BOOL)scanUpToCharactersFromSet:(NSCharacterSet *)set intoString:(NSString **)outValue; // inSet must only contain 7-bit ASCII characters
+
+- (BOOL)scanNumber:(NSNumber **)outValue;
+- (BOOL)scanDecimalNumber:(NSDecimalNumber **)outValue;
+
+- (BOOL)scanDataOfLength:(NSUInteger)inLength intoData:(NSData **)outData;
+
+- (void)skipWhitespace;
+
+- (NSString *)remainingString;
+- (NSData *)remainingData;
+
+@end
diff --git a/libs/TouchJSON/CDataScanner.m b/libs/TouchJSON/CDataScanner.m
new file mode 100644 (file)
index 0000000..b3cee6f
--- /dev/null
@@ -0,0 +1,340 @@
+//
+//  CDataScanner.m
+//  TouchCode
+//
+//  Created by Jonathan Wight on 04/16/08.
+//  Copyright 2008 toxicsoftware.com. All rights reserved.
+//
+//  Permission is hereby granted, free of charge, to any person
+//  obtaining a copy of this software and associated documentation
+//  files (the "Software"), to deal in the Software without
+//  restriction, including without limitation the rights to use,
+//  copy, modify, merge, publish, distribute, sublicense, and/or sell
+//  copies of the Software, and to permit persons to whom the
+//  Software is furnished to do so, subject to the following
+//  conditions:
+//
+//  The above copyright notice and this permission notice shall be
+//  included in all copies or substantial portions of the Software.
+//
+//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+//  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+//  OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+//  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+//  HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+//  WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+//  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+//  OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#import "CDataScanner.h"
+
+#import "CDataScanner_Extensions.h"
+
+@interface CDataScanner ()
+@end
+
+#pragma mark -
+
+inline static unichar CharacterAtPointer(void *start, void *end)
+    {
+    #pragma unused(end)
+
+    const u_int8_t theByte = *(u_int8_t *)start;
+    if (theByte & 0x80)
+        {
+        // TODO -- UNICODE!!!! (well in theory nothing todo here)
+        }
+    const unichar theCharacter = theByte;
+    return(theCharacter);
+    }
+
+    static NSCharacterSet *sDoubleCharacters = NULL;
+
+    @implementation CDataScanner
+
+- (id)init
+    {
+    if ((self = [super init]) != NULL)
+        {
+        }
+    return(self);
+    }
+
+- (id)initWithData:(NSData *)inData;
+    {
+    if ((self = [self init]) != NULL)
+        {
+        [self setData:inData];
+        }
+    return(self);
+    }
+
+    + (void)initialize
+    {
+    if (sDoubleCharacters == NULL)
+        {
+        sDoubleCharacters = [[NSCharacterSet characterSetWithCharactersInString:@"0123456789eE-+."] retain];
+        }
+    }
+
+- (void)dealloc
+    {
+    [data release];
+    data = NULL;
+    //
+    [super dealloc];
+    }
+
+- (NSUInteger)scanLocation
+    {
+    return(current - start);
+    }
+
+- (NSUInteger)bytesRemaining
+    {
+    return(end - current);
+    }
+
+- (NSData *)data
+    {
+    return(data);
+    }
+
+- (void)setData:(NSData *)inData
+    {
+    if (data != inData)
+        {
+        [data release];
+        data = [inData retain];
+        }
+
+    if (data)
+        {
+        start = (u_int8_t *)data.bytes;
+        end = start + data.length;
+        current = start;
+        length = data.length;
+        }
+    else
+        {
+        start = NULL;
+        end = NULL;
+        current = NULL;
+        length = 0;
+        }
+    }
+
+- (void)setScanLocation:(NSUInteger)inScanLocation
+    {
+    current = start + inScanLocation;
+    }
+
+- (BOOL)isAtEnd
+    {
+    return(self.scanLocation >= length);
+    }
+
+- (unichar)currentCharacter
+    {
+    return(CharacterAtPointer(current, end));
+    }
+
+#pragma mark -
+
+- (unichar)scanCharacter
+    {
+    const unichar theCharacter = CharacterAtPointer(current++, end);
+    return(theCharacter);
+    }
+
+- (BOOL)scanCharacter:(unichar)inCharacter
+    {
+    unichar theCharacter = CharacterAtPointer(current, end);
+    if (theCharacter == inCharacter)
+        {
+        ++current;
+        return(YES);
+        }
+    else
+        return(NO);
+    }
+
+- (BOOL)scanUTF8String:(const char *)inString intoString:(NSString **)outValue
+    {
+    const size_t theLength = strlen(inString);
+    if ((size_t)(end - current) < theLength)
+        return(NO);
+    if (strncmp((char *)current, inString, theLength) == 0)
+        {
+        current += theLength;
+        if (outValue)
+            *outValue = [NSString stringWithUTF8String:inString];
+        return(YES);
+        }
+    return(NO);
+    }
+
+- (BOOL)scanString:(NSString *)inString intoString:(NSString **)outValue
+    {
+    if ((size_t)(end - current) < inString.length)
+        return(NO);
+    if (strncmp((char *)current, [inString UTF8String], inString.length) == 0)
+        {
+        current += inString.length;
+        if (outValue)
+            *outValue = inString;
+        return(YES);
+        }
+    return(NO);
+    }
+
+- (BOOL)scanCharactersFromSet:(NSCharacterSet *)inSet intoString:(NSString **)outValue
+    {
+    u_int8_t *P;
+    for (P = current; P < end && [inSet characterIsMember:*P] == YES; ++P)
+        ;
+
+    if (P == current)
+        {
+        return(NO);
+        }
+
+    if (outValue)
+        {
+        *outValue = [[[NSString alloc] initWithBytes:current length:P - current encoding:NSUTF8StringEncoding] autorelease];
+        }
+
+    current = P;
+
+    return(YES);
+    }
+
+- (BOOL)scanUpToString:(NSString *)inString intoString:(NSString **)outValue
+    {
+    const char *theToken = [inString UTF8String];
+    const char *theResult = strnstr((char *)current, theToken, end - current);
+    if (theResult == NULL)
+        {
+        return(NO);
+        }
+
+    if (outValue)
+        {
+        *outValue = [[[NSString alloc] initWithBytes:current length:theResult - (char *)current encoding:NSUTF8StringEncoding] autorelease];
+        }
+
+    current = (u_int8_t *)theResult;
+
+    return(YES);
+    }
+
+- (BOOL)scanUpToCharactersFromSet:(NSCharacterSet *)inSet intoString:(NSString **)outValue
+    {
+    u_int8_t *P;
+    for (P = current; P < end && [inSet characterIsMember:*P] == NO; ++P)
+        ;
+
+    if (P == current)
+        {
+        return(NO);
+        }
+
+    if (outValue)
+        {
+        *outValue = [[[NSString alloc] initWithBytes:current length:P - current encoding:NSUTF8StringEncoding] autorelease];
+        }
+
+    current = P;
+
+    return(YES);
+    }
+
+- (BOOL)scanNumber:(NSNumber **)outValue
+        {
+        NSString *theString = NULL;
+        if ([self scanCharactersFromSet:sDoubleCharacters intoString:&theString])
+            {
+            if ([theString rangeOfString:@"."].location != NSNotFound)
+                {
+                if (outValue)
+                    {
+                    *outValue = [NSDecimalNumber decimalNumberWithString:theString];
+                    }
+                return(YES);
+                }
+            else if ([theString rangeOfString:@"-"].location != NSNotFound)
+                {
+                if (outValue != NULL)
+                    {
+                    *outValue = [NSNumber numberWithLongLong:[theString longLongValue]];
+                    }
+                return(YES);
+                }
+            else
+                {
+                if (outValue != NULL)
+                    {
+                    *outValue = [NSNumber numberWithUnsignedLongLong:strtoull([theString UTF8String], NULL, 0)];
+                    }
+                return(YES);
+                }
+            
+            }
+        return(NO);
+        }
+            
+- (BOOL)scanDecimalNumber:(NSDecimalNumber **)outValue;
+        {
+        NSString *theString = NULL;
+        if ([self scanCharactersFromSet:sDoubleCharacters intoString:&theString])
+            {
+            if (outValue)
+                {
+                *outValue = [NSDecimalNumber decimalNumberWithString:theString];
+                }
+            return(YES);
+            }
+        return(NO);
+        }
+
+- (BOOL)scanDataOfLength:(NSUInteger)inLength intoData:(NSData **)outData;
+        {
+        if (self.bytesRemaining < inLength)
+            {
+            return(NO);
+            }
+        
+        if (outData)
+            {
+            *outData = [NSData dataWithBytes:current length:inLength];
+            }
+
+        current += inLength;
+        return(YES);
+        }
+
+
+- (void)skipWhitespace
+    {
+    u_int8_t *P;
+    for (P = current; P < end && (isspace(*P)); ++P)
+        ;
+
+    current = P;
+    }
+
+- (NSString *)remainingString
+    {
+    NSData *theRemainingData = [NSData dataWithBytes:current length:end - current];
+    NSString *theString = [[[NSString alloc] initWithData:theRemainingData encoding:NSUTF8StringEncoding] autorelease];
+    return(theString);
+    }
+
+- (NSData *)remainingData;
+    {
+    NSData *theRemainingData = [NSData dataWithBytes:current length:end - current];
+    return(theRemainingData);
+    }
+
+    @end
diff --git a/libs/TouchJSON/Extensions/CDataScanner_Extensions.h b/libs/TouchJSON/Extensions/CDataScanner_Extensions.h
new file mode 100644 (file)
index 0000000..cde1dbb
--- /dev/null
@@ -0,0 +1,40 @@
+//
+//  CDataScanner_Extensions.h
+//  TouchCode
+//
+//  Created by Jonathan Wight on 12/08/2005.
+//  Copyright 2005 toxicsoftware.com. All rights reserved.
+//
+//  Permission is hereby granted, free of charge, to any person
+//  obtaining a copy of this software and associated documentation
+//  files (the "Software"), to deal in the Software without
+//  restriction, including without limitation the rights to use,
+//  copy, modify, merge, publish, distribute, sublicense, and/or sell
+//  copies of the Software, and to permit persons to whom the
+//  Software is furnished to do so, subject to the following
+//  conditions:
+//
+//  The above copyright notice and this permission notice shall be
+//  included in all copies or substantial portions of the Software.
+//
+//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+//  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+//  OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+//  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+//  HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+//  WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+//  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+//  OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#import "CDataScanner.h"
+
+@interface CDataScanner (CDataScanner_Extensions)
+
+- (BOOL)scanCStyleComment:(NSString **)outComment;
+- (BOOL)scanCPlusPlusStyleComment:(NSString **)outComment;
+
+- (NSUInteger)lineOfScanLocation;
+- (NSDictionary *)userInfoForScanLocation;
+
+@end
diff --git a/libs/TouchJSON/Extensions/CDataScanner_Extensions.m b/libs/TouchJSON/Extensions/CDataScanner_Extensions.m
new file mode 100644 (file)
index 0000000..90dbbda
--- /dev/null
@@ -0,0 +1,135 @@
+//
+//  CDataScanner_Extensions.m
+//  TouchCode
+//
+//  Created by Jonathan Wight on 12/08/2005.
+//  Copyright 2005 toxicsoftware.com. All rights reserved.
+//
+//  Permission is hereby granted, free of charge, to any person
+//  obtaining a copy of this software and associated documentation
+//  files (the "Software"), to deal in the Software without
+//  restriction, including without limitation the rights to use,
+//  copy, modify, merge, publish, distribute, sublicense, and/or sell
+//  copies of the Software, and to permit persons to whom the
+//  Software is furnished to do so, subject to the following
+//  conditions:
+//
+//  The above copyright notice and this permission notice shall be
+//  included in all copies or substantial portions of the Software.
+//
+//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+//  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+//  OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+//  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+//  HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+//  WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+//  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+//  OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#import "CDataScanner_Extensions.h"
+
+#define LF 0x000a // Line Feed
+#define FF 0x000c // Form Feed
+#define CR 0x000d // Carriage Return
+#define NEL 0x0085 // Next Line
+#define LS 0x2028 // Line Separator
+#define PS 0x2029 // Paragraph Separator
+
+@implementation CDataScanner (CDataScanner_Extensions)
+
+- (BOOL)scanCStyleComment:(NSString **)outComment
+{
+if ([self scanString:@"/*" intoString:NULL] == YES)
+       {
+       NSString *theComment = NULL;
+       if ([self scanUpToString:@"*/" intoString:&theComment] == NO)
+               [NSException raise:NSGenericException format:@"Started to scan a C style comment but it wasn't terminated."];
+               
+       if ([theComment rangeOfString:@"/*"].location != NSNotFound)
+               [NSException raise:NSGenericException format:@"C style comments should not be nested."];
+       
+       if ([self scanString:@"*/" intoString:NULL] == NO)
+               [NSException raise:NSGenericException format:@"C style comment did not end correctly."];
+               
+       if (outComment != NULL)
+               *outComment = theComment;
+
+       return(YES);
+       }
+else
+       {
+       return(NO);
+       }
+}
+
+- (BOOL)scanCPlusPlusStyleComment:(NSString **)outComment
+    {
+    if ([self scanString:@"//" intoString:NULL] == YES)
+        {
+        unichar theCharacters[] = { LF, FF, CR, NEL, LS, PS, };
+        NSCharacterSet *theLineBreaksCharacterSet = [NSCharacterSet characterSetWithCharactersInString:[NSString stringWithCharacters:theCharacters length:sizeof(theCharacters) / sizeof(*theCharacters)]];
+
+        NSString *theComment = NULL;
+        [self scanUpToCharactersFromSet:theLineBreaksCharacterSet intoString:&theComment];
+        [self scanCharactersFromSet:theLineBreaksCharacterSet intoString:NULL];
+
+        if (outComment != NULL)
+            *outComment = theComment;
+
+        return(YES);
+        }
+    else
+        {
+        return(NO);
+        }
+    }
+
+- (NSUInteger)lineOfScanLocation
+    {
+    NSUInteger theLine = 0;
+    for (const u_int8_t *C = start; C < current; ++C)
+        {
+        // TODO: JIW What about MS-DOS line endings you bastard! (Also other unicode line endings)
+        if (*C == '\n' || *C == '\r')
+            {
+            ++theLine;
+            }
+        }
+    return(theLine);
+    }
+
+- (NSDictionary *)userInfoForScanLocation
+    {
+    NSUInteger theLine = 0;
+    const u_int8_t *theLineStart = start;
+    for (const u_int8_t *C = start; C < current; ++C)
+        {
+        if (*C == '\n' || *C == '\r')
+            {
+            theLineStart = C - 1;
+            ++theLine;
+            }
+        }
+
+    NSUInteger theCharacter = current - theLineStart;
+
+    NSRange theStartRange = NSIntersectionRange((NSRange){ .location = MAX((NSInteger)self.scanLocation - 20, 0), .length = 20 + (NSInteger)self.scanLocation - 20 }, (NSRange){ .location = 0, .length = self.data.length });
+    NSRange theEndRange = NSIntersectionRange((NSRange){ .location = self.scanLocation, .length = 20 }, (NSRange){ .location = 0, .length = self.data.length });
+
+
+    NSString *theSnippet = [NSString stringWithFormat:@"%@!HERE>!%@",
+        [[[NSString alloc] initWithData:[self.data subdataWithRange:theStartRange] encoding:NSUTF8StringEncoding] autorelease],
+        [[[NSString alloc] initWithData:[self.data subdataWithRange:theEndRange] encoding:NSUTF8StringEncoding] autorelease]
+        ];
+
+    NSDictionary *theUserInfo = [NSDictionary dictionaryWithObjectsAndKeys:
+        [NSNumber numberWithUnsignedInteger:theLine], @"line",
+        [NSNumber numberWithUnsignedInteger:theCharacter], @"character",
+        [NSNumber numberWithUnsignedInteger:self.scanLocation], @"location",
+        theSnippet, @"snippet",
+        NULL];
+    return(theUserInfo);    
+    }
+
+@end
diff --git a/libs/TouchJSON/Extensions/NSDictionary_JSONExtensions.h b/libs/TouchJSON/Extensions/NSDictionary_JSONExtensions.h
new file mode 100644 (file)
index 0000000..6e611d0
--- /dev/null
@@ -0,0 +1,37 @@
+//
+//  NSDictionary_JSONExtensions.h
+//  TouchCode
+//
+//  Created by Jonathan Wight on 04/17/08.
+//  Copyright 2008 toxicsoftware.com. All rights reserved.
+//
+//  Permission is hereby granted, free of charge, to any person
+//  obtaining a copy of this software and associated documentation
+//  files (the "Software"), to deal in the Software without
+//  restriction, including without limitation the rights to use,
+//  copy, modify, merge, publish, distribute, sublicense, and/or sell
+//  copies of the Software, and to permit persons to whom the
+//  Software is furnished to do so, subject to the following
+//  conditions:
+//
+//  The above copyright notice and this permission notice shall be
+//  included in all copies or substantial portions of the Software.
+//
+//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+//  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+//  OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+//  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+//  HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+//  WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+//  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+//  OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#import <Foundation/Foundation.h>
+
+@interface NSDictionary (NSDictionary_JSONExtensions)
+
++ (id)dictionaryWithJSONData:(NSData *)inData error:(NSError **)outError;
++ (id)dictionaryWithJSONString:(NSString *)inJSON error:(NSError **)outError;
+
+@end
diff --git a/libs/TouchJSON/Extensions/NSDictionary_JSONExtensions.m b/libs/TouchJSON/Extensions/NSDictionary_JSONExtensions.m
new file mode 100644 (file)
index 0000000..c0bb43c
--- /dev/null
@@ -0,0 +1,47 @@
+//
+//  NSDictionary_JSONExtensions.m
+//  TouchCode
+//
+//  Created by Jonathan Wight on 04/17/08.
+//  Copyright 2008 toxicsoftware.com. All rights reserved.
+//
+//  Permission is hereby granted, free of charge, to any person
+//  obtaining a copy of this software and associated documentation
+//  files (the "Software"), to deal in the Software without
+//  restriction, including without limitation the rights to use,
+//  copy, modify, merge, publish, distribute, sublicense, and/or sell
+//  copies of the Software, and to permit persons to whom the
+//  Software is furnished to do so, subject to the following
+//  conditions:
+//
+//  The above copyright notice and this permission notice shall be
+//  included in all copies or substantial portions of the Software.
+//
+//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+//  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+//  OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+//  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+//  HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+//  WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+//  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+//  OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#import "NSDictionary_JSONExtensions.h"
+
+#import "CJSONDeserializer.h"
+
+@implementation NSDictionary (NSDictionary_JSONExtensions)
+
++ (id)dictionaryWithJSONData:(NSData *)inData error:(NSError **)outError
+    {
+    return([[CJSONDeserializer deserializer] deserialize:inData error:outError]);
+    }
+
++ (id)dictionaryWithJSONString:(NSString *)inJSON error:(NSError **)outError;
+    {
+    NSData *theData = [inJSON dataUsingEncoding:NSUTF8StringEncoding];
+    return([self dictionaryWithJSONData:theData error:outError]);
+    }
+
+@end
diff --git a/libs/TouchJSON/JSON/CJSONDeserializer.h b/libs/TouchJSON/JSON/CJSONDeserializer.h
new file mode 100644 (file)
index 0000000..0c3ed02
--- /dev/null
@@ -0,0 +1,63 @@
+//
+//  CJSONDeserializer.h
+//  TouchCode
+//
+//  Created by Jonathan Wight on 12/15/2005.
+//  Copyright 2005 toxicsoftware.com. All rights reserved.
+//
+//  Permission is hereby granted, free of charge, to any person
+//  obtaining a copy of this software and associated documentation
+//  files (the "Software"), to deal in the Software without
+//  restriction, including without limitation the rights to use,
+//  copy, modify, merge, publish, distribute, sublicense, and/or sell
+//  copies of the Software, and to permit persons to whom the
+//  Software is furnished to do so, subject to the following
+//  conditions:
+//
+//  The above copyright notice and this permission notice shall be
+//  included in all copies or substantial portions of the Software.
+//
+//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+//  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+//  OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+//  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+//  HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+//  WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+//  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+//  OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#import <Foundation/Foundation.h>
+
+#import "CJSONScanner.h"
+
+extern NSString *const kJSONDeserializerErrorDomain /* = @"CJSONDeserializerErrorDomain" */;
+
+enum {
+    kJSONDeserializationOptions_MutableContainers = kJSONScannerOptions_MutableContainers,
+    kJSONDeserializationOptions_MutableLeaves = kJSONScannerOptions_MutableLeaves,
+};
+typedef NSUInteger EJSONDeserializationOptions;
+
+@class CJSONScanner;
+
+@interface CJSONDeserializer : NSObject {
+    CJSONScanner *scanner;
+    EJSONDeserializationOptions options;
+}
+
+@property (readwrite, nonatomic, retain) CJSONScanner *scanner;
+/// Object to return instead when a null encountered in the JSON. Defaults to NSNull. Setting to null causes the scanner to skip null values.
+@property (readwrite, nonatomic, retain) id nullObject;
+/// JSON must be encoded in Unicode (UTF-8, UTF-16 or UTF-32). Use this if you expect to get the JSON in another encoding.
+@property (readwrite, nonatomic, assign) NSStringEncoding allowedEncoding;
+@property (readwrite, nonatomic, assign) EJSONDeserializationOptions options;
+
++ (id)deserializer;
+
+- (id)deserialize:(NSData *)inData error:(NSError **)outError;
+
+- (id)deserializeAsDictionary:(NSData *)inData error:(NSError **)outError;
+- (id)deserializeAsArray:(NSData *)inData error:(NSError **)outError;
+
+@end
diff --git a/libs/TouchJSON/JSON/CJSONDeserializer.m b/libs/TouchJSON/JSON/CJSONDeserializer.m
new file mode 100644 (file)
index 0000000..27a2d03
--- /dev/null
@@ -0,0 +1,161 @@
+//
+//  CJSONDeserializer.m
+//  TouchCode
+//
+//  Created by Jonathan Wight on 12/15/2005.
+//  Copyright 2005 toxicsoftware.com. All rights reserved.
+//
+//  Permission is hereby granted, free of charge, to any person
+//  obtaining a copy of this software and associated documentation
+//  files (the "Software"), to deal in the Software without
+//  restriction, including without limitation the rights to use,
+//  copy, modify, merge, publish, distribute, sublicense, and/or sell
+//  copies of the Software, and to permit persons to whom the
+//  Software is furnished to do so, subject to the following
+//  conditions:
+//
+//  The above copyright notice and this permission notice shall be
+//  included in all copies or substantial portions of the Software.
+//
+//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+//  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+//  OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+//  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+//  HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+//  WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+//  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+//  OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#import "CJSONDeserializer.h"
+
+#import "CJSONScanner.h"
+#import "CDataScanner.h"
+
+NSString *const kJSONDeserializerErrorDomain  = @"CJSONDeserializerErrorDomain";
+
+@interface CJSONDeserializer ()
+@end
+
+@implementation CJSONDeserializer
+
+@synthesize scanner;
+@synthesize options;
+
++ (id)deserializer
+    {
+    return([[[self alloc] init] autorelease]);
+    }
+
+- (id)init
+    {
+    if ((self = [super init]) != NULL)
+        {
+        }
+    return(self);
+    }
+
+- (void)dealloc
+    {
+    [scanner release];
+    scanner = NULL;
+    //
+    [super dealloc];
+    }
+
+#pragma mark -
+
+- (CJSONScanner *)scanner
+    {
+    if (scanner == NULL)
+        {
+        scanner = [[CJSONScanner alloc] init];
+        }
+    return(scanner);
+    }
+
+- (id)nullObject
+    {
+    return(self.scanner.nullObject);
+    }
+
+- (void)setNullObject:(id)inNullObject
+    {
+    self.scanner.nullObject = inNullObject;
+    }
+
+#pragma mark -
+
+- (NSStringEncoding)allowedEncoding
+    {
+    return(self.scanner.allowedEncoding);
+    }
+
+- (void)setAllowedEncoding:(NSStringEncoding)inAllowedEncoding
+    {
+    self.scanner.allowedEncoding = inAllowedEncoding;
+    }
+
+#pragma mark -
+
+- (id)deserialize:(NSData *)inData error:(NSError **)outError
+    {
+    if (inData == NULL || [inData length] == 0)
+        {
+        if (outError)
+            *outError = [NSError errorWithDomain:kJSONDeserializerErrorDomain code:kJSONScannerErrorCode_NothingToScan userInfo:NULL];
+
+        return(NULL);
+        }
+    if ([self.scanner setData:inData error:outError] == NO)
+        {
+        return(NULL);
+        }
+    id theObject = NULL;
+    if ([self.scanner scanJSONObject:&theObject error:outError] == YES)
+        return(theObject);
+    else
+        return(NULL);
+    }
+
+- (id)deserializeAsDictionary:(NSData *)inData error:(NSError **)outError
+    {
+    if (inData == NULL || [inData length] == 0)
+        {
+        if (outError)
+            *outError = [NSError errorWithDomain:kJSONDeserializerErrorDomain code:kJSONScannerErrorCode_NothingToScan userInfo:NULL];
+
+        return(NULL);
+        }
+    if ([self.scanner setData:inData error:outError] == NO)
+        {
+        return(NULL);
+        }
+    NSDictionary *theDictionary = NULL;
+    if ([self.scanner scanJSONDictionary:&theDictionary error:outError] == YES)
+        return(theDictionary);
+    else
+        return(NULL);
+    }
+
+- (id)deserializeAsArray:(NSData *)inData error:(NSError **)outError
+    {
+    if (inData == NULL || [inData length] == 0)
+        {
+        if (outError)
+            *outError = [NSError errorWithDomain:kJSONDeserializerErrorDomain code:kJSONScannerErrorCode_NothingToScan userInfo:NULL];
+
+        return(NULL);
+        }
+    if ([self.scanner setData:inData error:outError] == NO)
+        {
+        return(NULL);
+        }
+    NSArray *theArray = NULL;
+    if ([self.scanner scanJSONArray:&theArray error:outError] == YES)
+        return(theArray);
+    else
+        return(NULL);
+    }
+
+@end
diff --git a/libs/TouchJSON/JSON/CJSONScanner.h b/libs/TouchJSON/JSON/CJSONScanner.h
new file mode 100644 (file)
index 0000000..d410893
--- /dev/null
@@ -0,0 +1,95 @@
+//
+//  CJSONScanner.h
+//  TouchCode
+//
+//  Created by Jonathan Wight on 12/07/2005.
+//  Copyright 2005 toxicsoftware.com. All rights reserved.
+//
+//  Permission is hereby granted, free of charge, to any person
+//  obtaining a copy of this software and associated documentation
+//  files (the "Software"), to deal in the Software without
+//  restriction, including without limitation the rights to use,
+//  copy, modify, merge, publish, distribute, sublicense, and/or sell
+//  copies of the Software, and to permit persons to whom the
+//  Software is furnished to do so, subject to the following
+//  conditions:
+//
+//  The above copyright notice and this permission notice shall be
+//  included in all copies or substantial portions of the Software.
+//
+//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+//  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+//  OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+//  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+//  HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+//  WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+//  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+//  OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#import "CDataScanner.h"
+
+enum {
+    kJSONScannerOptions_MutableContainers = 0x1,
+    kJSONScannerOptions_MutableLeaves = 0x2,
+};
+typedef NSUInteger EJSONScannerOptions;
+
+/// CDataScanner subclass that understands JSON syntax natively. You should generally use CJSONDeserializer instead of this class. (TODO - this could have been a category?)
+@interface CJSONScanner : CDataScanner {
+       BOOL strictEscapeCodes;
+    id nullObject;
+       NSStringEncoding allowedEncoding;
+    EJSONScannerOptions options;
+}
+
+@property (readwrite, nonatomic, assign) BOOL strictEscapeCodes;
+@property (readwrite, nonatomic, retain) id nullObject;
+@property (readwrite, nonatomic, assign) NSStringEncoding allowedEncoding;
+@property (readwrite, nonatomic, assign) EJSONScannerOptions options;
+
+- (BOOL)setData:(NSData *)inData error:(NSError **)outError;
+
+- (BOOL)scanJSONObject:(id *)outObject error:(NSError **)outError;
+- (BOOL)scanJSONDictionary:(NSDictionary **)outDictionary error:(NSError **)outError;
+- (BOOL)scanJSONArray:(NSArray **)outArray error:(NSError **)outError;
+- (BOOL)scanJSONStringConstant:(NSString **)outStringConstant error:(NSError **)outError;
+- (BOOL)scanJSONNumberConstant:(NSNumber **)outNumberConstant error:(NSError **)outError;
+
+@end
+
+extern NSString *const kJSONScannerErrorDomain /* = @"kJSONScannerErrorDomain" */;
+
+typedef enum {
+    
+    // Fundamental scanning errors
+    kJSONScannerErrorCode_NothingToScan = -11, 
+    kJSONScannerErrorCode_CouldNotDecodeData = -12, 
+    kJSONScannerErrorCode_CouldNotSerializeData = -13,
+    kJSONScannerErrorCode_CouldNotSerializeObject = -14, 
+    kJSONScannerErrorCode_CouldNotScanObject = -15, 
+    
+    // Dictionary scanning
+    kJSONScannerErrorCode_DictionaryStartCharacterMissing = -101, 
+    kJSONScannerErrorCode_DictionaryKeyScanFailed = -102, 
+    kJSONScannerErrorCode_DictionaryKeyNotTerminated = -103, 
+    kJSONScannerErrorCode_DictionaryValueScanFailed = -104, 
+    kJSONScannerErrorCode_DictionaryKeyValuePairNoDelimiter = -105, 
+    kJSONScannerErrorCode_DictionaryNotTerminated = -106, 
+    
+    // Array scanning
+    kJSONScannerErrorCode_ArrayStartCharacterMissing = -201, 
+    kJSONScannerErrorCode_ArrayValueScanFailed = -202, 
+    kJSONScannerErrorCode_ArrayValueIsNull = -203, 
+    kJSONScannerErrorCode_ArrayNotTerminated = -204,
+    
+    // String scanning
+    kJSONScannerErrorCode_StringNotStartedWithBackslash = -301, 
+    kJSONScannerErrorCode_StringUnicodeNotDecoded = -302, 
+    kJSONScannerErrorCode_StringUnknownEscapeCode = -303, 
+    kJSONScannerErrorCode_StringNotTerminated = -304,
+    
+    // Number scanning
+    kJSONScannerErrorCode_NumberNotScannable = -401
+    
+} EJSONScannerErrorCode;
diff --git a/libs/TouchJSON/JSON/CJSONScanner.m b/libs/TouchJSON/JSON/CJSONScanner.m
new file mode 100644 (file)
index 0000000..c5ffeb4
--- /dev/null
@@ -0,0 +1,676 @@
+//
+//  CJSONScanner.m
+//  TouchCode
+//
+//  Created by Jonathan Wight on 12/07/2005.
+//  Copyright 2005 toxicsoftware.com. All rights reserved.
+//
+//  Permission is hereby granted, free of charge, to any person
+//  obtaining a copy of this software and associated documentation
+//  files (the "Software"), to deal in the Software without
+//  restriction, including without limitation the rights to use,
+//  copy, modify, merge, publish, distribute, sublicense, and/or sell
+//  copies of the Software, and to permit persons to whom the
+//  Software is furnished to do so, subject to the following
+//  conditions:
+//
+//  The above copyright notice and this permission notice shall be
+//  included in all copies or substantial portions of the Software.
+//
+//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+//  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+//  OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+//  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+//  HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+//  WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+//  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+//  OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#import "CJSONScanner.h"
+
+#import "CDataScanner_Extensions.h"
+
+#if !defined(TREAT_COMMENTS_AS_WHITESPACE)
+#define TREAT_COMMENTS_AS_WHITESPACE 0
+#endif // !defined(TREAT_COMMENTS_AS_WHITESPACE)
+
+NSString *const kJSONScannerErrorDomain = @"kJSONScannerErrorDomain";
+
+inline static int HexToInt(char inCharacter)
+    {
+    int theValues[] = { 0x0 /* 48 '0' */, 0x1 /* 49 '1' */, 0x2 /* 50 '2' */, 0x3 /* 51 '3' */, 0x4 /* 52 '4' */, 0x5 /* 53 '5' */, 0x6 /* 54 '6' */, 0x7 /* 55 '7' */, 0x8 /* 56 '8' */, 0x9 /* 57 '9' */, -1 /* 58 ':' */, -1 /* 59 ';' */, -1 /* 60 '<' */, -1 /* 61 '=' */, -1 /* 62 '>' */, -1 /* 63 '?' */, -1 /* 64 '@' */, 0xa /* 65 'A' */, 0xb /* 66 'B' */, 0xc /* 67 'C' */, 0xd /* 68 'D' */, 0xe /* 69 'E' */, 0xf /* 70 'F' */, -1 /* 71 'G' */, -1 /* 72 'H' */, -1 /* 73 'I' */, -1 /* 74 'J' */, -1 /* 75 'K' */, -1 /* 76 'L' */, -1 /* 77 'M' */, -1 /* 78 'N' */, -1 /* 79 'O' */, -1 /* 80 'P' */, -1 /* 81 'Q' */, -1 /* 82 'R' */, -1 /* 83 'S' */, -1 /* 84 'T' */, -1 /* 85 'U' */, -1 /* 86 'V' */, -1 /* 87 'W' */, -1 /* 88 'X' */, -1 /* 89 'Y' */, -1 /* 90 'Z' */, -1 /* 91 '[' */, -1 /* 92 '\' */, -1 /* 93 ']' */, -1 /* 94 '^' */, -1 /* 95 '_' */, -1 /* 96 '`' */, 0xa /* 97 'a' */, 0xb /* 98 'b' */, 0xc /* 99 'c' */, 0xd /* 100 'd' */, 0xe /* 101 'e' */, 0xf /* 102 'f' */, };
+    if (inCharacter >= '0' && inCharacter <= 'f')
+        return(theValues[inCharacter - '0']);
+    else
+        return(-1);
+    }
+
+@interface CJSONScanner ()
+- (BOOL)scanNotQuoteCharactersIntoString:(NSString **)outValue;
+@end
+
+#pragma mark -
+
+@implementation CJSONScanner
+
+@synthesize strictEscapeCodes;
+@synthesize nullObject;
+@synthesize allowedEncoding;
+@synthesize options;
+
+- (id)init
+    {
+    if ((self = [super init]) != NULL)
+        {
+        strictEscapeCodes = NO;
+        nullObject = [[NSNull null] retain];
+        }
+    return(self);
+    }
+
+- (void)dealloc
+    {
+    [nullObject release];
+    nullObject = NULL;
+    //
+    [super dealloc];
+    }
+
+#pragma mark -
+
+- (BOOL)setData:(NSData *)inData error:(NSError **)outError;
+    {
+    NSData *theData = inData;
+    if (theData && theData.length >= 4)
+        {
+        // This code is lame, but it works. Because the first character of any JSON string will always be a (ascii) control character we can work out the Unicode encoding by the bit pattern. See section 3 of http://www.ietf.org/rfc/rfc4627.txt
+        const char *theChars = theData.bytes;
+        NSStringEncoding theEncoding = NSUTF8StringEncoding;
+        if (theChars[0] != 0 && theChars[1] == 0)
+            {
+            if (theChars[2] != 0 && theChars[3] == 0)
+                theEncoding = NSUTF16LittleEndianStringEncoding;
+            else if (theChars[2] == 0 && theChars[3] == 0)
+                theEncoding = NSUTF32LittleEndianStringEncoding;
+            }
+        else if (theChars[0] == 0 && theChars[2] == 0 && theChars[3] != 0)
+            {
+            if (theChars[1] == 0)
+                theEncoding = NSUTF32BigEndianStringEncoding;
+            else if (theChars[1] != 0)
+                theEncoding = NSUTF16BigEndianStringEncoding;
+            }
+            
+        NSString *theString = [[NSString alloc] initWithData:theData encoding:theEncoding];
+        if (theString == NULL && self.allowedEncoding != 0)
+            {
+            theString = [[NSString alloc] initWithData:theData encoding:self.allowedEncoding];
+            }
+        theData = [theString dataUsingEncoding:NSUTF8StringEncoding];
+        [theString release];
+        }
+
+    if (theData)
+        {
+        [super setData:theData];
+        return(YES);
+        }
+    else
+        {
+        if (outError)
+            {
+            NSMutableDictionary *theUserInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
+                @"Could not scan data. Data wasn't encoded properly?", NSLocalizedDescriptionKey,
+                NULL];
+            [theUserInfo addEntriesFromDictionary:self.userInfoForScanLocation];
+            *outError = [NSError errorWithDomain:kJSONScannerErrorDomain code:kJSONScannerErrorCode_CouldNotDecodeData userInfo:theUserInfo];
+            }
+        return(NO);
+        }
+    }
+
+- (void)setData:(NSData *)inData
+    {
+    [self setData:inData error:NULL];
+    }
+
+#pragma mark -
+
+- (BOOL)scanJSONObject:(id *)outObject error:(NSError **)outError
+    {
+    BOOL theResult = YES;
+
+    [self skipWhitespace];
+
+    id theObject = NULL;
+
+    const unichar C = [self currentCharacter];
+    switch (C)
+        {
+        case 't':
+            if ([self scanUTF8String:"true" intoString:NULL])
+                {
+                theObject = [NSNumber numberWithBool:YES];
+                }
+            break;
+        case 'f':
+            if ([self scanUTF8String:"false" intoString:NULL])
+                {
+                theObject = [NSNumber numberWithBool:NO];
+                }
+            break;
+        case 'n':
+            if ([self scanUTF8String:"null" intoString:NULL])
+                {
+                theObject = self.nullObject;
+                }
+            break;
+        case '\"':
+        case '\'':
+            theResult = [self scanJSONStringConstant:&theObject error:outError];
+            break;
+        case '0':
+        case '1':
+        case '2':
+        case '3':
+        case '4':
+        case '5':
+        case '6':
+        case '7':
+        case '8':
+        case '9':
+        case '-':
+            theResult = [self scanJSONNumberConstant:&theObject error:outError];
+            break;
+        case '{':
+            theResult = [self scanJSONDictionary:&theObject error:outError];
+            break;
+        case '[':
+            theResult = [self scanJSONArray:&theObject error:outError];
+            break;
+        default:
+            theResult = NO;
+            if (outError)
+                {
+                NSMutableDictionary *theUserInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
+                    @"Could not scan object. Character not a valid JSON character.", NSLocalizedDescriptionKey,
+                    NULL];
+                [theUserInfo addEntriesFromDictionary:self.userInfoForScanLocation];
+                *outError = [NSError errorWithDomain:kJSONScannerErrorDomain code:kJSONScannerErrorCode_CouldNotScanObject userInfo:theUserInfo];
+                }
+            break;
+        }
+
+    if (outObject != NULL)
+        *outObject = theObject;
+
+    return(theResult);
+    }
+
+- (BOOL)scanJSONDictionary:(NSDictionary **)outDictionary error:(NSError **)outError
+    {
+    NSUInteger theScanLocation = [self scanLocation];
+
+    [self skipWhitespace];
+
+    if ([self scanCharacter:'{'] == NO)
+        {
+        if (outError)
+            {
+            NSMutableDictionary *theUserInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
+                @"Could not scan dictionary. Dictionary that does not start with '{' character.", NSLocalizedDescriptionKey,
+                NULL];
+            [theUserInfo addEntriesFromDictionary:self.userInfoForScanLocation];
+            *outError = [NSError errorWithDomain:kJSONScannerErrorDomain code:kJSONScannerErrorCode_DictionaryStartCharacterMissing userInfo:theUserInfo];
+            }
+        return(NO);
+        }
+
+    NSMutableDictionary *theDictionary = [[NSMutableDictionary alloc] init];
+
+    while ([self currentCharacter] != '}')
+        {
+        [self skipWhitespace];
+        
+        if ([self currentCharacter] == '}')
+            break;
+
+        NSString *theKey = NULL;
+        if ([self scanJSONStringConstant:&theKey error:outError] == NO)
+            {
+            [self setScanLocation:theScanLocation];
+            if (outError)
+                {
+                NSMutableDictionary *theUserInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
+                    @"Could not scan dictionary. Failed to scan a key.", NSLocalizedDescriptionKey,
+                    NULL];
+                [theUserInfo addEntriesFromDictionary:self.userInfoForScanLocation];
+                *outError = [NSError errorWithDomain:kJSONScannerErrorDomain code:kJSONScannerErrorCode_DictionaryKeyScanFailed userInfo:theUserInfo];
+                }
+            [theDictionary release];
+            return(NO);
+            }
+
+        [self skipWhitespace];
+
+        if ([self scanCharacter:':'] == NO)
+            {
+            [self setScanLocation:theScanLocation];
+            if (outError)
+                {
+                NSMutableDictionary *theUserInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
+                    @"Could not scan dictionary. Key was not terminated with a ':' character.", NSLocalizedDescriptionKey,
+                    NULL];
+                [theUserInfo addEntriesFromDictionary:self.userInfoForScanLocation];
+                *outError = [NSError errorWithDomain:kJSONScannerErrorDomain code:kJSONScannerErrorCode_DictionaryKeyNotTerminated userInfo:theUserInfo];
+                }
+            [theDictionary release];
+            return(NO);
+            }
+
+        id theValue = NULL;
+        if ([self scanJSONObject:&theValue error:outError] == NO)
+            {
+            [self setScanLocation:theScanLocation];
+            if (outError)
+                {
+                NSMutableDictionary *theUserInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
+                    @"Could not scan dictionary. Failed to scan a value.", NSLocalizedDescriptionKey,
+                    NULL];
+                    
+                [theUserInfo addEntriesFromDictionary:self.userInfoForScanLocation];
+                *outError = [NSError errorWithDomain:kJSONScannerErrorDomain code:kJSONScannerErrorCode_DictionaryValueScanFailed userInfo:theUserInfo];
+                }
+            [theDictionary release];
+            return(NO);
+            }
+
+        if (theValue == NULL && self.nullObject == NULL)
+            {
+            // If the value is a null and nullObject is also null then we're skipping this key/value pair.
+            }
+        else
+            {
+            [theDictionary setValue:theValue forKey:theKey];
+            }
+
+        [self skipWhitespace];
+        if ([self scanCharacter:','] == NO)
+            {
+            if ([self currentCharacter] != '}')
+                {
+                [self setScanLocation:theScanLocation];
+                if (outError)
+                    {
+                    NSMutableDictionary *theUserInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
+                        @"Could not scan dictionary. Key value pairs not delimited with a ',' character.", NSLocalizedDescriptionKey,
+                        NULL];
+                    [theUserInfo addEntriesFromDictionary:self.userInfoForScanLocation];
+                    *outError = [NSError errorWithDomain:kJSONScannerErrorDomain code:kJSONScannerErrorCode_DictionaryKeyValuePairNoDelimiter userInfo:theUserInfo];
+                    }
+                [theDictionary release];
+                return(NO);
+                }
+            break;
+            }
+        else
+            {
+            [self skipWhitespace];
+            if ([self currentCharacter] == '}')
+                break;
+            }
+        }
+
+    if ([self scanCharacter:'}'] == NO)
+        {
+        [self setScanLocation:theScanLocation];
+        if (outError)
+            {
+            NSMutableDictionary *theUserInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
+                @"Could not scan dictionary. Dictionary not terminated by a '}' character.", NSLocalizedDescriptionKey,
+                NULL];
+            [theUserInfo addEntriesFromDictionary:self.userInfoForScanLocation];
+            *outError = [NSError errorWithDomain:kJSONScannerErrorDomain code:kJSONScannerErrorCode_DictionaryNotTerminated userInfo:theUserInfo];
+            }
+        [theDictionary release];
+        return(NO);
+        }
+
+    if (outDictionary != NULL)
+        {
+        if (self.options & kJSONScannerOptions_MutableContainers)
+            {
+            *outDictionary = [theDictionary autorelease];
+            }
+        else
+            {
+            *outDictionary = [[theDictionary copy] autorelease];
+            [theDictionary release];
+            }
+        }
+    else
+        {
+        [theDictionary release];
+        }
+
+    return(YES);
+    }
+
+- (BOOL)scanJSONArray:(NSArray **)outArray error:(NSError **)outError
+    {
+    NSUInteger theScanLocation = [self scanLocation];
+
+    [self skipWhitespace];
+
+    if ([self scanCharacter:'['] == NO)
+        {
+        if (outError)
+            {
+            NSMutableDictionary *theUserInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
+                @"Could not scan array. Array not started by a '[' character.", NSLocalizedDescriptionKey,
+                NULL];
+            [theUserInfo addEntriesFromDictionary:self.userInfoForScanLocation];
+            *outError = [NSError errorWithDomain:kJSONScannerErrorDomain code:kJSONScannerErrorCode_ArrayStartCharacterMissing userInfo:theUserInfo];
+            }
+        return(NO);
+        }
+
+    NSMutableArray *theArray = [[NSMutableArray alloc] init];
+
+    [self skipWhitespace];
+    while ([self currentCharacter] != ']')
+        {
+        NSString *theValue = NULL;
+        if ([self scanJSONObject:&theValue error:outError] == NO)
+            {
+            [self setScanLocation:theScanLocation];
+            if (outError)
+                {
+                NSMutableDictionary *theUserInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
+                    @"Could not scan array. Could not scan a value.", NSLocalizedDescriptionKey,
+                    NULL];
+                [theUserInfo addEntriesFromDictionary:self.userInfoForScanLocation];
+                *outError = [NSError errorWithDomain:kJSONScannerErrorDomain code:kJSONScannerErrorCode_ArrayValueScanFailed userInfo:theUserInfo];
+                }
+            [theArray release];
+            return(NO);
+            }
+            
+        if (theValue == NULL)
+            {
+            if (self.nullObject != NULL)
+                {
+                if (outError)
+                    {
+                    NSMutableDictionary *theUserInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
+                        @"Could not scan array. Value is NULL.", NSLocalizedDescriptionKey,
+                        NULL];
+                    [theUserInfo addEntriesFromDictionary:self.userInfoForScanLocation];
+                    *outError = [NSError errorWithDomain:kJSONScannerErrorDomain code:kJSONScannerErrorCode_ArrayValueIsNull userInfo:theUserInfo];
+                    }
+                [theArray release];
+                return(NO);
+                }
+            }
+        else
+            {
+            [theArray addObject:theValue];
+            }
+        
+        [self skipWhitespace];
+        if ([self scanCharacter:','] == NO)
+            {
+            [self skipWhitespace];
+            if ([self currentCharacter] != ']')
+                {
+                [self setScanLocation:theScanLocation];
+                if (outError)
+                    {
+                    NSMutableDictionary *theUserInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
+                        @"Could not scan array. Array not terminated by a ']' character.", NSLocalizedDescriptionKey,
+                        NULL];
+                    [theUserInfo addEntriesFromDictionary:self.userInfoForScanLocation];
+                    *outError = [NSError errorWithDomain:kJSONScannerErrorDomain code:kJSONScannerErrorCode_ArrayNotTerminated userInfo:theUserInfo];
+                    }
+                [theArray release];
+                return(NO);
+                }
+            
+            break;
+            }
+        [self skipWhitespace];
+        }
+
+    [self skipWhitespace];
+
+    if ([self scanCharacter:']'] == NO)
+        {
+        [self setScanLocation:theScanLocation];
+        if (outError)
+            {
+            NSMutableDictionary *theUserInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
+                @"Could not scan array. Array not terminated by a ']' character.", NSLocalizedDescriptionKey,
+                NULL];
+            [theUserInfo addEntriesFromDictionary:self.userInfoForScanLocation];
+            *outError = [NSError errorWithDomain:kJSONScannerErrorDomain code:kJSONScannerErrorCode_ArrayNotTerminated userInfo:theUserInfo];
+            }
+        [theArray release];
+        return(NO);
+        }
+
+    if (outArray != NULL)
+        {
+        if (self.options & kJSONScannerOptions_MutableContainers)
+            {
+            *outArray = [theArray autorelease];
+            }
+        else
+            {
+            *outArray = [[theArray copy] autorelease];
+            [theArray release];
+            }
+        }
+    else
+        {
+        [theArray release];
+        }
+    return(YES);
+    }
+
+- (BOOL)scanJSONStringConstant:(NSString **)outStringConstant error:(NSError **)outError
+    {
+    NSUInteger theScanLocation = [self scanLocation];
+
+    [self skipWhitespace];
+
+    NSMutableString *theString = [[NSMutableString alloc] init];
+
+    if ([self scanCharacter:'"'] == NO)
+        {
+        [self setScanLocation:theScanLocation];
+        if (outError)
+            {
+            NSMutableDictionary *theUserInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
+                @"Could not scan string constant. String not started by a '\"' character.", NSLocalizedDescriptionKey,
+                NULL];
+            [theUserInfo addEntriesFromDictionary:self.userInfoForScanLocation];
+            *outError = [NSError errorWithDomain:kJSONScannerErrorDomain code:kJSONScannerErrorCode_StringNotStartedWithBackslash userInfo:theUserInfo];
+            }
+        [theString release];
+        return(NO);
+        }
+
+    while ([self scanCharacter:'"'] == NO)
+        {
+        NSString *theStringChunk = NULL;
+        if ([self scanNotQuoteCharactersIntoString:&theStringChunk])
+            {
+            CFStringAppend((CFMutableStringRef)theString, (CFStringRef)theStringChunk);
+            }
+        else if ([self scanCharacter:'\\'] == YES)
+            {
+            unichar theCharacter = [self scanCharacter];
+            switch (theCharacter)
+                {
+                case '"':
+                case '\\':
+                case '/':
+                    break;
+                case 'b':
+                    theCharacter = '\b';
+                    break;
+                case 'f':
+                    theCharacter = '\f';
+                    break;
+                case 'n':
+                    theCharacter = '\n';
+                    break;
+                case 'r':
+                    theCharacter = '\r';
+                    break;
+                case 't':
+                    theCharacter = '\t';
+                    break;
+                case 'u':
+                    {
+                    theCharacter = 0;
+
+                    int theShift;
+                    for (theShift = 12; theShift >= 0; theShift -= 4)
+                        {
+                        const int theDigit = HexToInt([self scanCharacter]);
+                        if (theDigit == -1)
+                            {
+                            [self setScanLocation:theScanLocation];
+                            if (outError)
+                                {
+                                NSMutableDictionary *theUserInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
+                                    @"Could not scan string constant. Unicode character could not be decoded.", NSLocalizedDescriptionKey,
+                                    NULL];
+                                [theUserInfo addEntriesFromDictionary:self.userInfoForScanLocation];
+                                *outError = [NSError errorWithDomain:kJSONScannerErrorDomain code:kJSONScannerErrorCode_StringUnicodeNotDecoded userInfo:theUserInfo];
+                                }
+                            [theString release];
+                            return(NO);
+                            }
+                        theCharacter |= (theDigit << theShift);
+                        }
+                    }
+                    break;
+                default:
+                    {
+                    if (strictEscapeCodes == YES)
+                        {
+                        [self setScanLocation:theScanLocation];
+                        if (outError)
+                            {
+                            NSMutableDictionary *theUserInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
+                                @"Could not scan string constant. Unknown escape code.", NSLocalizedDescriptionKey,
+                                NULL];
+                            [theUserInfo addEntriesFromDictionary:self.userInfoForScanLocation];
+                            *outError = [NSError errorWithDomain:kJSONScannerErrorDomain code:kJSONScannerErrorCode_StringUnknownEscapeCode userInfo:theUserInfo];
+                            }
+                        [theString release];
+                        return(NO);
+                        }
+                    }
+                    break;
+                }
+            CFStringAppendCharacters((CFMutableStringRef)theString, &theCharacter, 1);
+            }
+        else
+            {
+            if (outError)
+                {
+                NSMutableDictionary *theUserInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
+                    @"Could not scan string constant. No terminating double quote character.", NSLocalizedDescriptionKey,
+                    NULL];
+                [theUserInfo addEntriesFromDictionary:self.userInfoForScanLocation];
+                *outError = [NSError errorWithDomain:kJSONScannerErrorDomain code:kJSONScannerErrorCode_StringNotTerminated userInfo:theUserInfo];
+                }
+            [theString release];
+            return(NO);
+            }
+        }
+        
+    if (outStringConstant != NULL)
+        {
+        if (self.options & kJSONScannerOptions_MutableLeaves)
+            {
+            *outStringConstant = [theString autorelease];
+            }
+        else
+            {
+            *outStringConstant = [[theString copy] autorelease];
+            [theString release];
+            }
+        }
+    else
+        {
+        [theString release];
+        }
+
+    return(YES);
+    }
+
+- (BOOL)scanJSONNumberConstant:(NSNumber **)outNumberConstant error:(NSError **)outError
+    {
+    NSNumber *theNumber = NULL;
+
+    [self skipWhitespace];
+
+    if ([self scanNumber:&theNumber] == YES)
+        {
+        if (outNumberConstant != NULL)
+            *outNumberConstant = theNumber;
+        return(YES);
+        }
+    else
+        {
+        if (outError)
+            {
+            NSMutableDictionary *theUserInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
+                @"Could not scan number constant.", NSLocalizedDescriptionKey,
+                NULL];
+            [theUserInfo addEntriesFromDictionary:self.userInfoForScanLocation];
+            *outError = [NSError errorWithDomain:kJSONScannerErrorDomain code:kJSONScannerErrorCode_NumberNotScannable userInfo:theUserInfo];
+            }
+        return(NO);
+        }
+    }
+
+#if TREAT_COMMENTS_AS_WHITESPACE
+- (void)skipWhitespace
+    {
+    [super skipWhitespace];
+    [self scanCStyleComment:NULL];
+    [self scanCPlusPlusStyleComment:NULL];
+    [super skipWhitespace];
+    }
+#endif // TREAT_COMMENTS_AS_WHITESPACE
+
+#pragma mark -
+
+- (BOOL)scanNotQuoteCharactersIntoString:(NSString **)outValue
+    {
+    u_int8_t *P;
+    for (P = current; P < end && *P != '\"' && *P != '\\'; ++P)
+        ;
+
+    if (P == current)
+        {
+        return(NO);
+        }
+
+    if (outValue)
+        {
+        *outValue = [[[NSString alloc] initWithBytes:current length:P - current encoding:NSUTF8StringEncoding] autorelease];
+        }
+        
+    current = P;
+
+    return(YES);
+    }
+
+@end
diff --git a/libs/TouchJSON/JSON/CJSONSerializer.h b/libs/TouchJSON/JSON/CJSONSerializer.h
new file mode 100644 (file)
index 0000000..748a85c
--- /dev/null
@@ -0,0 +1,53 @@
+//
+//  CJSONSerializer.h
+//  TouchCode
+//
+//  Created by Jonathan Wight on 12/07/2005.
+//  Copyright 2005 toxicsoftware.com. All rights reserved.
+//
+//  Permission is hereby granted, free of charge, to any person
+//  obtaining a copy of this software and associated documentation
+//  files (the "Software"), to deal in the Software without
+//  restriction, including without limitation the rights to use,
+//  copy, modify, merge, publish, distribute, sublicense, and/or sell
+//  copies of the Software, and to permit persons to whom the
+//  Software is furnished to do so, subject to the following
+//  conditions:
+//
+//  The above copyright notice and this permission notice shall be
+//  included in all copies or substantial portions of the Software.
+//
+//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+//  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+//  OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+//  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+//  HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+//  WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+//  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+//  OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#import <Foundation/Foundation.h>
+
+@interface CJSONSerializer : NSObject {
+}
+
++ (id)serializer;
+
+- (BOOL)isValidJSONObject:(id)inObject;
+
+/// Take any JSON compatible object (generally NSNull, NSNumber, NSString, NSArray and NSDictionary) and produce an NSData containing the serialized JSON.
+- (NSData *)serializeObject:(id)inObject error:(NSError **)outError;
+
+- (NSData *)serializeNull:(NSNull *)inNull error:(NSError **)outError;
+- (NSData *)serializeNumber:(NSNumber *)inNumber error:(NSError **)outError;
+- (NSData *)serializeString:(NSString *)inString error:(NSError **)outError;
+- (NSData *)serializeArray:(NSArray *)inArray error:(NSError **)outError;
+- (NSData *)serializeDictionary:(NSDictionary *)inDictionary error:(NSError **)outError;
+
+@end
+
+typedef enum {
+    CJSONSerializerErrorCouldNotSerializeDataType = -1,
+    CJSONSerializerErrorCouldNotSerializeObject = -1
+} CJSONSerializerError;
diff --git a/libs/TouchJSON/JSON/CJSONSerializer.m b/libs/TouchJSON/JSON/CJSONSerializer.m
new file mode 100644 (file)
index 0000000..952b3c2
--- /dev/null
@@ -0,0 +1,342 @@
+//
+//  CJSONSerializer.m
+//  TouchCode
+//
+//  Created by Jonathan Wight on 12/07/2005.
+//  Copyright 2005 toxicsoftware.com. All rights reserved.
+//
+//  Permission is hereby granted, free of charge, to any person
+//  obtaining a copy of this software and associated documentation
+//  files (the "Software"), to deal in the Software without
+//  restriction, including without limitation the rights to use,
+//  copy, modify, merge, publish, distribute, sublicense, and/or sell
+//  copies of the Software, and to permit persons to whom the
+//  Software is furnished to do so, subject to the following
+//  conditions:
+//
+//  The above copyright notice and this permission notice shall be
+//  included in all copies or substantial portions of the Software.
+//
+//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+//  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+//  OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+//  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+//  HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+//  WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+//  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+//  OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#import "CJSONSerializer.h"
+
+#import "JSONRepresentation.h"
+
+static NSData *kNULL = NULL;
+static NSData *kFalse = NULL;
+static NSData *kTrue = NULL;
+
+@implementation CJSONSerializer
+
++ (void)initialize
+    {
+    NSAutoreleasePool *thePool = [[NSAutoreleasePool alloc] init];
+
+    if (self == [CJSONSerializer class])
+        {
+        if (kNULL == NULL)
+            kNULL = [[NSData alloc] initWithBytesNoCopy:(void *)"null" length:4 freeWhenDone:NO];
+        if (kFalse == NULL)
+            kFalse = [[NSData alloc] initWithBytesNoCopy:(void *)"false" length:5 freeWhenDone:NO];
+        if (kTrue == NULL)
+            kTrue = [[NSData alloc] initWithBytesNoCopy:(void *)"true" length:4 freeWhenDone:NO];
+
+        [thePool release];
+        }
+    }
+
++ (id)serializer
+    {
+    return([[[self alloc] init] autorelease]);
+    }
+    
+- (BOOL)isValidJSONObject:(id)inObject
+    {
+    if ([inObject isKindOfClass:[NSNull class]])
+        {
+        return(YES);
+        }
+    else if ([inObject isKindOfClass:[NSNumber class]])
+        {
+        return(YES);
+        }
+    else if ([inObject isKindOfClass:[NSString class]])
+        {
+        return(YES);
+        }
+    else if ([inObject isKindOfClass:[NSArray class]])
+        {
+        return(YES);
+        }
+    else if ([inObject isKindOfClass:[NSDictionary class]])
+        {
+        return(YES);
+        }
+    else if ([inObject isKindOfClass:[NSData class]])
+        {
+        return(YES);
+        }
+    else if ([inObject respondsToSelector:@selector(JSONDataRepresentation)])
+        {
+        return(YES);
+        }
+    else
+        {
+        return(NO);
+        }
+    }
+
+- (NSData *)serializeObject:(id)inObject error:(NSError **)outError
+    {
+    NSData *theResult = NULL;
+
+    if ([inObject isKindOfClass:[NSNull class]])
+        {
+        theResult = [self serializeNull:inObject error:outError];
+        }
+    else if ([inObject isKindOfClass:[NSNumber class]])
+        {
+        theResult = [self serializeNumber:inObject error:outError];
+        }
+    else if ([inObject isKindOfClass:[NSString class]])
+        {
+        theResult = [self serializeString:inObject error:outError];
+        }
+    else if ([inObject isKindOfClass:[NSArray class]])
+        {
+        theResult = [self serializeArray:inObject error:outError];
+        }
+    else if ([inObject isKindOfClass:[NSDictionary class]])
+        {
+        theResult = [self serializeDictionary:inObject error:outError];
+        }
+    else if ([inObject isKindOfClass:[NSData class]])
+        {
+        NSString *theString = [[[NSString alloc] initWithData:inObject encoding:NSUTF8StringEncoding] autorelease];
+        theResult = [self serializeString:theString error:outError];
+        }
+    else if ([inObject respondsToSelector:@selector(JSONDataRepresentation)])
+        {
+        theResult = [inObject JSONDataRepresentation];
+        }
+    else
+        {
+        if (outError)
+            {
+            NSDictionary *theUserInfo = [NSDictionary dictionaryWithObjectsAndKeys:
+                [NSString stringWithFormat:@"Cannot serialize data of type '%@'", NSStringFromClass([inObject class])], NSLocalizedDescriptionKey,
+                NULL];
+            *outError = [NSError errorWithDomain:@"TODO_DOMAIN" code:CJSONSerializerErrorCouldNotSerializeDataType userInfo:theUserInfo];
+            }
+        return(NULL);
+        }
+    if (theResult == NULL)
+        {
+        if (outError)
+            {
+            NSDictionary *theUserInfo = [NSDictionary dictionaryWithObjectsAndKeys:
+                [NSString stringWithFormat:@"Could not serialize object '%@'", inObject], NSLocalizedDescriptionKey,
+                NULL];
+            *outError = [NSError errorWithDomain:@"TODO_DOMAIN" code:CJSONSerializerErrorCouldNotSerializeObject userInfo:theUserInfo];
+            }
+        return(NULL);
+        }
+    return(theResult);
+    }
+
+- (NSData *)serializeNull:(NSNull *)inNull error:(NSError **)outError
+    {
+    #pragma unused (inNull, outError)
+    return(kNULL);
+    }
+
+- (NSData *)serializeNumber:(NSNumber *)inNumber error:(NSError **)outError
+    {
+    #pragma unused (outError)
+    NSData *theResult = NULL;
+    switch (CFNumberGetType((CFNumberRef)inNumber))
+        {
+        case kCFNumberCharType:
+            {
+            int theValue = [inNumber intValue];
+            if (theValue == 0)
+                theResult = kFalse;
+            else if (theValue == 1)
+                theResult = kTrue;
+            else
+                theResult = [[inNumber stringValue] dataUsingEncoding:NSASCIIStringEncoding];
+            }
+            break;
+        case kCFNumberFloat32Type:
+        case kCFNumberFloat64Type:
+        case kCFNumberFloatType:
+        case kCFNumberDoubleType:
+        case kCFNumberSInt8Type:
+        case kCFNumberSInt16Type:
+        case kCFNumberSInt32Type:
+        case kCFNumberSInt64Type:
+        case kCFNumberShortType:
+        case kCFNumberIntType:
+        case kCFNumberLongType:
+        case kCFNumberLongLongType:
+        case kCFNumberCFIndexType:
+        default:
+            theResult = [[inNumber stringValue] dataUsingEncoding:NSASCIIStringEncoding];
+            break;
+        }
+    return(theResult);
+    }
+
+- (NSData *)serializeString:(NSString *)inString error:(NSError **)outError
+    {
+    #pragma unused (outError)
+
+    const char *theUTF8String = [inString UTF8String];
+
+    NSMutableData *theData = [NSMutableData dataWithLength:strlen(theUTF8String) * 2 + 2];
+
+    char *theOutputStart = [theData mutableBytes];
+    char *OUT = theOutputStart;
+
+    *OUT++ = '"';
+
+    for (const char *IN = theUTF8String; IN && *IN != '\0'; ++IN)
+        {
+        switch (*IN)
+            {
+            case '\\':
+                {
+                *OUT++ = '\\';
+                *OUT++ = '\\';
+                }
+                break;
+            case '\"':
+                {
+                *OUT++ = '\\';
+                *OUT++ = '\"';
+                }
+                break;
+            case '/':
+                {
+                *OUT++ = '\\';
+                *OUT++ = '/';
+                }
+                break;
+            case '\b':
+                {
+                *OUT++ = '\\';
+                *OUT++ = 'b';
+                }
+                break;
+            case '\f':
+                {
+                *OUT++ = '\\';
+                *OUT++ = 'f';
+                }
+                break;
+            case '\n':
+                {
+                *OUT++ = '\\';
+                *OUT++ = 'n';
+                }
+                break;
+            case '\r':
+                {
+                *OUT++ = '\\';
+                *OUT++ = 'r';
+                }
+                break;
+            case '\t':
+                {
+                *OUT++ = '\\';
+                *OUT++ = 't';
+                }
+                break;
+            default:
+                {
+                *OUT++ = *IN;
+                }
+                break;
+            }
+        }
+
+    *OUT++ = '"';
+
+    theData.length = OUT - theOutputStart;
+    return(theData);
+    }
+
+- (NSData *)serializeArray:(NSArray *)inArray error:(NSError **)outError
+    {
+    NSMutableData *theData = [NSMutableData data];
+
+    [theData appendBytes:"[" length:1];
+
+    NSEnumerator *theEnumerator = [inArray objectEnumerator];
+    id theValue = NULL;
+    NSUInteger i = 0;
+    while ((theValue = [theEnumerator nextObject]) != NULL)
+        {
+        NSData *theValueData = [self serializeObject:theValue error:outError];
+        if (theValueData == NULL)
+            {
+            return(NULL);
+            }
+        [theData appendData:theValueData];
+        if (++i < [inArray count])
+            [theData appendBytes:"," length:1];
+        }
+
+    [theData appendBytes:"]" length:1];
+
+    return(theData);
+    }
+
+- (NSData *)serializeDictionary:(NSDictionary *)inDictionary error:(NSError **)outError
+    {
+    NSMutableData *theData = [NSMutableData data];
+
+    [theData appendBytes:"{" length:1];
+
+    NSArray *theKeys = [inDictionary allKeys];
+    NSEnumerator *theEnumerator = [theKeys objectEnumerator];
+    NSString *theKey = NULL;
+    while ((theKey = [theEnumerator nextObject]) != NULL)
+        {
+        id theValue = [inDictionary objectForKey:theKey];
+        
+        NSData *theKeyData = [self serializeString:theKey error:outError];
+        if (theKeyData == NULL)
+            {
+            return(NULL);
+            }
+        NSData *theValueData = [self serializeObject:theValue error:outError];
+        if (theValueData == NULL)
+            {
+            return(NULL);
+            }
+        
+        
+        [theData appendData:theKeyData];
+        [theData appendBytes:":" length:1];
+        [theData appendData:theValueData];
+        
+        if (theKey != [theKeys lastObject])
+            [theData appendData:[@"," dataUsingEncoding:NSASCIIStringEncoding]];
+        }
+
+    [theData appendBytes:"}" length:1];
+
+    return(theData);
+    }
+
+@end
diff --git a/libs/TouchJSON/JSON/JSONRepresentation.h b/libs/TouchJSON/JSON/JSONRepresentation.h
new file mode 100644 (file)
index 0000000..a83d76f
--- /dev/null
@@ -0,0 +1,18 @@
+//
+//  JSONRepresentation.h
+//  TouchJSON
+//
+//  Created by Jonathan Wight on 10/15/10.
+//  Copyright 2010 toxicsoftware.com. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+
+@protocol JSONRepresentation
+
+@optional
+- (id)initWithJSONDataRepresentation:(NSData *)inJSONData;
+
+- (NSData *)JSONDataRepresentation;
+
+@end
diff --git a/libs/cocos2d/CCAction.h b/libs/cocos2d/CCAction.h
new file mode 100644 (file)
index 0000000..51bad8e
--- /dev/null
@@ -0,0 +1,195 @@
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2008-2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+
+#include <sys/time.h>
+#import <Foundation/Foundation.h>
+
+#import "ccTypes.h"
+
+enum {
+       //! Default tag
+       kCCActionTagInvalid = -1,
+};
+
+/** Base class for CCAction objects.
+ */
+@interface CCAction : NSObject <NSCopying>
+{
+       id                      originalTarget_;
+       id                      target_;
+       NSInteger       tag_;
+}
+
+/** The "target". The action will modify the target properties.
+ The target will be set with the 'startWithTarget' method.
+ When the 'stop' method is called, target will be set to nil.
+ The target is 'assigned', it is not 'retained'.
+ */
+@property (nonatomic,readonly,assign) id target;
+
+/** The original target, since target can be nil.
+ Is the target that were used to run the action. Unless you are doing something complex, like CCActionManager, you should NOT call this method.
+ @since v0.8.2
+*/
+@property (nonatomic,readonly,assign) id originalTarget;
+
+
+/** The action tag. An identifier of the action */
+@property (nonatomic,readwrite,assign) NSInteger tag;
+
+/** Allocates and initializes the action */
++(id) action;
+
+/** Initializes the action */
+-(id) init;
+
+-(id) copyWithZone: (NSZone*) zone;
+
+//! return YES if the action has finished
+-(BOOL) isDone;
+//! called before the action start. It will also set the target.
+-(void) startWithTarget:(id)target;
+//! called after the action has finished. It will set the 'target' to nil.
+//! IMPORTANT: You should never call "[action stop]" manually. Instead, use: "[target stopAction:action];"
+-(void) stop;
+//! called every frame with it's delta time. DON'T override unless you know what you are doing.
+-(void) step: (ccTime) dt;
+//! called once per frame. time a value between 0 and 1
+//! For example: 
+//! * 0 means that the action just started
+//! * 0.5 means that the action is in the middle
+//! * 1 means that the action is over
+-(void) update: (ccTime) time;
+
+@end
+
+/** Base class actions that do have a finite time duration.
+ Possible actions:
+   - An action with a duration of 0 seconds
+   - An action with a duration of 35.5 seconds
+ Infitite time actions are valid
+ */
+@interface CCFiniteTimeAction : CCAction <NSCopying>
+{
+       //! duration in seconds
+       ccTime duration_;
+}
+//! duration in seconds of the action
+@property (nonatomic,readwrite) ccTime duration;
+
+/** returns a reversed action */
+- (CCFiniteTimeAction*) reverse;
+@end
+
+
+@class CCActionInterval;
+/** Repeats an action for ever.
+ To repeat the an action for a limited number of times use the Repeat action.
+ @warning This action can't be Sequenceable because it is not an IntervalAction
+ */
+@interface CCRepeatForever : CCAction <NSCopying>
+{
+       CCActionInterval *innerAction_;
+}
+/** Inner action */
+@property (nonatomic, readwrite, retain) CCActionInterval *innerAction;
+
+/** creates the action */
++(id) actionWithAction: (CCActionInterval*) action;
+/** initializes the action */
+-(id) initWithAction: (CCActionInterval*) action;
+@end
+
+/** Changes the speed of an action, making it take longer (speed>1)
+ or less (speed<1) time.
+ Useful to simulate 'slow motion' or 'fast forward' effect.
+ @warning This action can't be Sequenceable because it is not an CCIntervalAction
+ */
+@interface CCSpeed : CCAction <NSCopying>
+{
+       CCActionInterval        *innerAction_;
+       float speed_;
+}
+/** alter the speed of the inner function in runtime */
+@property (nonatomic,readwrite) float speed;
+/** Inner action of CCSpeed */
+@property (nonatomic, readwrite, retain) CCActionInterval *innerAction;
+
+/** creates the action */
++(id) actionWithAction: (CCActionInterval*) action speed:(float)rate;
+/** initializes the action */
+-(id) initWithAction: (CCActionInterval*) action speed:(float)rate;
+@end
+
+@class CCNode;
+/** CCFollow is an action that "follows" a node.
+ Eg:
+       [layer runAction: [CCFollow actionWithTarget:hero]];
+ Instead of using CCCamera as a "follower", use this action instead.
+ @since v0.99.2
+ */
+@interface CCFollow : CCAction <NSCopying>
+{
+       /* node to follow */
+       CCNode  *followedNode_;
+       
+       /* whether camera should be limited to certain area */
+       BOOL boundarySet;
+       
+       /* if screensize is bigger than the boundary - update not needed */
+       BOOL boundaryFullyCovered;
+       
+       /* fast access to the screen dimensions */
+       CGPoint halfScreenSize;
+       CGPoint fullScreenSize;
+       
+       /* world boundaries */
+       float leftBoundary;
+       float rightBoundary;
+       float topBoundary;
+       float bottomBoundary;
+}
+
+/** alter behavior - turn on/off boundary */
+@property (nonatomic,readwrite) BOOL boundarySet;
+
+/** creates the action with no boundary set */
++(id) actionWithTarget:(CCNode *)followedNode;
+
+/** creates the action with a set boundary */
++(id) actionWithTarget:(CCNode *)followedNode worldBoundary:(CGRect)rect;
+
+/** initializes the action */
+-(id) initWithTarget:(CCNode *)followedNode;
+
+/** initializes the action with a set boundary */
+-(id) initWithTarget:(CCNode *)followedNode worldBoundary:(CGRect)rect;
+
+@end
+
diff --git a/libs/cocos2d/CCAction.m b/libs/cocos2d/CCAction.m
new file mode 100644 (file)
index 0000000..0187dbd
--- /dev/null
@@ -0,0 +1,362 @@
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2008-2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+
+
+#import <Availability.h>
+#import "CCDirector.h"
+#import "ccMacros.h"
+#import "CCAction.h"
+#import "CCActionInterval.h"
+#import "Support/CGPointExtension.h"
+
+//
+// Action Base Class
+//
+#pragma mark -
+#pragma mark Action
+@implementation CCAction
+
+@synthesize tag = tag_, target = target_, originalTarget = originalTarget_;
+
++(id) action
+{
+       return [[[self alloc] init] autorelease];
+}
+
+-(id) init
+{
+       if( (self=[super init]) ) {     
+               originalTarget_ = target_ = nil;
+               tag_ = kCCActionTagInvalid;
+       }
+       return self;
+}
+
+-(void) dealloc
+{
+       CCLOGINFO(@"cocos2d: deallocing %@", self);
+       [super dealloc];
+}
+
+-(NSString*) description
+{
+       return [NSString stringWithFormat:@"<%@ = %08X | Tag = %i>", [self class], self, tag_];
+}
+
+-(id) copyWithZone: (NSZone*) zone
+{
+       CCAction *copy = [[[self class] allocWithZone: zone] init];
+       copy.tag = tag_;
+       return copy;
+}
+
+-(void) startWithTarget:(id)aTarget
+{
+       originalTarget_ = target_ = aTarget;
+}
+
+-(void) stop
+{
+       target_ = nil;
+}
+
+-(BOOL) isDone
+{
+       return YES;
+}
+
+-(void) step: (ccTime) dt
+{
+       NSLog(@"[Action step]. override me");
+}
+
+-(void) update: (ccTime) time
+{
+       NSLog(@"[Action update]. override me");
+}
+@end
+
+//
+// FiniteTimeAction
+//
+#pragma mark -
+#pragma mark FiniteTimeAction
+@implementation CCFiniteTimeAction
+@synthesize duration = duration_;
+
+- (CCFiniteTimeAction*) reverse
+{
+       CCLOG(@"cocos2d: FiniteTimeAction#reverse: Implement me");
+       return nil;
+}
+@end
+
+
+//
+// RepeatForever
+//
+#pragma mark -
+#pragma mark RepeatForever
+@implementation CCRepeatForever
+@synthesize innerAction=innerAction_;
++(id) actionWithAction: (CCActionInterval*) action
+{
+       return [[[self alloc] initWithAction: action] autorelease];
+}
+
+-(id) initWithAction: (CCActionInterval*) action
+{
+       if( (self=[super init]) )       
+               self.innerAction = action;
+
+       return self;
+}
+
+-(id) copyWithZone: (NSZone*) zone
+{
+       CCAction *copy = [[[self class] allocWithZone: zone] initWithAction:[[innerAction_ copy] autorelease] ];
+    return copy;
+}
+
+-(void) dealloc
+{
+       [innerAction_ release];
+       [super dealloc];
+}
+
+-(void) startWithTarget:(id)aTarget
+{
+       [super startWithTarget:aTarget];
+       [innerAction_ startWithTarget:target_];
+}
+
+-(void) step:(ccTime) dt
+{
+       [innerAction_ step: dt];
+       if( [innerAction_ isDone] ) {
+               ccTime diff = dt + innerAction_.duration - innerAction_.elapsed;
+               [innerAction_ startWithTarget:target_];
+               
+               // to prevent jerk. issue #390
+               [innerAction_ step: diff];
+       }
+}
+
+
+-(BOOL) isDone
+{
+       return NO;
+}
+
+- (CCActionInterval *) reverse
+{
+       return [CCRepeatForever actionWithAction:[innerAction_ reverse]];
+}
+@end
+
+//
+// Speed
+//
+#pragma mark -
+#pragma mark Speed
+@implementation CCSpeed
+@synthesize speed=speed_;
+@synthesize innerAction=innerAction_;
+
++(id) actionWithAction: (CCActionInterval*) action speed:(float)r
+{
+       return [[[self alloc] initWithAction: action speed:r] autorelease];
+}
+
+-(id) initWithAction: (CCActionInterval*) action speed:(float)r
+{
+       if( (self=[super init]) ) {
+               self.innerAction = action;
+               speed_ = r;
+       }
+       return self;
+}
+
+-(id) copyWithZone: (NSZone*) zone
+{
+       CCAction *copy = [[[self class] allocWithZone: zone] initWithAction:[[innerAction_ copy] autorelease] speed:speed_];
+    return copy;
+}
+
+-(void) dealloc
+{
+       [innerAction_ release];
+       [super dealloc];
+}
+
+-(void) startWithTarget:(id)aTarget
+{
+       [super startWithTarget:aTarget];
+       [innerAction_ startWithTarget:target_];
+}
+
+-(void) stop
+{
+       [innerAction_ stop];
+       [super stop];
+}
+
+-(void) step:(ccTime) dt
+{
+       [innerAction_ step: dt * speed_];
+}
+
+-(BOOL) isDone
+{
+       return [innerAction_ isDone];
+}
+
+- (CCActionInterval *) reverse
+{
+       return [CCSpeed actionWithAction:[innerAction_ reverse] speed:speed_];
+}
+@end
+
+//
+// Follow
+//
+#pragma mark -
+#pragma mark Follow
+@implementation CCFollow
+
+@synthesize boundarySet;
+
++(id) actionWithTarget:(CCNode *) fNode
+{
+       return [[[self alloc] initWithTarget:fNode] autorelease];
+}
+
++(id) actionWithTarget:(CCNode *) fNode worldBoundary:(CGRect)rect
+{
+       return [[[self alloc] initWithTarget:fNode worldBoundary:rect] autorelease];
+}
+
+-(id) initWithTarget:(CCNode *)fNode
+{
+       if( (self=[super init]) ) {
+       
+               followedNode_ = [fNode retain];
+               boundarySet = FALSE;
+               boundaryFullyCovered = FALSE;
+               
+               CGSize s = [[CCDirector sharedDirector] winSize];
+               fullScreenSize = CGPointMake(s.width, s.height);
+               halfScreenSize = ccpMult(fullScreenSize, .5f);
+       }
+       
+       return self;
+}
+
+-(id) initWithTarget:(CCNode *)fNode worldBoundary:(CGRect)rect
+{
+       if( (self=[super init]) ) {
+       
+               followedNode_ = [fNode retain];
+               boundarySet = TRUE;
+               boundaryFullyCovered = FALSE;
+               
+               CGSize winSize = [[CCDirector sharedDirector] winSize];
+               fullScreenSize = CGPointMake(winSize.width, winSize.height);
+               halfScreenSize = ccpMult(fullScreenSize, .5f);
+               
+               leftBoundary = -((rect.origin.x+rect.size.width) - fullScreenSize.x);
+               rightBoundary = -rect.origin.x ;
+               topBoundary = -rect.origin.y;
+               bottomBoundary = -((rect.origin.y+rect.size.height) - fullScreenSize.y);
+               
+               if(rightBoundary < leftBoundary)
+               {
+                       // screen width is larger than world's boundary width
+                       //set both in the middle of the world
+                       rightBoundary = leftBoundary = (leftBoundary + rightBoundary) / 2;
+               }
+               if(topBoundary < bottomBoundary)
+               {
+                       // screen width is larger than world's boundary width
+                       //set both in the middle of the world
+                       topBoundary = bottomBoundary = (topBoundary + bottomBoundary) / 2;
+               }
+               
+               if( (topBoundary == bottomBoundary) && (leftBoundary == rightBoundary) )
+                       boundaryFullyCovered = TRUE;
+       }
+       
+       return self;
+}
+
+-(id) copyWithZone: (NSZone*) zone
+{
+       CCAction *copy = [[[self class] allocWithZone: zone] init];
+       copy.tag = tag_;
+       return copy;
+}
+
+-(void) step:(ccTime) dt
+{
+#define CLAMP(x,y,z) MIN(MAX(x,y),z)
+       
+       if(boundarySet)
+       {
+               // whole map fits inside a single screen, no need to modify the position - unless map boundaries are increased
+               if(boundaryFullyCovered)
+                       return;
+               
+               CGPoint tempPos = ccpSub( halfScreenSize, followedNode_.position);
+               [target_ setPosition:ccp(CLAMP(tempPos.x,leftBoundary,rightBoundary), CLAMP(tempPos.y,bottomBoundary,topBoundary))];
+       }
+       else
+               [target_ setPosition:ccpSub( halfScreenSize, followedNode_.position )];
+       
+#undef CLAMP
+}
+
+
+-(BOOL) isDone
+{
+       return !followedNode_.isRunning;
+}
+
+-(void) stop
+{
+       target_ = nil;
+       [super stop];
+}
+
+-(void) dealloc
+{
+       [followedNode_ release];
+       [super dealloc];
+}
+
+@end
+
+
diff --git a/libs/cocos2d/CCActionCamera.h b/libs/cocos2d/CCActionCamera.h
new file mode 100644 (file)
index 0000000..1ea83a7
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2008-2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+#import "CCActionInterval.h"
+
+@class CCCamera;
+
+/** Base class for CCCamera actions
+ */
+@interface CCActionCamera : CCActionInterval <NSCopying>
+{      
+       float centerXOrig_;
+       float centerYOrig_;
+       float centerZOrig_;
+       
+       float eyeXOrig_;
+       float eyeYOrig_;
+       float eyeZOrig_;
+       
+       float upXOrig_;
+       float upYOrig_;
+       float upZOrig_;
+}
+@end
+
+/** CCOrbitCamera action
+ Orbits the camera around the center of the screen using spherical coordinates
+ */
+@interface CCOrbitCamera : CCActionCamera <NSCopying>
+{
+       float radius_;
+       float deltaRadius_;
+       float angleZ_;
+       float deltaAngleZ_;
+       float angleX_;
+       float deltaAngleX_;
+       
+       float radZ_;
+       float radDeltaZ_;
+       float radX_;
+       float radDeltaX_;
+       
+}
+/** creates a CCOrbitCamera action with radius, delta-radius,  z, deltaZ, x, deltaX */
++(id) actionWithDuration:(float) t radius:(float)r deltaRadius:(float) dr angleZ:(float)z deltaAngleZ:(float)dz angleX:(float)x deltaAngleX:(float)dx;
+/** initializes a CCOrbitCamera action with radius, delta-radius,  z, deltaZ, x, deltaX */
+-(id) initWithDuration:(float) t radius:(float)r deltaRadius:(float) dr angleZ:(float)z deltaAngleZ:(float)dz angleX:(float)x deltaAngleX:(float)dx;
+/** positions the camera according to spherical coordinates */
+-(void) sphericalRadius:(float*) r zenith:(float*) zenith azimuth:(float*) azimuth;
+@end
diff --git a/libs/cocos2d/CCActionCamera.m b/libs/cocos2d/CCActionCamera.m
new file mode 100644 (file)
index 0000000..4dafc4e
--- /dev/null
@@ -0,0 +1,147 @@
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2008-2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+
+
+#import "CCActionCamera.h"
+#import "CCNode.h"
+#import "CCCamera.h"
+#import "ccMacros.h"
+
+//
+// CameraAction
+//
+@implementation CCActionCamera
+-(void) startWithTarget:(id)aTarget
+{
+       [super startWithTarget:aTarget];
+       CCCamera *camera = [target_ camera];
+       [camera centerX:&centerXOrig_ centerY:&centerYOrig_ centerZ:&centerZOrig_];
+       [camera eyeX:&eyeXOrig_ eyeY:&eyeYOrig_ eyeZ:&eyeZOrig_];
+       [camera upX:&upXOrig_ upY:&upYOrig_ upZ: &upZOrig_];
+}
+
+-(id) reverse
+{
+       return [CCReverseTime actionWithAction:self];
+}
+@end
+
+@implementation CCOrbitCamera
++(id) actionWithDuration:(float)t radius:(float)r deltaRadius:(float) dr angleZ:(float)z deltaAngleZ:(float)dz angleX:(float)x deltaAngleX:(float)dx
+{
+       return [[[self alloc] initWithDuration:t radius:r deltaRadius:dr angleZ:z deltaAngleZ:dz angleX:x deltaAngleX:dx] autorelease];
+}
+
+-(id) copyWithZone: (NSZone*) zone
+{
+       return [[[self class] allocWithZone: zone] initWithDuration:duration_ radius:radius_ deltaRadius:deltaRadius_ angleZ:angleZ_ deltaAngleZ:deltaAngleZ_ angleX:angleX_ deltaAngleX:deltaAngleX_];
+}
+
+
+-(id) initWithDuration:(float)t radius:(float)r deltaRadius:(float) dr angleZ:(float)z deltaAngleZ:(float)dz angleX:(float)x deltaAngleX:(float)dx
+{
+       if((self=[super initWithDuration:t]) ) {
+       
+               radius_ = r;
+               deltaRadius_ = dr;
+               angleZ_ = z;
+               deltaAngleZ_ = dz;
+               angleX_ = x;
+               deltaAngleX_ = dx;
+
+               radDeltaZ_ = (CGFloat)CC_DEGREES_TO_RADIANS(dz);
+               radDeltaX_ = (CGFloat)CC_DEGREES_TO_RADIANS(dx);
+       }
+       
+       return self;
+}
+
+-(void) startWithTarget:(id)aTarget
+{
+       [super startWithTarget:aTarget];
+       float r, zenith, azimuth;
+       
+       [self sphericalRadius: &r zenith:&zenith azimuth:&azimuth];
+       
+#if 0 // isnan() is not supported on the simulator, and isnan() always returns false.
+       if( isnan(radius_) )
+               radius_ = r;
+       
+       if( isnan( angleZ_) )
+               angleZ_ = (CGFloat)CC_RADIANS_TO_DEGREES(zenith);
+       
+       if( isnan( angleX_ ) )
+               angleX_ = (CGFloat)CC_RADIANS_TO_DEGREES(azimuth);
+#endif
+
+       radZ_ = (CGFloat)CC_DEGREES_TO_RADIANS(angleZ_);
+       radX_ = (CGFloat)CC_DEGREES_TO_RADIANS(angleX_);
+}
+
+-(void) update: (ccTime) dt
+{
+       float r = (radius_ + deltaRadius_ * dt) *[CCCamera getZEye];
+       float za = radZ_ + radDeltaZ_ * dt;
+       float xa = radX_ + radDeltaX_ * dt;
+
+       float i = sinf(za) * cosf(xa) * r + centerXOrig_;
+       float j = sinf(za) * sinf(xa) * r + centerYOrig_;
+       float k = cosf(za) * r + centerZOrig_;
+
+       [[target_ camera] setEyeX:i eyeY:j eyeZ:k];     
+}
+
+-(void) sphericalRadius:(float*) newRadius zenith:(float*) zenith azimuth:(float*) azimuth
+{
+       float ex, ey, ez, cx, cy, cz, x, y, z;
+       float r; // radius
+       float s;
+       
+       CCCamera *camera = [target_ camera];
+       [camera eyeX:&ex eyeY:&ey eyeZ:&ez];
+       [camera centerX:&cx centerY:&cy centerZ:&cz];
+       
+       x = ex-cx;
+       y = ey-cy;
+       z = ez-cz;
+       
+       r = sqrtf( x*x + y*y + z*z);
+       s = sqrtf( x*x + y*y);
+       if(s==0.0f)
+               s = FLT_EPSILON;
+       if(r==0.0f)
+               r = FLT_EPSILON;
+
+       *zenith = acosf( z/r);
+       if( x < 0 )
+               *azimuth = (float)M_PI - asinf(y/s);
+       else
+               *azimuth = asinf(y/s);
+                                       
+       *newRadius = r / [CCCamera getZEye];                                    
+}
+@end
diff --git a/libs/cocos2d/CCActionEase.h b/libs/cocos2d/CCActionEase.h
new file mode 100644 (file)
index 0000000..fced701
--- /dev/null
@@ -0,0 +1,159 @@
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2008-2009 Jason Booth
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+
+#import "CCActionInterval.h"
+
+/** Base class for Easing actions
+ */
+@interface CCActionEase : CCActionInterval <NSCopying>
+{
+       CCActionInterval * other;
+}
+/** creates the action */
++(id) actionWithAction: (CCActionInterval*) action;
+/** initializes the action */
+-(id) initWithAction: (CCActionInterval*) action;
+@end
+
+/** Base class for Easing actions with rate parameters
+ */
+@interface CCEaseRateAction :  CCActionEase <NSCopying>
+{
+       float   rate;
+}
+/** rate value for the actions */
+@property (nonatomic,readwrite,assign) float rate;
+/** Creates the action with the inner action and the rate parameter */
++(id) actionWithAction: (CCActionInterval*) action rate:(float)rate;
+/** Initializes the action with the inner action and the rate parameter */
+-(id) initWithAction: (CCActionInterval*) action rate:(float)rate;
+@end
+
+/** CCEaseIn action with a rate
+ */
+@interface CCEaseIn : CCEaseRateAction <NSCopying> {} @end
+
+/** CCEaseOut action with a rate
+ */
+@interface CCEaseOut : CCEaseRateAction <NSCopying> {} @end
+
+/** CCEaseInOut action with a rate
+ */
+@interface CCEaseInOut : CCEaseRateAction <NSCopying> {} @end
+
+/** CCEase Exponential In
+ */
+@interface CCEaseExponentialIn : CCActionEase <NSCopying> {} @end
+/** Ease Exponential Out
+ */
+@interface CCEaseExponentialOut : CCActionEase <NSCopying> {} @end
+/** Ease Exponential InOut
+ */
+@interface CCEaseExponentialInOut : CCActionEase <NSCopying> {} @end
+/** Ease Sine In
+ */
+@interface CCEaseSineIn : CCActionEase <NSCopying> {} @end
+/** Ease Sine Out
+ */
+@interface CCEaseSineOut : CCActionEase <NSCopying> {} @end
+/** Ease Sine InOut
+ */
+@interface CCEaseSineInOut : CCActionEase <NSCopying> {} @end
+
+/** Ease Elastic abstract class
+ @since v0.8.2
+ */
+@interface CCEaseElastic : CCActionEase <NSCopying>
+{
+       float period_;
+}
+
+/** period of the wave in radians. default is 0.3 */
+@property (nonatomic,readwrite) float period;
+
+/** Creates the action with the inner action and the period in radians (default is 0.3) */
++(id) actionWithAction: (CCActionInterval*) action period:(float)period;
+/** Initializes the action with the inner action and the period in radians (default is 0.3) */
+-(id) initWithAction: (CCActionInterval*) action period:(float)period;
+@end
+
+/** Ease Elastic In action.
+ @warning This action doesn't use a bijective fucntion. Actions like Sequence might have an unexpected result when used with this action.
+ @since v0.8.2
+ */
+@interface CCEaseElasticIn : CCEaseElastic <NSCopying> {} @end
+/** Ease Elastic Out action.
+ @warning This action doesn't use a bijective fucntion. Actions like Sequence might have an unexpected result when used with this action.
+ @since v0.8.2
+ */
+@interface CCEaseElasticOut : CCEaseElastic <NSCopying> {} @end
+/** Ease Elastic InOut action.
+ @warning This action doesn't use a bijective fucntion. Actions like Sequence might have an unexpected result when used with this action.
+ @since v0.8.2
+ */
+@interface CCEaseElasticInOut : CCEaseElastic <NSCopying> {} @end
+
+/** CCEaseBounce abstract class.
+ @since v0.8.2
+*/
+@interface CCEaseBounce : CCActionEase <NSCopying> {} @end
+
+/** CCEaseBounceIn action.
+ @warning This action doesn't use a bijective fucntion. Actions like Sequence might have an unexpected result when used with this action.
+ @since v0.8.2
+*/
+@interface CCEaseBounceIn : CCEaseBounce <NSCopying> {} @end
+
+/** EaseBounceOut action.
+ @warning This action doesn't use a bijective fucntion. Actions like Sequence might have an unexpected result when used with this action.
+ @since v0.8.2
+ */
+@interface CCEaseBounceOut : CCEaseBounce <NSCopying> {} @end
+
+/** CCEaseBounceInOut action.
+ @warning This action doesn't use a bijective fucntion. Actions like Sequence might have an unexpected result when used with this action.
+ @since v0.8.2
+ */
+@interface CCEaseBounceInOut : CCEaseBounce <NSCopying> {} @end
+
+/** CCEaseBackIn action.
+ @warning This action doesn't use a bijective fucntion. Actions like Sequence might have an unexpected result when used with this action.
+ @since v0.8.2
+ */
+@interface CCEaseBackIn : CCActionEase <NSCopying> {} @end
+
+/** CCEaseBackOut action.
+ @warning This action doesn't use a bijective fucntion. Actions like Sequence might have an unexpected result when used with this action.
+ @since v0.8.2
+ */
+@interface CCEaseBackOut : CCActionEase <NSCopying> {} @end
+
+/** CCEaseBackInOut action.
+ @warning This action doesn't use a bijective fucntion. Actions like Sequence might have an unexpected result when used with this action.
+ @since v0.8.2
+ */
+@interface CCEaseBackInOut : CCActionEase <NSCopying> {} @end
+
diff --git a/libs/cocos2d/CCActionEase.m b/libs/cocos2d/CCActionEase.m
new file mode 100644 (file)
index 0000000..f28be11
--- /dev/null
@@ -0,0 +1,534 @@
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2008-2009 Jason Booth
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+
+/*
+ * Elastic, Back and Bounce actions based on code from:
+ * http://github.com/NikhilK/silverlightfx/
+ *
+ * by http://github.com/NikhilK
+ */
+
+#import "CCActionEase.h"
+
+#ifndef M_PI_X_2
+#define M_PI_X_2 (float)M_PI * 2.0f
+#endif
+
+#pragma mark EaseAction
+
+//
+// EaseAction
+//
+@implementation CCActionEase
+
++(id) actionWithAction: (CCActionInterval*) action
+{
+       return [[[self alloc] initWithAction: action] autorelease ];
+}
+
+-(id) initWithAction: (CCActionInterval*) action
+{
+       NSAssert( action!=nil, @"Ease: arguments must be non-nil");
+  
+       if( (self=[super initWithDuration: action.duration]) )
+               other = [action retain];
+       
+       return self;
+}
+
+-(id) copyWithZone: (NSZone*) zone
+{
+       CCAction *copy = [[[self class] allocWithZone:zone] initWithAction:[[other copy] autorelease]];
+       return copy;
+}
+
+-(void) dealloc
+{
+       [other release];
+       [super dealloc];
+}
+
+-(void) startWithTarget:(id)aTarget
+{
+       [super startWithTarget:aTarget];
+       [other startWithTarget:target_];
+}
+
+-(void) stop
+{
+       [other stop];
+       [super stop];
+}
+
+-(void) update: (ccTime) t
+{
+       [other update: t];
+}
+
+-(CCActionInterval*) reverse
+{
+       return [[self class] actionWithAction: [other reverse]];
+}
+@end
+
+
+#pragma mark -
+#pragma mark EaseRate
+
+//
+// EaseRateAction
+//
+@implementation CCEaseRateAction
+@synthesize rate;
++(id) actionWithAction: (CCActionInterval*) action rate:(float)aRate
+{
+       return [[[self alloc] initWithAction: action rate:aRate] autorelease ];
+}
+
+-(id) initWithAction: (CCActionInterval*) action rate:(float)aRate
+{
+       if( (self=[super initWithAction:action ]) )
+               self.rate = aRate;
+       
+       return self;
+}
+
+-(id) copyWithZone: (NSZone*) zone
+{
+       CCAction *copy = [[[self class] allocWithZone:zone] initWithAction:[[other copy] autorelease] rate:rate];
+       return copy;
+}
+
+-(void) dealloc
+{
+       [super dealloc];
+}
+
+-(CCActionInterval*) reverse
+{
+       return [[self class] actionWithAction: [other reverse] rate:1/rate];
+}
+@end
+
+//
+// EeseIn
+//
+@implementation CCEaseIn
+-(void) update: (ccTime) t
+{
+       [other update: powf(t,rate)];
+}
+@end
+
+//
+// EaseOut
+//
+@implementation CCEaseOut
+-(void) update: (ccTime) t
+{
+       [other update: powf(t,1/rate)];
+}
+@end
+
+//
+// EaseInOut
+//
+@implementation CCEaseInOut
+-(void) update: (ccTime) t
+{      
+       int sign =1;
+       int r = (int) rate;
+       if (r % 2 == 0)
+               sign = -1;
+       t *= 2;
+       if (t < 1) 
+               [other update: 0.5f * powf (t, rate)];
+       else
+               [other update: sign*0.5f * (powf (t-2, rate) + sign*2)];        
+}
+
+// InOut and OutIn are symmetrical
+-(CCActionInterval*) reverse
+{
+       return [[self class] actionWithAction: [other reverse] rate:rate];
+}
+
+@end
+
+#pragma mark -
+#pragma mark EaseExponential
+
+//
+// EaseExponentialIn
+//
+@implementation CCEaseExponentialIn
+-(void) update: (ccTime) t
+{
+       [other update: (t==0) ? 0 : powf(2, 10 * (t/1 - 1)) - 1 * 0.001f];
+}
+
+- (CCActionInterval*) reverse
+{
+       return [CCEaseExponentialOut actionWithAction: [other reverse]];
+}
+@end
+
+//
+// EaseExponentialOut
+//
+@implementation CCEaseExponentialOut
+-(void) update: (ccTime) t
+{
+       [other update: (t==1) ? 1 : (-powf(2, -10 * t/1) + 1)];
+}
+
+- (CCActionInterval*) reverse
+{
+       return [CCEaseExponentialIn actionWithAction: [other reverse]];
+}
+@end
+
+//
+// EaseExponentialInOut
+//
+@implementation CCEaseExponentialInOut
+-(void) update: (ccTime) t
+{
+       t /= 0.5f;
+       if (t < 1)
+               t = 0.5f * powf(2, 10 * (t - 1));
+       else
+               t = 0.5f * (-powf(2, -10 * (t -1) ) + 2);
+       
+       [other update:t];
+}
+@end
+
+
+#pragma mark -
+#pragma mark EaseSin actions
+
+//
+// EaseSineIn
+//
+@implementation CCEaseSineIn
+-(void) update: (ccTime) t
+{
+       [other update:-1*cosf(t * (float)M_PI_2) +1];
+}
+
+- (CCActionInterval*) reverse
+{
+       return [CCEaseSineOut actionWithAction: [other reverse]];
+}
+@end
+
+//
+// EaseSineOut
+//
+@implementation CCEaseSineOut
+-(void) update: (ccTime) t
+{
+       [other update:sinf(t * (float)M_PI_2)];
+}
+
+- (CCActionInterval*) reverse
+{
+       return [CCEaseSineIn actionWithAction: [other reverse]];
+}
+@end
+
+//
+// EaseSineInOut
+//
+@implementation CCEaseSineInOut
+-(void) update: (ccTime) t
+{
+       [other update:-0.5f*(cosf( (float)M_PI*t) - 1)];
+}
+@end
+
+#pragma mark -
+#pragma mark EaseElastic actions
+
+//
+// EaseElastic
+//
+@implementation CCEaseElastic
+
+@synthesize period = period_;
+
++(id) actionWithAction: (CCActionInterval*) action
+{
+       return [[[self alloc] initWithAction:action period:0.3f] autorelease];
+}
+
++(id) actionWithAction: (CCActionInterval*) action period:(float)period
+{
+       return [[[self alloc] initWithAction:action period:period] autorelease];
+}
+
+-(id) initWithAction: (CCActionInterval*) action
+{
+       return [self initWithAction:action period:0.3f];
+}
+
+-(id) initWithAction: (CCActionInterval*) action period:(float)period
+{
+       if( (self=[super initWithAction:action]) )
+               period_ = period;
+       
+       return self;
+}
+
+-(id) copyWithZone: (NSZone*) zone
+{
+       CCAction *copy = [[[self class] allocWithZone:zone] initWithAction:[[other copy] autorelease] period:period_];
+       return copy;
+}
+
+-(CCActionInterval*) reverse
+{
+       NSAssert(NO,@"Override me");
+       return nil;
+}
+
+@end
+
+//
+// EaseElasticIn
+//
+
+@implementation CCEaseElasticIn
+-(void) update: (ccTime) t
+{      
+       ccTime newT = 0;
+       if (t == 0 || t == 1)
+               newT = t;
+               
+       else {
+               float s = period_ / 4;
+               t = t - 1;
+               newT = -powf(2, 10 * t) * sinf( (t-s) *M_PI_X_2 / period_);
+       }
+       [other update:newT];
+}
+
+- (CCActionInterval*) reverse
+{
+       return [CCEaseElasticOut actionWithAction: [other reverse] period:period_];
+}
+
+@end
+
+//
+// EaseElasticOut
+//
+@implementation CCEaseElasticOut
+
+-(void) update: (ccTime) t
+{      
+       ccTime newT = 0;
+       if (t == 0 || t == 1) {
+               newT = t;
+               
+       } else {
+               float s = period_ / 4;
+               newT = powf(2, -10 * t) * sinf( (t-s) *M_PI_X_2 / period_) + 1;
+       }
+       [other update:newT];
+}
+
+- (CCActionInterval*) reverse
+{
+       return [CCEaseElasticIn actionWithAction: [other reverse] period:period_];
+}
+
+@end
+
+//
+// EaseElasticInOut
+//
+@implementation CCEaseElasticInOut
+-(void) update: (ccTime) t
+{
+       ccTime newT = 0;
+       
+       if( t == 0 || t == 1 )
+               newT = t;
+       else {
+               t = t * 2;
+               if(! period_ )
+                       period_ = 0.3f * 1.5f;
+               ccTime s = period_ / 4;
+               
+               t = t -1;
+               if( t < 0 )
+                       newT = -0.5f * powf(2, 10 * t) * sinf((t - s) * M_PI_X_2 / period_);
+               else
+                       newT = powf(2, -10 * t) * sinf((t - s) * M_PI_X_2 / period_) * 0.5f + 1;
+       }
+       [other update:newT];    
+}
+
+- (CCActionInterval*) reverse
+{
+       return [CCEaseElasticInOut actionWithAction: [other reverse] period:period_];
+}
+
+@end
+
+#pragma mark -
+#pragma mark EaseBounce actions
+
+//
+// EaseBounce
+//
+@implementation CCEaseBounce
+-(ccTime) bounceTime:(ccTime) t
+{
+       if (t < 1 / 2.75) {
+               return 7.5625f * t * t;
+       }
+       else if (t < 2 / 2.75) {
+               t -= 1.5f / 2.75f;
+               return 7.5625f * t * t + 0.75f;
+       }
+       else if (t < 2.5 / 2.75) {
+               t -= 2.25f / 2.75f;
+               return 7.5625f * t * t + 0.9375f;
+       }
+
+       t -= 2.625f / 2.75f;
+       return 7.5625f * t * t + 0.984375f;
+}
+@end
+
+//
+// EaseBounceIn
+//
+
+@implementation CCEaseBounceIn
+
+-(void) update: (ccTime) t
+{
+       ccTime newT = 1 - [self bounceTime:1-t];        
+       [other update:newT];
+}
+
+- (CCActionInterval*) reverse
+{
+       return [CCEaseBounceOut actionWithAction: [other reverse]];
+}
+
+@end
+
+@implementation CCEaseBounceOut
+
+-(void) update: (ccTime) t
+{
+       ccTime newT = [self bounceTime:t];      
+       [other update:newT];
+}
+
+- (CCActionInterval*) reverse
+{
+       return [CCEaseBounceIn actionWithAction: [other reverse]];
+}
+
+@end
+
+@implementation CCEaseBounceInOut
+
+-(void) update: (ccTime) t
+{
+       ccTime newT = 0;
+       if (t < 0.5) {
+               t = t * 2;
+               newT = (1 - [self bounceTime:1-t] ) * 0.5f;
+       } else
+               newT = [self bounceTime:t * 2 - 1] * 0.5f + 0.5f;
+       
+       [other update:newT];
+}
+@end
+
+#pragma mark -
+#pragma mark Ease Back actions
+
+//
+// EaseBackIn
+//
+@implementation CCEaseBackIn
+
+-(void) update: (ccTime) t
+{
+       ccTime overshoot = 1.70158f;
+       [other update: t * t * ((overshoot + 1) * t - overshoot)];
+}
+
+- (CCActionInterval*) reverse
+{
+       return [CCEaseBackOut actionWithAction: [other reverse]];
+}
+@end
+
+//
+// EaseBackOut
+//
+@implementation CCEaseBackOut
+-(void) update: (ccTime) t
+{
+       ccTime overshoot = 1.70158f;
+       
+       t = t - 1;
+       [other update: t * t * ((overshoot + 1) * t + overshoot) + 1];
+}
+
+- (CCActionInterval*) reverse
+{
+       return [CCEaseBackIn actionWithAction: [other reverse]];
+}
+@end
+
+//
+// EaseBackInOut
+//
+@implementation CCEaseBackInOut
+
+-(void) update: (ccTime) t
+{
+       ccTime overshoot = 1.70158f * 1.525f;
+       
+       t = t * 2;
+       if (t < 1)
+               [other update: (t * t * ((overshoot + 1) * t - overshoot)) / 2];
+       else {
+               t = t - 2;
+               [other update: (t * t * ((overshoot + 1) * t + overshoot)) / 2 + 1];
+       }
+}
+@end
diff --git a/libs/cocos2d/CCActionGrid.h b/libs/cocos2d/CCActionGrid.h
new file mode 100644 (file)
index 0000000..13b6bc7
--- /dev/null
@@ -0,0 +1,165 @@
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2009 On-Core
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+
+#import "CCActionInterval.h"
+#import "CCActionInstant.h"
+#import "CCGrid.h"
+
+@class CCGridBase;
+
+/** Base class for Grid actions */
+@interface CCGridAction : CCActionInterval
+{
+       ccGridSize gridSize_;
+}
+
+/** size of the grid */
+@property (nonatomic,readwrite) ccGridSize gridSize;
+
+/** creates the action with size and duration */
++(id) actionWithSize:(ccGridSize)size duration:(ccTime)d;
+/** initializes the action with size and duration */
+-(id) initWithSize:(ccGridSize)gridSize duration:(ccTime)d;
+/** returns the grid */
+-(CCGridBase *)grid;
+
+@end
+
+////////////////////////////////////////////////////////////
+
+/** Base class for CCGrid3D actions.
+ Grid3D actions can modify a non-tiled grid.
+ */
+@interface CCGrid3DAction : CCGridAction
+{
+}
+
+/** returns the vertex than belongs to certain position in the grid */
+-(ccVertex3F)vertex:(ccGridSize)pos;
+/** returns the non-transformed vertex than belongs to certain position in the grid */
+-(ccVertex3F)originalVertex:(ccGridSize)pos;
+/** sets a new vertex to a certain position of the grid */
+-(void)setVertex:(ccGridSize)pos vertex:(ccVertex3F)vertex;
+
+@end
+
+////////////////////////////////////////////////////////////
+
+/** Base class for CCTiledGrid3D actions */
+@interface CCTiledGrid3DAction : CCGridAction
+{
+}
+
+/** returns the tile that belongs to a certain position of the grid */
+-(ccQuad3)tile:(ccGridSize)pos;
+/** returns the non-transformed tile that belongs to a certain position of the grid */
+-(ccQuad3)originalTile:(ccGridSize)pos;
+/** sets a new tile to a certain position of the grid */
+-(void)setTile:(ccGridSize)pos coords:(ccQuad3)coords;
+
+@end
+
+////////////////////////////////////////////////////////////
+
+/** CCAccelDeccelAmplitude action */
+@interface CCAccelDeccelAmplitude : CCActionInterval
+{
+       float                   rate;
+       CCActionInterval *other;
+}
+
+/** amplitude rate */
+@property (nonatomic,readwrite) float rate;
+
+/** creates the action with an inner action that has the amplitude property, and a duration time */
++(id)actionWithAction:(CCAction*)action duration:(ccTime)d;
+/** initializes the action with an inner action that has the amplitude property, and a duration time */
+-(id)initWithAction:(CCAction*)action duration:(ccTime)d;
+
+@end
+
+////////////////////////////////////////////////////////////
+
+/** CCAccelAmplitude action */
+@interface CCAccelAmplitude : CCActionInterval
+{
+       float                   rate;
+       CCActionInterval *other;
+}
+
+/** amplitude rate */
+@property (nonatomic,readwrite) float rate;
+
+/** creates the action with an inner action that has the amplitude property, and a duration time */
++(id)actionWithAction:(CCAction*)action duration:(ccTime)d;
+/** initializes the action with an inner action that has the amplitude property, and a duration time */
+-(id)initWithAction:(CCAction*)action duration:(ccTime)d;
+
+@end
+
+////////////////////////////////////////////////////////////
+
+/** CCDeccelAmplitude action */
+@interface CCDeccelAmplitude : CCActionInterval
+{
+       float                   rate;
+       CCActionInterval *other;
+}
+
+/** amplitude rate */
+@property (nonatomic,readwrite) float rate;
+
+/** creates the action with an inner action that has the amplitude property, and a duration time */
++(id)actionWithAction:(CCAction*)action duration:(ccTime)d;
+/** initializes the action with an inner action that has the amplitude property, and a duration time */
+-(id)initWithAction:(CCAction*)action duration:(ccTime)d;
+
+@end
+
+////////////////////////////////////////////////////////////
+
+/** CCStopGrid action.
+ Don't call this action if another grid action is active.
+ Call if you want to remove the the grid effect. Example:
+ [Sequence actions:[Lens ...], [StopGrid action], nil];
+ */
+@interface CCStopGrid : CCActionInstant
+{
+}
+@end
+
+////////////////////////////////////////////////////////////
+
+/** CCReuseGrid action */
+@interface CCReuseGrid : CCActionInstant
+{
+       int t;
+}
+/** creates an action with the number of times that the current grid will be reused */
++(id) actionWithTimes: (int) times;
+/** initializes an action with the number of times that the current grid will be reused */
+-(id) initWithTimes: (int) times;
+@end
diff --git a/libs/cocos2d/CCActionGrid.m b/libs/cocos2d/CCActionGrid.m
new file mode 100644 (file)
index 0000000..b2d8f98
--- /dev/null
@@ -0,0 +1,386 @@
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2009 On-Core
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+
+#import "CCActionGrid.h"
+#import "CCDirector.h"
+
+#pragma mark -
+#pragma mark GridAction
+
+@implementation CCGridAction
+
+@synthesize gridSize = gridSize_;
+
++(id) actionWithSize:(ccGridSize)size duration:(ccTime)d
+{
+       return [[[self alloc] initWithSize:size duration:d ] autorelease];
+}
+
+-(id) initWithSize:(ccGridSize)gSize duration:(ccTime)d
+{
+       if ( (self = [super initWithDuration:d]) )
+       {
+               gridSize_ = gSize;
+       }
+       
+       return self;
+}
+
+-(void)startWithTarget:(id)aTarget
+{
+       [super startWithTarget:aTarget];
+
+       CCGridBase *newgrid = [self grid];
+       
+       CCNode *t = (CCNode*) target_;
+       CCGridBase *targetGrid = [t grid];
+       
+       if ( targetGrid && targetGrid.reuseGrid > 0 )
+       {
+               if ( targetGrid.active && targetGrid.gridSize.x == gridSize_.x && targetGrid.gridSize.y == gridSize_.y && [targetGrid isKindOfClass:[newgrid class]] )
+                       [targetGrid reuse];
+               else
+                       [NSException raise:@"GridBase" format:@"Cannot reuse grid"];
+       }
+       else
+       {
+               if ( targetGrid && targetGrid.active )
+                       targetGrid.active = NO;
+               
+               t.grid = newgrid;
+               t.grid.active = YES;
+       }       
+}
+
+-(CCGridBase *)grid
+{
+       [NSException raise:@"GridBase" format:@"Abstract class needs implementation"];
+       return nil;
+}
+
+- (CCActionInterval*) reverse
+{
+       return [CCReverseTime actionWithAction:self];
+}
+
+-(id) copyWithZone: (NSZone*) zone
+{
+       CCGridAction *copy = [[[self class] allocWithZone:zone] initWithSize:gridSize_ duration:duration_];
+       return copy;
+}
+@end
+
+////////////////////////////////////////////////////////////
+
+#pragma mark -
+#pragma mark Grid3DAction
+
+@implementation CCGrid3DAction
+
+-(CCGridBase *)grid
+{
+       return [CCGrid3D gridWithSize:gridSize_];
+}
+
+-(ccVertex3F)vertex:(ccGridSize)pos
+{
+       CCGrid3D *g = (CCGrid3D *)[target_ grid];
+       return [g vertex:pos];
+}
+
+-(ccVertex3F)originalVertex:(ccGridSize)pos
+{
+       CCGrid3D *g = (CCGrid3D *)[target_ grid];
+       return [g originalVertex:pos];
+}
+
+-(void)setVertex:(ccGridSize)pos vertex:(ccVertex3F)vertex
+{
+       CCGrid3D *g = (CCGrid3D *)[target_ grid];
+       return [g setVertex:pos vertex:vertex];
+}
+@end
+
+////////////////////////////////////////////////////////////
+
+#pragma mark -
+#pragma mark TiledGrid3DAction
+
+@implementation CCTiledGrid3DAction
+
+-(CCGridBase *)grid
+{
+       return [CCTiledGrid3D gridWithSize:gridSize_];
+}
+
+-(ccQuad3)tile:(ccGridSize)pos
+{
+       CCTiledGrid3D *g = (CCTiledGrid3D *)[target_ grid];
+       return [g tile:pos];
+}
+
+-(ccQuad3)originalTile:(ccGridSize)pos
+{
+       CCTiledGrid3D *g = (CCTiledGrid3D *)[target_ grid];
+       return [g originalTile:pos];
+}
+
+-(void)setTile:(ccGridSize)pos coords:(ccQuad3)coords
+{
+       CCTiledGrid3D *g = (CCTiledGrid3D *)[target_ grid];
+       [g setTile:pos coords:coords];
+}
+
+@end
+
+////////////////////////////////////////////////////////////
+
+@interface CCActionInterval (Amplitude)
+-(void)setAmplitudeRate:(CGFloat)amp;
+-(CGFloat)getAmplitudeRate;
+@end
+
+@implementation CCActionInterval (Amplitude)
+-(void)setAmplitudeRate:(CGFloat)amp
+{
+       [NSException raise:@"IntervalAction (Amplitude)" format:@"Abstract class needs implementation"];
+}
+
+-(CGFloat)getAmplitudeRate
+{
+       [NSException raise:@"IntervalAction (Amplitude)" format:@"Abstract class needs implementation"];
+       return 0;
+}
+@end
+
+////////////////////////////////////////////////////////////
+
+#pragma mark -
+#pragma mark AccelDeccelAmplitude
+
+@implementation CCAccelDeccelAmplitude
+
+@synthesize rate;
+
++(id)actionWithAction:(CCAction*)action duration:(ccTime)d
+{
+       return [[[self alloc] initWithAction:action duration:d ] autorelease];
+}
+
+-(id)initWithAction:(CCAction *)action duration:(ccTime)d
+{
+       if ( (self = [super initWithDuration:d]) )
+       {
+               rate = 1.0f;
+               other = [action retain];
+       }
+       
+       return self;
+}
+
+-(void)dealloc
+{
+       [other release];
+       [super dealloc];
+}
+
+-(void)startWithTarget:(id)aTarget
+{
+       [super startWithTarget:aTarget];
+       [other startWithTarget:target_];
+}
+
+-(void) update: (ccTime) time
+{
+       float f = time*2;
+       
+       if (f > 1)
+       {
+               f -= 1;
+               f = 1 - f;
+       }
+       
+       [other setAmplitudeRate:powf(f, rate)];
+       [other update:time];
+}
+
+- (CCActionInterval*) reverse
+{
+       return [CCAccelDeccelAmplitude actionWithAction:[other reverse] duration:duration_];
+}
+
+@end
+
+////////////////////////////////////////////////////////////
+
+#pragma mark -
+#pragma mark AccelAmplitude
+
+@implementation CCAccelAmplitude
+
+@synthesize rate;
+
++(id)actionWithAction:(CCAction*)action duration:(ccTime)d
+{
+       return [[[self alloc] initWithAction:action duration:d ] autorelease];
+}
+
+-(id)initWithAction:(CCAction *)action duration:(ccTime)d
+{
+       if ( (self = [super initWithDuration:d]) )
+       {
+               rate = 1.0f;
+               other = [action retain];
+       }
+       
+       return self;
+}
+
+-(void)dealloc
+{
+       [other release];
+       [super dealloc];
+}
+
+-(void)startWithTarget:(id)aTarget
+{
+       [super startWithTarget:aTarget];
+       [other startWithTarget:target_];
+}
+
+-(void) update: (ccTime) time
+{
+       [other setAmplitudeRate:powf(time, rate)];
+       [other update:time];
+}
+
+- (CCActionInterval*) reverse
+{
+       return [CCAccelAmplitude actionWithAction:[other reverse] duration:self.duration];
+}
+
+@end
+
+////////////////////////////////////////////////////////////
+
+#pragma mark -
+#pragma mark DeccelAmplitude
+
+@implementation CCDeccelAmplitude
+
+@synthesize rate;
+
++(id)actionWithAction:(CCAction*)action duration:(ccTime)d
+{
+       return [[[self alloc] initWithAction:action duration:d ] autorelease];
+}
+
+-(id)initWithAction:(CCAction *)action duration:(ccTime)d
+{
+       if ( (self = [super initWithDuration:d]) )
+       {
+               rate = 1.0f;
+               other = [action retain];
+       }
+       
+       return self;
+}
+
+-(void)dealloc
+{
+       [other release];
+       [super dealloc];
+}
+
+-(void)startWithTarget:(id)aTarget
+{
+       [super startWithTarget:aTarget];
+       [other startWithTarget:target_];
+}
+
+-(void) update: (ccTime) time
+{
+       [other setAmplitudeRate:powf((1-time), rate)];
+       [other update:time];
+}
+
+- (CCActionInterval*) reverse
+{
+       return [CCDeccelAmplitude actionWithAction:[other reverse] duration:self.duration];
+}
+
+@end
+
+////////////////////////////////////////////////////////////
+
+#pragma mark -
+#pragma mark StopGrid
+
+@implementation CCStopGrid
+
+-(void)startWithTarget:(id)aTarget
+{
+       [super startWithTarget:aTarget];
+
+       if ( [[self target] grid] && [[[self target] grid] active] ) {
+               [[[self target] grid] setActive: NO];
+               
+//             [[self target] setGrid: nil];
+       }
+}
+
+@end
+
+////////////////////////////////////////////////////////////
+
+#pragma mark -
+#pragma mark ReuseGrid
+
+@implementation CCReuseGrid
+
++(id)actionWithTimes:(int)times
+{
+       return [[[self alloc] initWithTimes:times ] autorelease];
+}
+
+-(id)initWithTimes:(int)times
+{
+       if ( (self = [super init]) )
+               t = times;
+       
+       return self;
+}
+
+-(void)startWithTarget:(id)aTarget
+{
+       [super startWithTarget:aTarget];
+
+       CCNode *myTarget = (CCNode*) [self target];
+       if ( myTarget.grid && myTarget.grid.active )
+               myTarget.grid.reuseGrid += t;
+}
+
+@end
diff --git a/libs/cocos2d/CCActionGrid3D.h b/libs/cocos2d/CCActionGrid3D.h
new file mode 100644 (file)
index 0000000..a8003f4
--- /dev/null
@@ -0,0 +1,208 @@
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2009 On-Core
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+
+#import "CCActionGrid.h"
+
+/** CCWaves3D action */
+@interface CCWaves3D : CCGrid3DAction
+{
+       int waves;
+       float amplitude;
+       float amplitudeRate;
+}
+
+/** amplitude of the wave */
+@property (nonatomic,readwrite) float amplitude;
+/** amplitude rate of the wave */
+@property (nonatomic,readwrite) float amplitudeRate;
+
++(id)actionWithWaves:(int)wav amplitude:(float)amp grid:(ccGridSize)gridSize duration:(ccTime)d;
+-(id)initWithWaves:(int)wav amplitude:(float)amp grid:(ccGridSize)gridSize duration:(ccTime)d;
+
+@end
+
+////////////////////////////////////////////////////////////
+
+/** CCFlipX3D action */
+@interface CCFlipX3D : CCGrid3DAction
+{
+}
+
+/** creates the action with duration */
++(id) actionWithDuration:(ccTime)d;
+/** initizlies the action with duration */
+-(id) initWithDuration:(ccTime)d;
+
+@end
+
+////////////////////////////////////////////////////////////
+
+/** CCFlipY3D action */
+@interface CCFlipY3D : CCFlipX3D
+{
+}
+
+@end
+
+////////////////////////////////////////////////////////////
+
+/** CCLens3D action */
+@interface CCLens3D : CCGrid3DAction
+{
+       CGPoint position_;
+       CGPoint positionInPixels_;
+       float   radius_;
+       float   lensEffect_;
+       BOOL    dirty_;
+}
+
+/** lens effect. Defaults to 0.7 - 0 means no effect, 1 is very strong effect */
+@property (nonatomic,readwrite) float lensEffect;
+/** lens center position in Points */
+@property (nonatomic,readwrite) CGPoint position;
+
+/** creates the action with center position in Points, radius, a grid size and duration */
++(id)actionWithPosition:(CGPoint)pos radius:(float)r grid:(ccGridSize)gridSize duration:(ccTime)d;
+/** initializes the action with center position in Points, radius, a grid size and duration */
+-(id)initWithPosition:(CGPoint)pos radius:(float)r grid:(ccGridSize)gridSize duration:(ccTime)d;
+
+@end
+
+////////////////////////////////////////////////////////////
+
+/** CCRipple3D action */
+@interface CCRipple3D : CCGrid3DAction
+{
+       CGPoint position_;
+       CGPoint positionInPixels_;
+       float   radius_;
+       int             waves_;
+       float   amplitude_;
+       float   amplitudeRate_;
+}
+
+/** center position in Points */
+@property (nonatomic,readwrite) CGPoint position;
+/** amplitude */
+@property (nonatomic,readwrite) float amplitude;
+/** amplitude rate */
+@property (nonatomic,readwrite) float amplitudeRate;
+
+/** creates the action with a position in points, radius, number of waves, amplitude, a grid size and duration */
++(id)actionWithPosition:(CGPoint)pos radius:(float)r waves:(int)wav amplitude:(float)amp grid:(ccGridSize)gridSize duration:(ccTime)d;
+/** initializes the action with a position in points, radius, number of waves, amplitude, a grid size and duration */
+-(id)initWithPosition:(CGPoint)pos radius:(float)r waves:(int)wav amplitude:(float)amp grid:(ccGridSize)gridSize duration:(ccTime)d;
+
+@end
+
+////////////////////////////////////////////////////////////
+
+/** CCShaky3D action */
+@interface CCShaky3D : CCGrid3DAction
+{
+       int randrange;
+       BOOL    shakeZ;
+}
+
+/** creates the action with a range, shake Z vertices, a grid and duration */
++(id)actionWithRange:(int)range shakeZ:(BOOL)shakeZ grid:(ccGridSize)gridSize duration:(ccTime)d;
+/** initializes the action with a range, shake Z vertices, a grid and duration */
+-(id)initWithRange:(int)range shakeZ:(BOOL)shakeZ grid:(ccGridSize)gridSize duration:(ccTime)d;
+
+@end
+
+////////////////////////////////////////////////////////////
+
+/** CCLiquid action */
+@interface CCLiquid : CCGrid3DAction
+{
+       int waves;
+       float amplitude;
+       float amplitudeRate;
+       
+}
+
+/** amplitude */
+@property (nonatomic,readwrite) float amplitude;
+/** amplitude rate */
+@property (nonatomic,readwrite) float amplitudeRate;
+
+/** creates the action with amplitude, a grid and duration */
++(id)actionWithWaves:(int)wav amplitude:(float)amp grid:(ccGridSize)gridSize duration:(ccTime)d;
+/** initializes the action with amplitude, a grid and duration */
+-(id)initWithWaves:(int)wav amplitude:(float)amp grid:(ccGridSize)gridSize duration:(ccTime)d;
+
+@end
+
+////////////////////////////////////////////////////////////
+
+/** CCWaves action */
+@interface CCWaves : CCGrid3DAction
+{
+       int             waves;
+       float   amplitude;
+       float   amplitudeRate;
+       BOOL    vertical;
+       BOOL    horizontal;
+}
+
+/** amplitude */
+@property (nonatomic,readwrite) float amplitude;
+/** amplitude rate */
+@property (nonatomic,readwrite) float amplitudeRate;
+
+/** initializes the action with amplitude, horizontal sin, vertical sin, a grid and duration */
++(id)actionWithWaves:(int)wav amplitude:(float)amp horizontal:(BOOL)h vertical:(BOOL)v grid:(ccGridSize)gridSize duration:(ccTime)d;
+/** creates the action with amplitude, horizontal sin, vertical sin, a grid and duration */
+-(id)initWithWaves:(int)wav amplitude:(float)amp horizontal:(BOOL)h vertical:(BOOL)v grid:(ccGridSize)gridSize duration:(ccTime)d;
+
+@end
+
+////////////////////////////////////////////////////////////
+
+/** CCTwirl action */
+@interface CCTwirl : CCGrid3DAction
+{
+       CGPoint position_;
+       CGPoint positionInPixels_;
+       int             twirls_;
+       float   amplitude_;
+       float   amplitudeRate_;
+}
+
+/** twirl center */
+@property (nonatomic,readwrite) CGPoint position;
+/** amplitude */
+@property (nonatomic,readwrite) float amplitude;
+/** amplitude rate */
+@property (nonatomic,readwrite) float amplitudeRate;
+
+/** creates the action with center position, number of twirls, amplitude, a grid size and duration */
++(id)actionWithPosition:(CGPoint)pos twirls:(int)t amplitude:(float)amp grid:(ccGridSize)gridSize duration:(ccTime)d;
+/** initializes the action with center position, number of twirls, amplitude, a grid size and duration */
+-(id)initWithPosition:(CGPoint)pos twirls:(int)t amplitude:(float)amp grid:(ccGridSize)gridSize duration:(ccTime)d;
+
+@end
diff --git a/libs/cocos2d/CCActionGrid3D.m b/libs/cocos2d/CCActionGrid3D.m
new file mode 100644 (file)
index 0000000..1d4a783
--- /dev/null
@@ -0,0 +1,659 @@
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2009 On-Core
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+
+#import "CCActionGrid3D.h"
+#import "ccMacros.h"
+#import "Support/CGPointExtension.h"
+
+#pragma mark -
+#pragma mark Waves3D
+
+@implementation CCWaves3D
+
+@synthesize amplitude;
+@synthesize amplitudeRate;
+
++(id)actionWithWaves:(int)wav amplitude:(float)amp grid:(ccGridSize)gridSize duration:(ccTime)d
+{
+       return [[[self alloc] initWithWaves:wav amplitude:amp grid:gridSize duration:d] autorelease];
+}
+
+-(id)initWithWaves:(int)wav amplitude:(float)amp grid:(ccGridSize)gSize duration:(ccTime)d
+{
+       if ( (self = [super initWithSize:gSize duration:d]) )
+       {
+               waves = wav;
+               amplitude = amp;
+               amplitudeRate = 1.0f;
+       }
+       
+       return self;
+}
+
+-(id) copyWithZone: (NSZone*) zone
+{
+       CCGridAction *copy = [[[self class] allocWithZone:zone] initWithWaves:waves amplitude:amplitude grid:gridSize_ duration:duration_];
+       return copy;
+}
+
+
+-(void)update:(ccTime)time
+{
+       int i, j;
+       
+       for( i = 0; i < (gridSize_.x+1); i++ )
+       {
+               for( j = 0; j < (gridSize_.y+1); j++ )
+               {
+                       ccVertex3F      v = [self originalVertex:ccg(i,j)];
+                       v.z += (sinf((CGFloat)M_PI*time*waves*2 + (v.y+v.x) * .01f) * amplitude * amplitudeRate);
+                       [self setVertex:ccg(i,j) vertex:v];
+               }
+       }
+}
+@end
+
+////////////////////////////////////////////////////////////
+
+#pragma mark -
+#pragma mark FlipX3D
+
+@implementation CCFlipX3D
+
++(id) actionWithDuration:(ccTime)d
+{
+       return [[[self alloc] initWithSize:ccg(1,1) duration:d] autorelease];
+}
+
+-(id) initWithDuration:(ccTime)d
+{
+       return [super initWithSize:ccg(1,1) duration:d];
+}
+
+-(id)initWithSize:(ccGridSize)gSize duration:(ccTime)d
+{
+       if ( gSize.x != 1 || gSize.y != 1 )
+       {
+               [NSException raise:@"FlipX3D" format:@"Grid size must be (1,1)"];
+       }
+       
+       return [super initWithSize:gSize duration:d];
+}
+
+-(id) copyWithZone: (NSZone*) zone
+{
+       CCGridAction *copy = [[[self class] allocWithZone:zone] initWithSize:gridSize_ duration:duration_];
+       return copy;
+}
+
+
+-(void)update:(ccTime)time
+{
+       CGFloat angle = (CGFloat)M_PI * time; // 180 degrees
+       CGFloat mz = sinf( angle );
+       angle = angle / 2.0f;     // x calculates degrees from 0 to 90
+       CGFloat mx = cosf( angle );
+       
+       ccVertex3F      v0, v1, v, diff;
+       
+       v0 = [self originalVertex:ccg(1,1)];
+       v1 = [self originalVertex:ccg(0,0)];
+       
+       CGFloat x0 = v0.x;
+       CGFloat x1 = v1.x;
+       CGFloat x;
+       ccGridSize      a, b, c, d;
+       
+       if ( x0 > x1 )
+       {
+               // Normal Grid
+               a = ccg(0,0);
+               b = ccg(0,1);
+               c = ccg(1,0);
+               d = ccg(1,1);
+               x = x0;
+       }
+       else
+       {
+               // Reversed Grid
+               c = ccg(0,0);
+               d = ccg(0,1);
+               a = ccg(1,0);
+               b = ccg(1,1);
+               x = x1;
+       }
+       
+       diff.x = ( x - x * mx );
+       diff.z = fabsf( floorf( (x * mz) / 4.0f ) );
+       
+// bottom-left
+       v = [self originalVertex:a];
+       v.x = diff.x;
+       v.z += diff.z;
+       [self setVertex:a vertex:v];
+       
+// upper-left
+       v = [self originalVertex:b];
+       v.x = diff.x;
+       v.z += diff.z;
+       [self setVertex:b vertex:v];
+       
+// bottom-right
+       v = [self originalVertex:c];
+       v.x -= diff.x;
+       v.z -= diff.z;
+       [self setVertex:c vertex:v];
+       
+// upper-right
+       v = [self originalVertex:d];
+       v.x -= diff.x;
+       v.z -= diff.z;
+       [self setVertex:d vertex:v];
+}
+
+@end
+
+////////////////////////////////////////////////////////////
+
+#pragma mark -
+#pragma mark FlipY3D
+
+@implementation CCFlipY3D
+
+-(void)update:(ccTime)time
+{
+       CGFloat angle = (CGFloat)M_PI * time; // 180 degrees
+       CGFloat mz = sinf( angle );
+       angle = angle / 2.0f;     // x calculates degrees from 0 to 90
+       CGFloat my = cosf( angle );
+       
+       ccVertex3F      v0, v1, v, diff;
+       
+       v0 = [self originalVertex:ccg(1,1)];
+       v1 = [self originalVertex:ccg(0,0)];
+       
+       CGFloat y0 = v0.y;
+       CGFloat y1 = v1.y;
+       CGFloat y;
+       ccGridSize      a, b, c, d;
+       
+       if ( y0 > y1 )
+       {
+               // Normal Grid
+               a = ccg(0,0);
+               b = ccg(0,1);
+               c = ccg(1,0);
+               d = ccg(1,1);
+               y = y0;
+       }
+       else
+       {
+               // Reversed Grid
+               b = ccg(0,0);
+               a = ccg(0,1);
+               d = ccg(1,0);
+               c = ccg(1,1);
+               y = y1;
+       }
+       
+       diff.y = y - y * my;
+       diff.z = fabsf( floorf( (y * mz) / 4.0f ) );
+       
+       // bottom-left
+       v = [self originalVertex:a];
+       v.y = diff.y;
+       v.z += diff.z;
+       [self setVertex:a vertex:v];
+       
+       // upper-left
+       v = [self originalVertex:b];
+       v.y -= diff.y;
+       v.z -= diff.z;
+       [self setVertex:b vertex:v];
+       
+       // bottom-right
+       v = [self originalVertex:c];
+       v.y = diff.y;
+       v.z += diff.z;
+       [self setVertex:c vertex:v];
+       
+       // upper-right
+       v = [self originalVertex:d];
+       v.y -= diff.y;
+       v.z -= diff.z;
+       [self setVertex:d vertex:v];
+}
+
+@end
+
+////////////////////////////////////////////////////////////
+
+#pragma mark -
+#pragma mark Lens3D
+
+@implementation CCLens3D
+
+@synthesize lensEffect=lensEffect_;
+
++(id)actionWithPosition:(CGPoint)pos radius:(float)r grid:(ccGridSize)gridSize duration:(ccTime)d
+{
+       return [[[self alloc] initWithPosition:pos radius:r grid:gridSize duration:d] autorelease];
+}
+
+-(id)initWithPosition:(CGPoint)pos radius:(float)r grid:(ccGridSize)gSize duration:(ccTime)d
+{
+       if ( (self = [super initWithSize:gSize duration:d]) )
+       {
+               position_ = ccp(-1,-1);
+               self.position = pos;
+               radius_ = r;
+               lensEffect_ = 0.7f;
+               dirty_ = YES;
+       }
+       
+       return self;
+}
+
+-(id) copyWithZone: (NSZone*) zone
+{
+       CCGridAction *copy = [[[self class] allocWithZone:zone] initWithPosition:position_ radius:radius_ grid:gridSize_ duration:duration_];
+       return copy;
+}
+
+-(void) setPosition:(CGPoint)pos
+{
+       if( ! CGPointEqualToPoint(pos, position_) ) {
+               position_ = pos;
+               positionInPixels_.x = pos.x * CC_CONTENT_SCALE_FACTOR();
+               positionInPixels_.y = pos.y * CC_CONTENT_SCALE_FACTOR();
+               
+               dirty_ = YES;
+       }
+}
+
+-(CGPoint) position
+{
+       return position_;
+}
+       
+-(void)update:(ccTime)time
+{
+       if ( dirty_ )
+       {
+               int i, j;
+               
+               for( i = 0; i < gridSize_.x+1; i++ )
+               {
+                       for( j = 0; j < gridSize_.y+1; j++ )
+                       {
+                               ccVertex3F      v = [self originalVertex:ccg(i,j)];
+                               CGPoint vect = ccpSub(positionInPixels_, ccp(v.x,v.y));
+                               CGFloat r = ccpLength(vect);
+                               
+                               if ( r < radius_ )
+                               {
+                                       r = radius_ - r;
+                                       CGFloat pre_log = r / radius_;
+                                       if ( pre_log == 0 ) pre_log = 0.001f;
+                                       float l = logf(pre_log) * lensEffect_;
+                                       float new_r = expf( l ) * radius_;
+                                       
+                                       if ( ccpLength(vect) > 0 )
+                                       {
+                                               vect = ccpNormalize(vect);
+                                               CGPoint new_vect = ccpMult(vect, new_r);
+                                               v.z += ccpLength(new_vect) * lensEffect_;
+                                       }
+                               }
+                               
+                               [self setVertex:ccg(i,j) vertex:v];
+                       }
+               }
+               
+               dirty_ = NO;
+       }
+}
+
+@end
+
+////////////////////////////////////////////////////////////
+
+#pragma mark -
+#pragma mark Ripple3D
+
+@implementation CCRipple3D
+
+@synthesize amplitude = amplitude_;
+@synthesize amplitudeRate = amplitudeRate_;
+
++(id)actionWithPosition:(CGPoint)pos radius:(float)r waves:(int)wav amplitude:(float)amp grid:(ccGridSize)gridSize duration:(ccTime)d
+{
+       return [[[self alloc] initWithPosition:pos radius:r waves:wav amplitude:amp grid:gridSize duration:d] autorelease];
+}
+
+-(id)initWithPosition:(CGPoint)pos radius:(float)r waves:(int)wav amplitude:(float)amp grid:(ccGridSize)gSize duration:(ccTime)d
+{
+       if ( (self = [super initWithSize:gSize duration:d]) )
+       {
+               self.position = pos;
+               radius_ = r;
+               waves_ = wav;
+               amplitude_ = amp;
+               amplitudeRate_ = 1.0f;
+       }
+       
+       return self;
+}
+
+-(CGPoint) position
+{
+       return position_;
+}
+
+-(void) setPosition:(CGPoint)pos
+{
+       position_ = pos;
+       positionInPixels_.x = pos.x * CC_CONTENT_SCALE_FACTOR();
+       positionInPixels_.y = pos.y * CC_CONTENT_SCALE_FACTOR();
+}
+
+-(id) copyWithZone: (NSZone*) zone
+{
+       CCGridAction *copy = [[[self class] allocWithZone:zone] initWithPosition:position_ radius:radius_ waves:waves_ amplitude:amplitude_ grid:gridSize_ duration:duration_];
+       return copy;
+}
+
+
+-(void)update:(ccTime)time
+{
+       int i, j;
+       
+       for( i = 0; i < (gridSize_.x+1); i++ )
+       {
+               for( j = 0; j < (gridSize_.y+1); j++ )
+               {
+                       ccVertex3F      v = [self originalVertex:ccg(i,j)];
+                       CGPoint vect = ccpSub(positionInPixels_, ccp(v.x,v.y));
+                       CGFloat r = ccpLength(vect);
+                       
+                       if ( r < radius_ )
+                       {
+                               r = radius_ - r;
+                               CGFloat rate = powf( r / radius_, 2);
+                               v.z += (sinf( time*(CGFloat)M_PI*waves_*2 + r * 0.1f) * amplitude_ * amplitudeRate_ * rate );
+                       }
+                       
+                       [self setVertex:ccg(i,j) vertex:v];
+               }
+       }
+}
+
+@end
+
+////////////////////////////////////////////////////////////
+
+#pragma mark -
+#pragma mark Shaky3D
+
+@implementation CCShaky3D
+
++(id)actionWithRange:(int)range shakeZ:(BOOL)sz grid:(ccGridSize)gridSize duration:(ccTime)d
+{
+       return [[[self alloc] initWithRange:range shakeZ:sz grid:gridSize duration:d] autorelease];
+}
+
+-(id)initWithRange:(int)range shakeZ:(BOOL)sz grid:(ccGridSize)gSize duration:(ccTime)d
+{
+       if ( (self = [super initWithSize:gSize duration:d]) )
+       {
+               randrange = range;
+               shakeZ = sz;
+       }
+       
+       return self;
+}
+
+-(id) copyWithZone: (NSZone*) zone
+{
+       CCGridAction *copy = [[[self class] allocWithZone:zone] initWithRange:randrange shakeZ:shakeZ grid:gridSize_ duration:duration_];
+       return copy;
+}
+
+-(void)update:(ccTime)time
+{
+       int i, j;
+       
+       for( i = 0; i < (gridSize_.x+1); i++ )
+       {
+               for( j = 0; j < (gridSize_.y+1); j++ )
+               {
+                       ccVertex3F      v = [self originalVertex:ccg(i,j)];
+                       v.x += ( rand() % (randrange*2) ) - randrange;
+                       v.y += ( rand() % (randrange*2) ) - randrange;
+                       if( shakeZ )
+                               v.z += ( rand() % (randrange*2) ) - randrange;
+                       
+                       [self setVertex:ccg(i,j) vertex:v];
+               }
+       }
+}
+
+@end
+
+////////////////////////////////////////////////////////////
+
+#pragma mark -
+#pragma mark Liquid
+
+@implementation CCLiquid
+
+@synthesize amplitude;
+@synthesize amplitudeRate;
+
++(id)actionWithWaves:(int)wav amplitude:(float)amp grid:(ccGridSize)gridSize duration:(ccTime)d
+{
+       return [[[self alloc] initWithWaves:wav amplitude:amp grid:gridSize duration:d] autorelease];
+}
+
+-(id)initWithWaves:(int)wav amplitude:(float)amp grid:(ccGridSize)gSize duration:(ccTime)d
+{
+       if ( (self = [super initWithSize:gSize duration:d]) )
+       {
+               waves = wav;
+               amplitude = amp;
+               amplitudeRate = 1.0f;
+       }
+       
+       return self;
+}
+
+-(void)update:(ccTime)time
+{
+       int i, j;
+       
+       for( i = 1; i < gridSize_.x; i++ )
+       {
+               for( j = 1; j < gridSize_.y; j++ )
+               {
+                       ccVertex3F      v = [self originalVertex:ccg(i,j)];
+                       v.x = (v.x + (sinf(time*(CGFloat)M_PI*waves*2 + v.x * .01f) * amplitude * amplitudeRate));
+                       v.y = (v.y + (sinf(time*(CGFloat)M_PI*waves*2 + v.y * .01f) * amplitude * amplitudeRate));
+                       [self setVertex:ccg(i,j) vertex:v];
+               }
+       }
+}      
+
+-(id) copyWithZone: (NSZone*) zone
+{
+       CCGridAction *copy = [[[self class] allocWithZone:zone] initWithWaves:waves amplitude:amplitude grid:gridSize_ duration:duration_];
+       return copy;
+}
+
+@end
+
+////////////////////////////////////////////////////////////
+
+#pragma mark -
+#pragma mark Waves
+
+@implementation CCWaves
+
+@synthesize amplitude;
+@synthesize amplitudeRate;
+
++(id)actionWithWaves:(int)wav amplitude:(float)amp horizontal:(BOOL)h vertical:(BOOL)v grid:(ccGridSize)gridSize duration:(ccTime)d
+{
+       return [[[self alloc] initWithWaves:wav amplitude:amp horizontal:h vertical:v grid:gridSize duration:d] autorelease];
+}
+
+-(id)initWithWaves:(int)wav amplitude:(float)amp horizontal:(BOOL)h vertical:(BOOL)v grid:(ccGridSize)gSize duration:(ccTime)d
+{
+       if ( (self = [super initWithSize:gSize duration:d]) )
+       {
+               waves = wav;
+               amplitude = amp;
+               amplitudeRate = 1.0f;
+               horizontal = h;
+               vertical = v;
+       }
+       
+       return self;
+}
+
+-(void)update:(ccTime)time
+{
+       int i, j;
+       
+       for( i = 0; i < (gridSize_.x+1); i++ )
+       {
+               for( j = 0; j < (gridSize_.y+1); j++ )
+               {
+                       ccVertex3F      v = [self originalVertex:ccg(i,j)];
+                       
+                       if ( vertical )
+                               v.x = (v.x + (sinf(time*(CGFloat)M_PI*waves*2 + v.y * .01f) * amplitude * amplitudeRate));
+                       
+                       if ( horizontal )
+                               v.y = (v.y + (sinf(time*(CGFloat)M_PI*waves*2 + v.x * .01f) * amplitude * amplitudeRate));
+                                       
+                       [self setVertex:ccg(i,j) vertex:v];
+               }
+       }
+}
+
+-(id) copyWithZone: (NSZone*) zone
+{
+       CCGridAction *copy = [[[self class] allocWithZone:zone] initWithWaves:waves amplitude:amplitude horizontal:horizontal vertical:vertical grid:gridSize_ duration:duration_];
+       return copy;
+}
+
+@end
+
+////////////////////////////////////////////////////////////
+
+#pragma mark -
+#pragma mark Twirl
+
+@implementation CCTwirl
+
+@synthesize amplitude = amplitude_;
+@synthesize amplitudeRate = amplitudeRate_;
+
++(id)actionWithPosition:(CGPoint)pos twirls:(int)t amplitude:(float)amp grid:(ccGridSize)gridSize duration:(ccTime)d
+{
+       return [[[self alloc] initWithPosition:pos twirls:t amplitude:amp grid:gridSize duration:d] autorelease];
+}
+
+-(id)initWithPosition:(CGPoint)pos twirls:(int)t amplitude:(float)amp grid:(ccGridSize)gSize duration:(ccTime)d
+{
+       if ( (self = [super initWithSize:gSize duration:d]) )
+       {
+               self.position = pos;
+               twirls_ = t;
+               amplitude_ = amp;
+               amplitudeRate_ = 1.0f;
+       }
+       
+       return self;
+}
+
+-(void) setPosition:(CGPoint)pos
+{
+       position_ = pos;
+       positionInPixels_.x = pos.x * CC_CONTENT_SCALE_FACTOR();
+       positionInPixels_.y = pos.y * CC_CONTENT_SCALE_FACTOR();
+}
+
+-(CGPoint) position
+{
+       return position_;
+}
+
+-(void)update:(ccTime)time
+{
+       int i, j;
+       CGPoint c = positionInPixels_;
+       
+       for( i = 0; i < (gridSize_.x+1); i++ )
+       {
+               for( j = 0; j < (gridSize_.y+1); j++ )
+               {
+                       ccVertex3F v = [self originalVertex:ccg(i,j)];
+                       
+                       CGPoint avg = ccp(i-(gridSize_.x/2.0f), j-(gridSize_.y/2.0f));
+                       CGFloat r = ccpLength( avg );
+                       
+                       CGFloat amp = 0.1f * amplitude_ * amplitudeRate_;
+                       CGFloat a = r * cosf( (CGFloat)M_PI/2.0f + time * (CGFloat)M_PI * twirls_ * 2 ) * amp;
+                       
+                       float cosA = cosf(a);
+                       float sinA = sinf(a);
+                       
+                       CGPoint d = {
+                               sinA * (v.y-c.y) + cosA * (v.x-c.x),
+                               cosA * (v.y-c.y) - sinA * (v.x-c.x)
+                       };
+                       
+                       v.x = c.x + d.x;
+                       v.y = c.y + d.y;
+                       
+                       [self setVertex:ccg(i,j) vertex:v];
+               }
+       }
+}
+
+-(id) copyWithZone: (NSZone*) zone
+{
+       CCGridAction *copy = [[[self class] allocWithZone:zone] initWithPosition:position_
+                                                                                                                                         twirls:twirls_
+                                                                                                                                  amplitude:amplitude_
+                                                                                                                                               grid:gridSize_
+                                                                                                                                       duration:duration_];
+       return copy;
+}
+
+
+@end
diff --git a/libs/cocos2d/CCActionInstant.h b/libs/cocos2d/CCActionInstant.h
new file mode 100644 (file)
index 0000000..5a1bc2d
--- /dev/null
@@ -0,0 +1,205 @@
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2008-2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+
+#import "CCAction.h"
+
+/** Instant actions are immediate actions. They don't have a duration like
+ the CCIntervalAction actions.
+*/ 
+@interface CCActionInstant : CCFiniteTimeAction <NSCopying>
+{
+}
+@end
+
+/** Show the node
+ */
+ @interface CCShow : CCActionInstant
+{
+}
+@end
+
+/** Hide the node
+ */
+@interface CCHide : CCActionInstant
+{
+}
+@end
+
+/** Toggles the visibility of a node
+ */
+@interface CCToggleVisibility : CCActionInstant
+{
+}
+@end
+
+/** Flips the sprite horizontally
+ @since v0.99.0
+ */
+@interface CCFlipX : CCActionInstant
+{
+       BOOL    flipX;
+}
++(id) actionWithFlipX:(BOOL)x;
+-(id) initWithFlipX:(BOOL)x;
+@end
+
+/** Flips the sprite vertically
+ @since v0.99.0
+ */
+@interface CCFlipY : CCActionInstant
+{
+       BOOL    flipY;
+}
++(id) actionWithFlipY:(BOOL)y;
+-(id) initWithFlipY:(BOOL)y;
+@end
+
+/** Places the node in a certain position
+ */
+@interface CCPlace : CCActionInstant <NSCopying>
+{
+       CGPoint position;
+}
+/** creates a Place action with a position */
++(id) actionWithPosition: (CGPoint) pos;
+/** Initializes a Place action with a position */
+-(id) initWithPosition: (CGPoint) pos;
+@end
+
+/** Calls a 'callback'
+ */
+@interface CCCallFunc : CCActionInstant <NSCopying>
+{
+       id targetCallback_;
+       SEL selector_;
+}
+
+/** Target that will be called */
+@property (nonatomic, readwrite, retain) id targetCallback;
+
+/** creates the action with the callback */
++(id) actionWithTarget: (id) t selector:(SEL) s;
+/** initializes the action with the callback */
+-(id) initWithTarget: (id) t selector:(SEL) s;
+/** exeuctes the callback */
+-(void) execute;
+@end
+
+/** Calls a 'callback' with the node as the first argument.
+ N means Node
+ */
+@interface CCCallFuncN : CCCallFunc
+{
+}
+@end
+
+typedef void (*CC_CALLBACK_ND)(id, SEL, id, void *);
+/** Calls a 'callback' with the node as the first argument and the 2nd argument is data.
+ * ND means: Node and Data. Data is void *, so it could be anything.
+ */
+@interface CCCallFuncND : CCCallFuncN
+{
+       void                    *data_;
+       CC_CALLBACK_ND  callbackMethod_;
+}
+
+/** Invocation object that has the target#selector and the parameters */
+@property (nonatomic,readwrite) CC_CALLBACK_ND callbackMethod;
+
+/** creates the action with the callback and the data to pass as an argument */
++(id) actionWithTarget: (id) t selector:(SEL) s data:(void*)d;
+/** initializes the action with the callback and the data to pass as an argument */
+-(id) initWithTarget:(id) t selector:(SEL) s data:(void*) d;
+@end
+
+/** Calls a 'callback' with an object as the first argument.
+ O means Object.
+ @since v0.99.5
+ */
+@interface CCCallFuncO : CCCallFunc
+{
+       id      object_;
+}
+/** object to be passed as argument */
+@property (nonatomic, readwrite, retain) id object;
+
+/** creates the action with the callback and the object to pass as an argument */
++(id) actionWithTarget: (id) t selector:(SEL) s object:(id)object;
+/** initializes the action with the callback and the object to pass as an argument */
+-(id) initWithTarget:(id) t selector:(SEL) s object:(id)object;
+
+@end
+
+#pragma mark Blocks Support
+
+#if NS_BLOCKS_AVAILABLE
+
+/** Executes a callback using a block.
+ */
+@interface CCCallBlock : CCActionInstant<NSCopying>
+{
+       void (^block_)();
+}
+
+/** creates the action with the specified block, to be used as a callback.
+ The block will be "copied".
+ */
++(id) actionWithBlock:(void(^)())block;
+
+/** initialized the action with the specified block, to be used as a callback.
+ The block will be "copied".
+ */
+-(id) initWithBlock:(void(^)())block;
+
+/** executes the callback */
+-(void) execute;
+@end
+
+@class CCNode;
+
+/** Executes a callback using a block with a single CCNode parameter.
+ */
+@interface CCCallBlockN : CCActionInstant<NSCopying>
+{
+       void (^block_)(CCNode *);
+}
+
+/** creates the action with the specified block, to be used as a callback.
+ The block will be "copied".
+ */
++(id) actionWithBlock:(void(^)(CCNode *node))block;
+
+/** initialized the action with the specified block, to be used as a callback.
+ The block will be "copied".
+ */
+-(id) initWithBlock:(void(^)(CCNode *node))block;
+
+/** executes the callback */
+-(void) execute;
+@end
+
+#endif
diff --git a/libs/cocos2d/CCActionInstant.m b/libs/cocos2d/CCActionInstant.m
new file mode 100644 (file)
index 0000000..e7f6fad
--- /dev/null
@@ -0,0 +1,477 @@
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2008-2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+
+#import "CCBlockSupport.h"
+#import "CCActionInstant.h"
+#import "CCNode.h"
+#import "CCSprite.h"
+
+
+//
+// InstantAction
+//
+#pragma mark CCActionInstant
+
+@implementation CCActionInstant
+
+-(id) init
+{
+       if( (self=[super init]) )       
+               duration_ = 0;
+       
+       return self;
+}
+
+-(id) copyWithZone: (NSZone*) zone
+{
+       CCActionInstant *copy = [[[self class] allocWithZone: zone] init];
+       return copy;
+}
+
+- (BOOL) isDone
+{
+       return YES;
+}
+
+-(void) step: (ccTime) dt
+{
+       [self update: 1];
+}
+
+-(void) update: (ccTime) t
+{
+       // ignore
+}
+
+-(CCFiniteTimeAction*) reverse
+{
+       return [[self copy] autorelease];
+}
+@end
+
+//
+// Show
+//
+#pragma mark CCShow
+
+@implementation CCShow
+-(void) startWithTarget:(id)aTarget
+{
+       [super startWithTarget:aTarget];
+       ((CCNode *)target_).visible = YES;
+}
+
+-(CCFiniteTimeAction*) reverse
+{
+       return [CCHide action];
+}
+@end
+
+//
+// Hide
+//
+#pragma mark CCHide
+
+@implementation CCHide
+-(void) startWithTarget:(id)aTarget
+{
+       [super startWithTarget:aTarget];
+       ((CCNode *)target_).visible = NO;
+}
+
+-(CCFiniteTimeAction*) reverse
+{
+       return [CCShow action];
+}
+@end
+
+//
+// ToggleVisibility
+//
+#pragma mark CCToggleVisibility
+
+@implementation CCToggleVisibility
+-(void) startWithTarget:(id)aTarget
+{
+       [super startWithTarget:aTarget];
+       ((CCNode *)target_).visible = !((CCNode *)target_).visible;
+}
+@end
+
+//
+// FlipX
+//
+#pragma mark CCFlipX
+
+@implementation CCFlipX
++(id) actionWithFlipX:(BOOL)x
+{
+       return [[[self alloc] initWithFlipX:x] autorelease];
+}
+
+-(id) initWithFlipX:(BOOL)x
+{
+       if(( self=[super init]))
+               flipX = x;
+       
+       return self;
+}
+
+-(void) startWithTarget:(id)aTarget
+{
+       [super startWithTarget:aTarget];
+       [(CCSprite*)aTarget setFlipX:flipX];
+}
+
+-(CCFiniteTimeAction*) reverse
+{
+       return [CCFlipX actionWithFlipX:!flipX];
+}
+
+-(id) copyWithZone: (NSZone*) zone
+{
+       CCActionInstant *copy = [[[self class] allocWithZone: zone] initWithFlipX:flipX];
+       return copy;
+}
+@end
+
+//
+// FlipY
+//
+#pragma mark CCFlipY
+
+@implementation CCFlipY
++(id) actionWithFlipY:(BOOL)y
+{
+       return [[[self alloc] initWithFlipY:y] autorelease];
+}
+
+-(id) initWithFlipY:(BOOL)y
+{
+       if(( self=[super init]))
+               flipY = y;
+       
+       return self;
+}
+
+-(void) startWithTarget:(id)aTarget
+{
+       [super startWithTarget:aTarget];
+       [(CCSprite*)aTarget setFlipY:flipY];
+}
+
+-(CCFiniteTimeAction*) reverse
+{
+       return [CCFlipY actionWithFlipY:!flipY];
+}
+
+-(id) copyWithZone: (NSZone*) zone
+{
+       CCActionInstant *copy = [[[self class] allocWithZone: zone] initWithFlipY:flipY];
+       return copy;
+}
+@end
+
+
+//
+// Place
+//
+#pragma mark CCPlace
+
+@implementation CCPlace
++(id) actionWithPosition: (CGPoint) pos
+{
+       return [[[self alloc]initWithPosition:pos]autorelease];
+}
+
+-(id) initWithPosition: (CGPoint) pos
+{
+       if( (self=[super init]) )
+               position = pos;
+       
+       return self;
+}
+
+-(id) copyWithZone: (NSZone*) zone
+{
+       CCActionInstant *copy = [[[self class] allocWithZone: zone] initWithPosition: position];
+       return copy;
+}
+
+-(void) startWithTarget:(id)aTarget
+{
+       [super startWithTarget:aTarget];
+       ((CCNode *)target_).position = position;
+}
+
+@end
+
+//
+// CallFunc
+//
+#pragma mark CCCallFunc
+
+@implementation CCCallFunc
+
+@synthesize targetCallback = targetCallback_;
+
++(id) actionWithTarget: (id) t selector:(SEL) s
+{
+       return [[[self alloc] initWithTarget: t selector: s] autorelease];
+}
+
+-(id) initWithTarget: (id) t selector:(SEL) s
+{
+       if( (self=[super init]) ) {
+               self.targetCallback = t;
+               selector_ = s;
+       }
+       return self;
+}
+
+-(NSString*) description
+{
+       return [NSString stringWithFormat:@"<%@ = %08X | Tag = %i | target = %@ | selector = %@>",
+                       [self class],
+                       self,
+                       tag_,
+                       [targetCallback_ class],
+                       NSStringFromSelector(selector_)
+                       ];
+}
+
+-(void) dealloc
+{
+       [targetCallback_ release];
+       [super dealloc];
+}
+
+-(id) copyWithZone: (NSZone*) zone
+{
+       CCActionInstant *copy = [[[self class] allocWithZone: zone] initWithTarget:targetCallback_ selector:selector_];
+       return copy;
+}
+
+-(void) startWithTarget:(id)aTarget
+{
+       [super startWithTarget:aTarget];
+       [self execute];
+}
+
+-(void) execute
+{
+       [targetCallback_ performSelector:selector_];
+}
+@end
+
+//
+// CallFuncN
+//
+#pragma mark CCCallFuncN
+
+@implementation CCCallFuncN
+
+-(void) execute
+{
+       [targetCallback_ performSelector:selector_ withObject:target_];
+}
+@end
+
+//
+// CallFuncND
+//
+#pragma mark CCCallFuncND
+
+@implementation CCCallFuncND
+
+@synthesize callbackMethod = callbackMethod_;
+
++(id) actionWithTarget:(id)t selector:(SEL)s data:(void*)d
+{
+       return [[[self alloc] initWithTarget:t selector:s data:d] autorelease];
+}
+
+-(id) initWithTarget:(id)t selector:(SEL)s data:(void*)d
+{
+       if( (self=[super initWithTarget:t selector:s]) ) {
+               data_ = d;
+
+#if COCOS2D_DEBUG
+               NSMethodSignature * sig = [t methodSignatureForSelector:s]; // added
+               NSAssert(sig !=0 , @"Signature not found for selector - does it have the following form? -(void)name:(id)sender data:(void*)data");
+#endif
+               callbackMethod_ = (CC_CALLBACK_ND) [t methodForSelector:s];
+       }
+       return self;
+}
+
+-(id) copyWithZone: (NSZone*) zone
+{
+       CCActionInstant *copy = [[[self class] allocWithZone: zone] initWithTarget:targetCallback_ selector:selector_ data:data_];
+       return copy;
+}
+
+-(void) dealloc
+{
+       // nothing to dealloc really. Everything is dealloc on super (CCCallFuncN)
+       [super dealloc];
+}
+
+-(void) execute
+{
+       callbackMethod_(targetCallback_,selector_,target_, data_);
+}
+@end
+
+@implementation CCCallFuncO
+@synthesize  object = object_;
+
++(id) actionWithTarget: (id) t selector:(SEL) s object:(id)object
+{
+       return [[[self alloc] initWithTarget:t selector:s object:object] autorelease];
+}
+
+-(id) initWithTarget:(id) t selector:(SEL) s object:(id)object
+{
+       if( (self=[super initWithTarget:t selector:s] ) )
+               self.object = object;
+       
+       return self;
+}
+
+- (void) dealloc
+{
+       [object_ release];
+       [super dealloc];
+}
+
+-(id) copyWithZone: (NSZone*) zone
+{
+       CCActionInstant *copy = [[[self class] allocWithZone: zone] initWithTarget:targetCallback_ selector:selector_ object:object_];
+       return copy;
+}
+
+
+-(void) execute
+{
+       [targetCallback_ performSelector:selector_ withObject:object_];
+}
+
+@end
+
+
+#pragma mark -
+#pragma mark Blocks
+
+#if NS_BLOCKS_AVAILABLE
+
+#pragma mark CCCallBlock
+
+@implementation CCCallBlock
+
++(id) actionWithBlock:(void(^)())block
+{
+       return [[[self alloc] initWithBlock:block] autorelease];
+}
+
+-(id) initWithBlock:(void(^)())block
+{
+       if ((self = [super init]))
+               block_ = [block copy];
+       
+       return self;
+}
+
+-(id) copyWithZone: (NSZone*) zone
+{
+       CCActionInstant *copy = [[[self class] allocWithZone: zone] initWithBlock:block_];
+       return copy;
+}
+
+-(void) startWithTarget:(id)aTarget
+{
+       [super startWithTarget:aTarget];
+       [self execute];
+}
+
+-(void) execute
+{
+       block_();
+}
+
+-(void) dealloc
+{
+       [block_ release];
+       [super dealloc];
+}
+
+@end
+
+#pragma mark CCCallBlockN
+
+@implementation CCCallBlockN
+
++(id) actionWithBlock:(void(^)(CCNode *node))block
+{
+       return [[[self alloc] initWithBlock:block] autorelease];
+}
+
+-(id) initWithBlock:(void(^)(CCNode *node))block
+{
+       if ((self = [super init]))
+               block_ = [block copy];
+       
+       return self;
+}
+
+-(id) copyWithZone: (NSZone*) zone
+{
+       CCActionInstant *copy = [[[self class] allocWithZone: zone] initWithBlock:block_];
+       return copy;
+}
+
+-(void) startWithTarget:(id)aTarget
+{
+       [super startWithTarget:aTarget];
+       [self execute];
+}
+
+-(void) execute
+{
+       block_(target_);
+}
+
+-(void) dealloc
+{
+       [block_ release];
+       [super dealloc];
+}
+
+@end
+
+
+#endif // NS_BLOCKS_AVAILABLE
diff --git a/libs/cocos2d/CCActionInterval.h b/libs/cocos2d/CCActionInterval.h
new file mode 100644 (file)
index 0000000..c667963
--- /dev/null
@@ -0,0 +1,421 @@
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2008-2011 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+
+#import "CCNode.h"
+#import "CCAction.h"
+#import "CCProtocols.h"
+
+#include <sys/time.h>
+
+/** An interval action is an action that takes place within a certain period of time.
+It has an start time, and a finish time. The finish time is the parameter
+duration plus the start time.
+
+These CCActionInterval actions have some interesting properties, like:
+ - They can run normally (default)
+ - They can run reversed with the reverse method
+ - They can run with the time altered with the Accelerate, AccelDeccel and Speed actions.
+
+For example, you can simulate a Ping Pong effect running the action normally and
+then running it again in Reverse mode.
+
+Example:
+       CCAction * pingPongAction = [CCSequence actions: action, [action reverse], nil];
+*/
+@interface CCActionInterval: CCFiniteTimeAction <NSCopying>
+{
+       ccTime  elapsed_;
+       BOOL    firstTick_;
+}
+
+/** how many seconds had elapsed since the actions started to run. */
+@property (nonatomic,readonly) ccTime elapsed;
+
+/** creates the action */
++(id) actionWithDuration: (ccTime) d;
+/** initializes the action */
+-(id) initWithDuration: (ccTime) d;
+/** returns YES if the action has finished */
+-(BOOL) isDone;
+/** returns a reversed action */
+- (CCActionInterval*) reverse;
+@end
+
+/** Runs actions sequentially, one after another
+ */
+@interface CCSequence : CCActionInterval <NSCopying>
+{
+       CCFiniteTimeAction *actions_[2];
+       ccTime split_;
+       int last_;
+}
+/** helper contructor to create an array of sequenceable actions */
++(id) actions: (CCFiniteTimeAction*) action1, ... NS_REQUIRES_NIL_TERMINATION;
+/** helper contructor to create an array of sequenceable actions given an array */
++(id) actionsWithArray: (NSArray*) actions;
+/** creates the action */
++(id) actionOne:(CCFiniteTimeAction*)actionOne two:(CCFiniteTimeAction*)actionTwo;
+/** initializes the action */
+-(id) initOne:(CCFiniteTimeAction*)actionOne two:(CCFiniteTimeAction*)actionTwo;
+@end
+
+
+/** Repeats an action a number of times.
+ * To repeat an action forever use the CCRepeatForever action.
+ */
+@interface CCRepeat : CCActionInterval <NSCopying>
+{
+       NSUInteger times_;
+       NSUInteger total_;
+       CCFiniteTimeAction *innerAction_;
+}
+
+/** Inner action */
+@property (nonatomic,readwrite,retain) CCFiniteTimeAction *innerAction;
+
+/** creates a CCRepeat action. Times is an unsigned integer between 1 and MAX_UINT */
++(id) actionWithAction:(CCFiniteTimeAction*)action times: (NSUInteger)times;
+/** initializes a CCRepeat action. Times is an unsigned integer between 1 and MAX_UINT */
+-(id) initWithAction:(CCFiniteTimeAction*)action times: (NSUInteger)times;
+@end
+
+/** Spawn a new action immediately
+ */
+@interface CCSpawn : CCActionInterval <NSCopying>
+{
+       CCFiniteTimeAction *one_;
+       CCFiniteTimeAction *two_;
+}
+/** helper constructor to create an array of spawned actions */
++(id) actions: (CCFiniteTimeAction*) action1, ... NS_REQUIRES_NIL_TERMINATION;
+/** helper contructor to create an array of spawned actions given an array */
++(id) actionsWithArray: (NSArray*) actions;
+/** creates the Spawn action */
++(id) actionOne: (CCFiniteTimeAction*) one two:(CCFiniteTimeAction*) two;
+/** initializes the Spawn action with the 2 actions to spawn */
+-(id) initOne: (CCFiniteTimeAction*) one two:(CCFiniteTimeAction*) two;
+@end
+
+/**  Rotates a CCNode object to a certain angle by modifying it's
+ rotation attribute.
+ The direction will be decided by the shortest angle.
+*/ 
+@interface CCRotateTo : CCActionInterval <NSCopying>
+{
+       float dstAngle_;
+       float startAngle_;
+       float diffAngle_;
+}
+/** creates the action */
++(id) actionWithDuration:(ccTime)duration angle:(float)angle;
+/** initializes the action */
+-(id) initWithDuration:(ccTime)duration angle:(float)angle;
+@end
+
+/** Rotates a CCNode object clockwise a number of degrees by modiying it's rotation attribute.
+*/
+@interface CCRotateBy : CCActionInterval <NSCopying>
+{
+       float angle_;
+       float startAngle_;
+}
+/** creates the action */
++(id) actionWithDuration:(ccTime)duration angle:(float)deltaAngle;
+/** initializes the action */
+-(id) initWithDuration:(ccTime)duration angle:(float)deltaAngle;
+@end
+
+/** Moves a CCNode object to the position x,y. x and y are absolute coordinates by modifying it's position attribute.
+*/
+@interface CCMoveTo : CCActionInterval <NSCopying>
+{
+       CGPoint endPosition_;
+       CGPoint startPosition_;
+       CGPoint delta_;
+}
+/** creates the action */
++(id) actionWithDuration:(ccTime)duration position:(CGPoint)position;
+/** initializes the action */
+-(id) initWithDuration:(ccTime)duration position:(CGPoint)position;
+@end
+
+/**  Moves a CCNode object x,y pixels by modifying it's position attribute.
+ x and y are relative to the position of the object.
+ Duration is is seconds.
+*/ 
+@interface CCMoveBy : CCMoveTo <NSCopying>
+{
+}
+/** creates the action */
++(id) actionWithDuration: (ccTime)duration position:(CGPoint)deltaPosition;
+/** initializes the action */
+-(id) initWithDuration: (ccTime)duration position:(CGPoint)deltaPosition;
+@end
+
+/** Skews a CCNode object to given angles by modifying it's skewX and skewY attributes
+ @since v1.0
+ */
+@interface CCSkewTo : CCActionInterval <NSCopying>
+{
+       float skewX_;
+       float skewY_;
+       float startSkewX_;
+       float startSkewY_;
+       float endSkewX_;
+       float endSkewY_;
+       float deltaX_;
+       float deltaY_;
+}
+/** creates the action */
++(id) actionWithDuration:(ccTime)t skewX:(float)sx skewY:(float)sy;
+/** initializes the action */
+-(id) initWithDuration:(ccTime)t skewX:(float)sx skewY:(float)sy;
+@end
+
+/** Skews a CCNode object by skewX and skewY degrees
+ @since v1.0
+ */
+@interface CCSkewBy : CCSkewTo <NSCopying>
+{
+}
+@end
+
+/** Moves a CCNode object simulating a parabolic jump movement by modifying it's position attribute.
+*/
+ @interface CCJumpBy : CCActionInterval <NSCopying>
+{
+       CGPoint startPosition_;
+       CGPoint delta_;
+       ccTime height_;
+       NSUInteger jumps_;
+}
+/** creates the action */
++(id) actionWithDuration: (ccTime)duration position:(CGPoint)position height:(ccTime)height jumps:(NSUInteger)jumps;
+/** initializes the action */
+-(id) initWithDuration: (ccTime)duration position:(CGPoint)position height:(ccTime)height jumps:(NSUInteger)jumps;
+@end
+
+/** Moves a CCNode object to a parabolic position simulating a jump movement by modifying it's position attribute.
+*/ 
+ @interface CCJumpTo : CCJumpBy <NSCopying>
+{
+}
+@end
+
+/** bezier configuration structure
+ */
+typedef struct _ccBezierConfig {
+       //! end position of the bezier
+       CGPoint endPosition;
+       //! Bezier control point 1
+       CGPoint controlPoint_1;
+       //! Bezier control point 2
+       CGPoint controlPoint_2;
+} ccBezierConfig;
+
+/** An action that moves the target with a cubic Bezier curve by a certain distance.
+ */
+@interface CCBezierBy : CCActionInterval <NSCopying>
+{
+       ccBezierConfig config_;
+       CGPoint startPosition_;
+}
+
+/** creates the action with a duration and a bezier configuration */
++(id) actionWithDuration: (ccTime) t bezier:(ccBezierConfig) c;
+
+/** initializes the action with a duration and a bezier configuration */
+-(id) initWithDuration: (ccTime) t bezier:(ccBezierConfig) c;
+@end
+
+/** An action that moves the target with a cubic Bezier curve to a destination point.
+ @since v0.8.2
+ */
+@interface CCBezierTo : CCBezierBy
+{
+}
+@end
+
+/** Scales a CCNode object to a zoom factor by modifying it's scale attribute.
+ @warning This action doesn't support "reverse"
+ */
+@interface CCScaleTo : CCActionInterval <NSCopying>
+{
+       float scaleX_;
+       float scaleY_;
+       float startScaleX_;
+       float startScaleY_;
+       float endScaleX_;
+       float endScaleY_;
+       float deltaX_;
+       float deltaY_;
+}
+/** creates the action with the same scale factor for X and Y */
++(id) actionWithDuration: (ccTime)duration scale:(float) s;
+/** initializes the action with the same scale factor for X and Y */
+-(id) initWithDuration: (ccTime)duration scale:(float) s;
+/** creates the action with and X factor and a Y factor */
++(id) actionWithDuration: (ccTime)duration scaleX:(float) sx scaleY:(float)sy;
+/** initializes the action with and X factor and a Y factor */
+-(id) initWithDuration: (ccTime)duration scaleX:(float) sx scaleY:(float)sy;
+@end
+
+/** Scales a CCNode object a zoom factor by modifying it's scale attribute.
+*/
+@interface CCScaleBy : CCScaleTo <NSCopying>
+{
+}
+@end
+
+/** Blinks a CCNode object by modifying it's visible attribute
+*/
+@interface CCBlink : CCActionInterval <NSCopying>
+{
+       NSUInteger times_;
+}
+/** creates the action */
++(id) actionWithDuration: (ccTime)duration blinks:(NSUInteger)blinks;
+/** initilizes the action */
+-(id) initWithDuration: (ccTime)duration blinks:(NSUInteger)blinks;
+@end
+
+/** Fades In an object that implements the CCRGBAProtocol protocol. It modifies the opacity from 0 to 255.
+ The "reverse" of this action is FadeOut
+ */
+@interface CCFadeIn : CCActionInterval <NSCopying>
+{
+}
+@end
+
+/** Fades Out an object that implements the CCRGBAProtocol protocol. It modifies the opacity from 255 to 0.
+ The "reverse" of this action is FadeIn
+*/
+@interface CCFadeOut : CCActionInterval <NSCopying>
+{
+}
+@end
+
+/** Fades an object that implements the CCRGBAProtocol protocol. It modifies the opacity from the current value to a custom one.
+ @warning This action doesn't support "reverse"
+ */
+@interface CCFadeTo : CCActionInterval <NSCopying>
+{
+       GLubyte toOpacity_;
+       GLubyte fromOpacity_;
+}
+/** creates an action with duration and opactiy */
++(id) actionWithDuration:(ccTime)duration opacity:(GLubyte)opactiy;
+/** initializes the action with duration and opacity */
+-(id) initWithDuration:(ccTime)duration opacity:(GLubyte)opacity;
+@end
+
+/** Tints a CCNode that implements the CCNodeRGB protocol from current tint to a custom one.
+ @warning This action doesn't support "reverse"
+ @since v0.7.2
+*/
+@interface CCTintTo : CCActionInterval <NSCopying>
+{
+       ccColor3B to_;
+       ccColor3B from_;
+}
+/** creates an action with duration and color */
++(id) actionWithDuration:(ccTime)duration red:(GLubyte)red green:(GLubyte)green blue:(GLubyte)blue;
+/** initializes the action with duration and color */
+-(id) initWithDuration:(ccTime)duration red:(GLubyte)red green:(GLubyte)green blue:(GLubyte)blue;
+@end
+
+/** Tints a CCNode that implements the CCNodeRGB protocol from current tint to a custom one.
+ @since v0.7.2
+ */
+@interface CCTintBy : CCActionInterval <NSCopying>
+{
+       GLshort deltaR_, deltaG_, deltaB_;
+       GLshort fromR_, fromG_, fromB_;
+}
+/** creates an action with duration and color */
++(id) actionWithDuration:(ccTime)duration red:(GLshort)deltaRed green:(GLshort)deltaGreen blue:(GLshort)deltaBlue;
+/** initializes the action with duration and color */
+-(id) initWithDuration:(ccTime)duration red:(GLshort)deltaRed green:(GLshort)deltaGreen blue:(GLshort)deltaBlue;
+@end
+
+/** Delays the action a certain amount of seconds
+*/
+@interface CCDelayTime : CCActionInterval <NSCopying>
+{
+}
+@end
+
+/** Executes an action in reverse order, from time=duration to time=0
+ @warning Use this action carefully. This action is not
+ sequenceable. Use it as the default "reversed" method
+ of your own actions, but using it outside the "reversed"
+ scope is not recommended.
+*/
+@interface CCReverseTime : CCActionInterval <NSCopying>
+{
+       CCFiniteTimeAction * other_;
+}
+/** creates the action */
++(id) actionWithAction: (CCFiniteTimeAction*) action;
+/** initializes the action */
+-(id) initWithAction: (CCFiniteTimeAction*) action;
+@end
+
+
+@class CCAnimation;
+@class CCTexture2D;
+/** Animates a sprite given the name of an Animation */
+@interface CCAnimate : CCActionInterval <NSCopying>
+{
+       CCAnimation *animation_;
+       id origFrame_;
+       BOOL restoreOriginalFrame_;
+}
+/** animation used for the animage */
+@property (readwrite,nonatomic,retain) CCAnimation * animation;
+
+/** creates the action with an Animation and will restore the original frame when the animation is over */
++(id) actionWithAnimation:(CCAnimation*) a;
+/** initializes the action with an Animation and will restore the original frame when the animtion is over */
+-(id) initWithAnimation:(CCAnimation*) a;
+/** creates the action with an Animation */
++(id) actionWithAnimation:(CCAnimation*) a restoreOriginalFrame:(BOOL)b;
+/** initializes the action with an Animation */
+-(id) initWithAnimation:(CCAnimation*) a restoreOriginalFrame:(BOOL)b;
+/** creates an action with a duration, animation and depending of the restoreOriginalFrame, it will restore the original frame or not.
+ The 'delay' parameter of the animation will be overrided by the duration parameter.
+ @since v0.99.0
+ */
++(id) actionWithDuration:(ccTime)duration animation:(CCAnimation*)animation restoreOriginalFrame:(BOOL)b;
+/** initializes an action with a duration, animation and depending of the restoreOriginalFrame, it will restore the original frame or not.
+ The 'delay' parameter of the animation will be overrided by the duration parameter.
+ @since v0.99.0
+ */
+-(id) initWithDuration:(ccTime)duration animation:(CCAnimation*)animation restoreOriginalFrame:(BOOL)b;
+@end
diff --git a/libs/cocos2d/CCActionInterval.m b/libs/cocos2d/CCActionInterval.m
new file mode 100644 (file)
index 0000000..ef374bf
--- /dev/null
@@ -0,0 +1,1354 @@
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2008-2011 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+
+
+#import "CCActionInterval.h"
+#import "CCSprite.h"
+#import "CCSpriteFrame.h"
+#import "CCAnimation.h"
+#import "CCNode.h"
+#import "Support/CGPointExtension.h"
+
+//
+// IntervalAction
+//
+#pragma mark -
+#pragma mark IntervalAction
+@implementation CCActionInterval
+
+@synthesize elapsed = elapsed_;
+
+-(id) init
+{
+       NSAssert(NO, @"IntervalActionInit: Init not supported. Use InitWithDuration");
+       [self release];
+       return nil;
+}
+
++(id) actionWithDuration: (ccTime) d
+{
+       return [[[self alloc] initWithDuration:d ] autorelease];
+}
+
+-(id) initWithDuration: (ccTime) d
+{
+       if( (self=[super init]) ) {
+               duration_ = d;
+               
+               // prevent division by 0
+               // This comparison could be in step:, but it might decrease the performance
+               // by 3% in heavy based action games.
+               if( duration_ == 0 )
+                       duration_ = FLT_EPSILON;
+               elapsed_ = 0;
+               firstTick_ = YES;
+       }
+       return self;
+}
+
+-(id) copyWithZone: (NSZone*) zone
+{
+       CCAction *copy = [[[self class] allocWithZone: zone] initWithDuration: [self duration] ];
+       return copy;
+}
+
+- (BOOL) isDone
+{
+       return (elapsed_ >= duration_);
+}
+
+-(void) step: (ccTime) dt
+{
+       if( firstTick_ ) {
+               firstTick_ = NO;
+               elapsed_ = 0;
+       } else
+               elapsed_ += dt;
+
+       [self update: MIN(1, elapsed_/duration_)];
+}
+
+-(void) startWithTarget:(id)aTarget
+{
+       [super startWithTarget:aTarget];
+       elapsed_ = 0.0f;
+       firstTick_ = YES;
+}
+
+- (CCActionInterval*) reverse
+{
+       NSAssert(NO, @"CCIntervalAction: reverse not implemented.");
+       return nil;
+}
+@end
+
+//
+// Sequence
+//
+#pragma mark -
+#pragma mark Sequence
+@implementation CCSequence
++(id) actions: (CCFiniteTimeAction*) action1, ...
+{
+       va_list params;
+       va_start(params,action1);
+       
+       CCFiniteTimeAction *now;
+       CCFiniteTimeAction *prev = action1;
+       
+       while( action1 ) {
+               now = va_arg(params,CCFiniteTimeAction*);
+               if ( now )
+                       prev = [self actionOne: prev two: now];
+               else
+                       break;
+       }
+       va_end(params);
+       return prev;
+}
+
++(id) actionsWithArray: (NSArray*) actions
+{
+       CCFiniteTimeAction *prev = [actions objectAtIndex:0];
+       
+       for (int i = 1; i < [actions count]; i++)
+               prev = [self actionOne:prev two:[actions objectAtIndex:i]];
+       
+       return prev;
+}
+
++(id) actionOne: (CCFiniteTimeAction*) one two: (CCFiniteTimeAction*) two
+{      
+       return [[[self alloc] initOne:one two:two ] autorelease];
+}
+
+-(id) initOne: (CCFiniteTimeAction*) one two: (CCFiniteTimeAction*) two
+{
+       NSAssert( one!=nil && two!=nil, @"Sequence: arguments must be non-nil");
+       NSAssert( one!=actions_[0] && one!=actions_[1], @"Sequence: re-init using the same parameters is not supported");
+       NSAssert( two!=actions_[1] && two!=actions_[0], @"Sequence: re-init using the same parameters is not supported");
+               
+       ccTime d = [one duration] + [two duration];
+       
+       if( (self=[super initWithDuration: d]) ) {
+
+               // XXX: Supports re-init without leaking. Fails if one==one_ || two==two_
+               [actions_[0] release];
+               [actions_[1] release];
+
+               actions_[0] = [one retain];
+               actions_[1] = [two retain];
+       }
+       
+       return self;
+}
+
+-(id) copyWithZone: (NSZone*) zone
+{
+       CCAction *copy = [[[self class] allocWithZone:zone] initOne:[[actions_[0] copy] autorelease] two:[[actions_[1] copy] autorelease] ];
+       return copy;
+}
+
+-(void) dealloc
+{
+       [actions_[0] release];
+       [actions_[1] release];
+       [super dealloc];
+}
+
+-(void) startWithTarget:(id)aTarget
+{
+       [super startWithTarget:aTarget];        
+       split_ = [actions_[0] duration] / duration_;
+       last_ = -1;
+}
+
+-(void) stop
+{
+       [actions_[0] stop];
+       [actions_[1] stop];
+       [super stop];
+}
+
+-(void) update: (ccTime) t
+{
+       int found = 0;
+       ccTime new_t = 0.0f;
+       
+       if( t >= split_ ) {
+               found = 1;
+               if ( split_ == 1 )
+                       new_t = 1;
+               else
+                       new_t = (t-split_) / (1 - split_ );
+       } else {
+               found = 0;
+               if( split_ != 0 )
+                       new_t = t / split_;
+               else
+                       new_t = 1;
+       }
+       
+       if (last_ == -1 && found==1)    {
+               [actions_[0] startWithTarget:target_];
+               [actions_[0] update:1.0f];
+               [actions_[0] stop];
+       }
+
+       if (last_ != found ) {
+               if( last_ != -1 ) {
+                       [actions_[last_] update: 1.0f];
+                       [actions_[last_] stop];
+               }
+               [actions_[found] startWithTarget:target_];
+       }
+       [actions_[found] update: new_t];
+       last_ = found;
+}
+
+- (CCActionInterval *) reverse
+{
+       return [[self class] actionOne: [actions_[1] reverse] two: [actions_[0] reverse ] ];
+}
+@end
+
+//
+// Repeat
+//
+#pragma mark -
+#pragma mark CCRepeat
+@implementation CCRepeat
+@synthesize innerAction=innerAction_;
+
++(id) actionWithAction:(CCFiniteTimeAction*)action times:(NSUInteger)times
+{
+       return [[[self alloc] initWithAction:action times:times] autorelease];
+}
+
+-(id) initWithAction:(CCFiniteTimeAction*)action times:(NSUInteger)times
+{
+       ccTime d = [action duration] * times;
+
+       if( (self=[super initWithDuration: d ]) ) {
+               times_ = times;
+               self.innerAction = action;
+
+               total_ = 0;
+       }
+       return self;
+}
+
+-(id) copyWithZone: (NSZone*) zone
+{
+       CCAction *copy = [[[self class] allocWithZone:zone] initWithAction:[[innerAction_ copy] autorelease] times:times_];
+       return copy;
+}
+
+-(void) dealloc
+{
+       [innerAction_ release];
+       [super dealloc];
+}
+
+-(void) startWithTarget:(id)aTarget
+{
+       total_ = 0;
+       [super startWithTarget:aTarget];
+       [innerAction_ startWithTarget:aTarget];
+}
+
+-(void) stop
+{    
+    [innerAction_ stop];
+       [super stop];
+}
+
+
+// issue #80. Instead of hooking step:, hook update: since it can be called by any 
+// container action like Repeat, Sequence, AccelDeccel, etc..
+-(void) update:(ccTime) dt
+{
+       ccTime t = dt * times_;
+       if( t > total_+1 ) {
+               [innerAction_ update:1.0f];
+               total_++;
+               [innerAction_ stop];
+               [innerAction_ startWithTarget:target_];
+               
+               // repeat is over ?
+               if( total_== times_ )
+                       // so, set it in the original position
+                       [innerAction_ update:0];
+               else {
+                       // no ? start next repeat with the right update
+                       // to prevent jerk (issue #390)
+                       [innerAction_ update: t-total_];
+               }
+
+       } else {
+               
+               float r = fmodf(t, 1.0f);
+               
+               // fix last repeat position
+               // else it could be 0.
+               if( dt== 1.0f) {
+                       r = 1.0f;
+                       total_++; // this is the added line
+               }
+               [innerAction_ update: MIN(r,1)];
+       }
+}
+
+-(BOOL) isDone
+{
+       return ( total_ == times_ );
+}
+
+- (CCActionInterval *) reverse
+{
+       return [[self class] actionWithAction:[innerAction_ reverse] times:times_];
+}
+@end
+
+//
+// Spawn
+//
+#pragma mark -
+#pragma mark Spawn
+
+@implementation CCSpawn
++(id) actions: (CCFiniteTimeAction*) action1, ...
+{
+       va_list params;
+       va_start(params,action1);
+       
+       CCFiniteTimeAction *now;
+       CCFiniteTimeAction *prev = action1;
+       
+       while( action1 ) {
+               now = va_arg(params,CCFiniteTimeAction*);
+               if ( now )
+                       prev = [self actionOne: prev two: now];
+               else
+                       break;
+       }
+       va_end(params);
+       return prev;
+}
+
++(id) actionsWithArray: (NSArray*) actions
+{
+       CCFiniteTimeAction *prev = [actions objectAtIndex:0];
+       
+       for (int i = 1; i < [actions count]; i++)
+               prev = [self actionOne:prev two:[actions objectAtIndex:i]];
+       
+       return prev;
+}
+
++(id) actionOne: (CCFiniteTimeAction*) one two: (CCFiniteTimeAction*) two
+{      
+       return [[[self alloc] initOne:one two:two ] autorelease];
+}
+
+-(id) initOne: (CCFiniteTimeAction*) one two: (CCFiniteTimeAction*) two
+{
+       NSAssert( one!=nil && two!=nil, @"Spawn: arguments must be non-nil");
+       NSAssert( one!=one_ && one!=two_, @"Spawn: reinit using same parameters is not supported");
+       NSAssert( two!=two_ && two!=one_, @"Spawn: reinit using same parameters is not supported");
+
+       ccTime d1 = [one duration];
+       ccTime d2 = [two duration];     
+       
+       [super initWithDuration: MAX(d1,d2)];
+
+       // XXX: Supports re-init without leaking. Fails if one==one_ || two==two_
+       [one_ release];
+       [two_ release];
+
+       one_ = one;
+       two_ = two;
+
+       if( d1 > d2 )
+               two_ = [CCSequence actionOne:two two:[CCDelayTime actionWithDuration: (d1-d2)] ];
+       else if( d1 < d2)
+               one_ = [CCSequence actionOne:one two: [CCDelayTime actionWithDuration: (d2-d1)] ];
+       
+       [one_ retain];
+       [two_ retain];
+       return self;
+}
+
+-(id) copyWithZone: (NSZone*) zone
+{
+       CCAction *copy = [[[self class] allocWithZone: zone] initOne: [[one_ copy] autorelease] two: [[two_ copy] autorelease] ];
+       return copy;
+}
+
+-(void) dealloc
+{
+       [one_ release];
+       [two_ release];
+       [super dealloc];
+}
+
+-(void) startWithTarget:(id)aTarget
+{
+       [super startWithTarget:aTarget];
+       [one_ startWithTarget:target_];
+       [two_ startWithTarget:target_];
+}
+
+-(void) stop
+{
+       [one_ stop];
+       [two_ stop];
+       [super stop];
+}
+
+-(void) update: (ccTime) t
+{
+       [one_ update:t];
+       [two_ update:t];
+}
+
+- (CCActionInterval *) reverse
+{
+       return [[self class] actionOne: [one_ reverse] two: [two_ reverse ] ];
+}
+@end
+
+//
+// RotateTo
+//
+#pragma mark -
+#pragma mark RotateTo
+
+@implementation CCRotateTo
++(id) actionWithDuration: (ccTime) t angle:(float) a
+{      
+       return [[[self alloc] initWithDuration:t angle:a ] autorelease];
+}
+
+-(id) initWithDuration: (ccTime) t angle:(float) a
+{
+       if( (self=[super initWithDuration: t]) )
+               dstAngle_ = a;
+       
+       return self;
+}
+
+-(id) copyWithZone: (NSZone*) zone
+{
+       CCAction *copy = [[[self class] allocWithZone: zone] initWithDuration:[self duration] angle:dstAngle_];
+       return copy;
+}
+
+-(void) startWithTarget:(CCNode *)aTarget
+{
+       [super startWithTarget:aTarget];
+       
+       startAngle_ = [target_ rotation];
+       if (startAngle_ > 0)
+               startAngle_ = fmodf(startAngle_, 360.0f);
+       else
+               startAngle_ = fmodf(startAngle_, -360.0f);
+       
+       diffAngle_ =dstAngle_ - startAngle_;
+       if (diffAngle_ > 180)
+               diffAngle_ -= 360;
+       if (diffAngle_ < -180)
+               diffAngle_ += 360;
+}
+-(void) update: (ccTime) t
+{
+       [target_ setRotation: startAngle_ + diffAngle_ * t];
+}
+@end
+
+
+//
+// RotateBy
+//
+#pragma mark -
+#pragma mark RotateBy
+
+@implementation CCRotateBy
++(id) actionWithDuration: (ccTime) t angle:(float) a
+{      
+       return [[[self alloc] initWithDuration:t angle:a ] autorelease];
+}
+
+-(id) initWithDuration: (ccTime) t angle:(float) a
+{
+       if( (self=[super initWithDuration: t]) )
+               angle_ = a;
+       
+       return self;
+}
+
+-(id) copyWithZone: (NSZone*) zone
+{
+       CCAction *copy = [[[self class] allocWithZone: zone] initWithDuration: [self duration] angle: angle_];
+       return copy;
+}
+
+-(void) startWithTarget:(id)aTarget
+{
+       [super startWithTarget:aTarget];
+       startAngle_ = [target_ rotation];
+}
+
+-(void) update: (ccTime) t
+{      
+       // XXX: shall I add % 360
+       [target_ setRotation: (startAngle_ +angle_ * t )];
+}
+
+-(CCActionInterval*) reverse
+{
+       return [[self class] actionWithDuration:duration_ angle:-angle_];
+}
+
+@end
+
+//
+// MoveTo
+//
+#pragma mark -
+#pragma mark MoveTo
+
+@implementation CCMoveTo
++(id) actionWithDuration: (ccTime) t position: (CGPoint) p
+{      
+       return [[[self alloc] initWithDuration:t position:p ] autorelease];
+}
+
+-(id) initWithDuration: (ccTime) t position: (CGPoint) p
+{
+       if( (self=[super initWithDuration: t]) )
+               endPosition_ = p;
+       
+       return self;
+}
+
+-(id) copyWithZone: (NSZone*) zone
+{
+       CCAction *copy = [[[self class] allocWithZone: zone] initWithDuration: [self duration] position: endPosition_];
+       return copy;
+}
+
+-(void) startWithTarget:(CCNode *)aTarget
+{
+       [super startWithTarget:aTarget];
+       startPosition_ = [(CCNode*)target_ position];
+       delta_ = ccpSub( endPosition_, startPosition_ );
+}
+
+-(void) update: (ccTime) t
+{      
+       [target_ setPosition: ccp( (startPosition_.x + delta_.x * t ), (startPosition_.y + delta_.y * t ) )];
+}
+@end
+
+//
+// MoveBy
+//
+#pragma mark -
+#pragma mark MoveBy
+
+@implementation CCMoveBy
++(id) actionWithDuration: (ccTime) t position: (CGPoint) p
+{      
+       return [[[self alloc] initWithDuration:t position:p ] autorelease];
+}
+
+-(id) initWithDuration: (ccTime) t position: (CGPoint) p
+{
+       if( (self=[super initWithDuration: t]) )
+               delta_ = p;
+       
+       return self;
+}
+
+-(id) copyWithZone: (NSZone*) zone
+{
+       CCAction *copy = [[[self class] allocWithZone: zone] initWithDuration: [self duration] position: delta_];
+       return copy;
+}
+
+-(void) startWithTarget:(CCNode *)aTarget
+{
+       CGPoint dTmp = delta_;
+       [super startWithTarget:aTarget];
+       delta_ = dTmp;
+}
+
+-(CCActionInterval*) reverse
+{
+       return [[self class] actionWithDuration:duration_ position:ccp( -delta_.x, -delta_.y)];
+}
+@end
+
+
+//
+// SkewTo
+//
+#pragma mark -
+#pragma mark SkewTo
+
+@implementation CCSkewTo
++(id) actionWithDuration:(ccTime)t skewX:(float)sx skewY:(float)sy 
+{
+       return [[[self alloc] initWithDuration: t skewX:sx skewY:sy] autorelease];
+}
+
+-(id) initWithDuration:(ccTime)t skewX:(float)sx skewY:(float)sy 
+{
+       if( (self=[super initWithDuration:t]) ) {       
+               endSkewX_ = sx;
+               endSkewY_ = sy;
+       }
+       return self;
+}
+
+-(id) copyWithZone: (NSZone*) zone
+{
+       CCAction *copy = [[[self class] allocWithZone: zone] initWithDuration:[self duration] skewX:endSkewX_ skewY:endSkewY_];
+       return copy;
+}
+
+-(void) startWithTarget:(CCNode *)aTarget
+{
+       [super startWithTarget:aTarget];
+       
+       startSkewX_ = [target_ skewX];
+       
+       if (startSkewX_ > 0)
+               startSkewX_ = fmodf(startSkewX_, 180.0f);
+       else
+               startSkewX_ = fmodf(startSkewX_, -180.0f);
+       
+       deltaX_ = endSkewX_ - startSkewX_;
+       
+       if ( deltaX_ > 180 ) {
+               deltaX_ -= 360;
+       }
+       if ( deltaX_ < -180 ) {
+               deltaX_ += 360;
+       }
+       
+       startSkewY_ = [target_ skewY];
+               
+       if (startSkewY_ > 0)
+               startSkewY_ = fmodf(startSkewY_, 360.0f);
+       else
+               startSkewY_ = fmodf(startSkewY_, -360.0f);
+       
+       deltaY_ = endSkewY_ - startSkewY_;
+       
+       if ( deltaY_ > 180 ) {
+               deltaY_ -= 360;
+       }
+       if ( deltaY_ < -180 ) {
+               deltaY_ += 360;
+       }
+}
+
+-(void) update: (ccTime) t
+{
+       [target_ setSkewX: (startSkewX_ + deltaX_ * t ) ];
+       [target_ setSkewY: (startSkewY_ + deltaY_ * t ) ];
+}
+
+@end
+
+//
+// CCSkewBy
+//
+@implementation CCSkewBy
+
+-(id) initWithDuration:(ccTime)t skewX:(float)deltaSkewX skewY:(float)deltaSkewY
+{
+       if( (self=[super initWithDuration:t skewX:deltaSkewX skewY:deltaSkewY]) ) {     
+               skewX_ = deltaSkewX;
+               skewY_ = deltaSkewY;
+       }
+       return self;
+}
+
+-(void) startWithTarget:(CCNode *)aTarget
+{
+       [super startWithTarget:aTarget];
+       deltaX_ = skewX_;
+       deltaY_ = skewY_;
+       endSkewX_ = startSkewX_ + deltaX_;
+       endSkewY_ = startSkewY_ + deltaY_;
+}
+
+-(CCActionInterval*) reverse
+{
+       return [[self class] actionWithDuration:duration_ skewX:-skewX_ skewY:-skewY_];
+}
+@end
+
+
+//
+// JumpBy
+//
+#pragma mark -
+#pragma mark JumpBy
+
+@implementation CCJumpBy
++(id) actionWithDuration: (ccTime) t position: (CGPoint) pos height: (ccTime) h jumps:(NSUInteger)j
+{
+       return [[[self alloc] initWithDuration: t position: pos height: h jumps:j] autorelease];
+}
+
+-(id) initWithDuration: (ccTime) t position: (CGPoint) pos height: (ccTime) h jumps:(NSUInteger)j
+{
+       if( (self=[super initWithDuration:t]) ) {
+               delta_ = pos;
+               height_ = h;
+               jumps_ = j;
+       }
+       return self;
+}
+
+-(id) copyWithZone: (NSZone*) zone
+{
+       CCAction *copy = [[[self class] allocWithZone: zone] initWithDuration:[self duration] position:delta_ height:height_ jumps:jumps_];
+       return copy;
+}
+
+-(void) startWithTarget:(id)aTarget
+{
+       [super startWithTarget:aTarget];
+       startPosition_ = [(CCNode*)target_ position];
+}
+
+-(void) update: (ccTime) t
+{
+       // Sin jump. Less realistic
+//     ccTime y = height * fabsf( sinf(t * (CGFloat)M_PI * jumps ) );
+//     y += delta.y * t;
+//     ccTime x = delta.x * t;
+//     [target setPosition: ccp( startPosition.x + x, startPosition.y + y )];  
+       
+       // parabolic jump (since v0.8.2)
+       ccTime frac = fmodf( t * jumps_, 1.0f );
+       ccTime y = height_ * 4 * frac * (1 - frac);
+       y += delta_.y * t;
+       ccTime x = delta_.x * t;
+       [target_ setPosition: ccp( startPosition_.x + x, startPosition_.y + y )];
+       
+}
+
+-(CCActionInterval*) reverse
+{
+       return [[self class] actionWithDuration:duration_ position: ccp(-delta_.x,-delta_.y) height:height_ jumps:jumps_];
+}
+@end
+
+//
+// JumpTo
+//
+#pragma mark -
+#pragma mark JumpTo
+
+@implementation CCJumpTo
+-(void) startWithTarget:(CCNode *)aTarget
+{
+       [super startWithTarget:aTarget];
+       delta_ = ccp( delta_.x - startPosition_.x, delta_.y - startPosition_.y );
+}
+@end
+
+
+#pragma mark -
+#pragma mark BezierBy
+
+// Bezier cubic formula:
+//     ((1 - t) + t)3 = 1 
+// Expands to… 
+//   (1 - t)3 + 3t(1-t)2 + 3t2(1 - t) + t3 = 1 
+static inline float bezierat( float a, float b, float c, float d, ccTime t )
+{
+       return (powf(1-t,3) * a + 
+                       3*t*(powf(1-t,2))*b + 
+                       3*powf(t,2)*(1-t)*c +
+                       powf(t,3)*d );
+}
+
+//
+// BezierBy
+//
+@implementation CCBezierBy
++(id) actionWithDuration: (ccTime) t bezier:(ccBezierConfig) c
+{      
+       return [[[self alloc] initWithDuration:t bezier:c ] autorelease];
+}
+
+-(id) initWithDuration: (ccTime) t bezier:(ccBezierConfig) c
+{
+       if( (self=[super initWithDuration: t]) ) {
+               config_ = c;
+       }
+       return self;
+}
+
+-(id) copyWithZone: (NSZone*) zone
+{
+       CCAction *copy = [[[self class] allocWithZone: zone] initWithDuration:[self duration] bezier:config_];
+    return copy;
+}
+
+-(void) startWithTarget:(id)aTarget
+{
+       [super startWithTarget:aTarget];
+       startPosition_ = [(CCNode*)target_ position];
+}
+
+-(void) update: (ccTime) t
+{
+       float xa = 0;
+       float xb = config_.controlPoint_1.x;
+       float xc = config_.controlPoint_2.x;
+       float xd = config_.endPosition.x;
+       
+       float ya = 0;
+       float yb = config_.controlPoint_1.y;
+       float yc = config_.controlPoint_2.y;
+       float yd = config_.endPosition.y;
+       
+       float x = bezierat(xa, xb, xc, xd, t);
+       float y = bezierat(ya, yb, yc, yd, t);
+       [target_ setPosition:  ccpAdd( startPosition_, ccp(x,y))];
+}
+
+- (CCActionInterval*) reverse
+{
+       ccBezierConfig r;
+
+       r.endPosition    = ccpNeg(config_.endPosition);
+       r.controlPoint_1 = ccpAdd(config_.controlPoint_2, ccpNeg(config_.endPosition));
+       r.controlPoint_2 = ccpAdd(config_.controlPoint_1, ccpNeg(config_.endPosition));
+       
+       CCBezierBy *action = [[self class] actionWithDuration:[self duration] bezier:r];
+       return action;
+}
+@end
+
+//
+// BezierTo
+//
+#pragma mark -
+#pragma mark BezierTo
+@implementation CCBezierTo
+-(void) startWithTarget:(id)aTarget
+{
+       [super startWithTarget:aTarget];
+       config_.controlPoint_1 = ccpSub(config_.controlPoint_1, startPosition_);
+       config_.controlPoint_2 = ccpSub(config_.controlPoint_2, startPosition_);
+       config_.endPosition = ccpSub(config_.endPosition, startPosition_);
+}
+@end
+
+
+//
+// ScaleTo
+//
+#pragma mark -
+#pragma mark ScaleTo
+@implementation CCScaleTo
++(id) actionWithDuration: (ccTime) t scale:(float) s
+{
+       return [[[self alloc] initWithDuration: t scale:s] autorelease];
+}
+
+-(id) initWithDuration: (ccTime) t scale:(float) s
+{
+       if( (self=[super initWithDuration: t]) ) {
+               endScaleX_ = s;
+               endScaleY_ = s;
+       }
+       return self;
+}
+
++(id) actionWithDuration: (ccTime) t scaleX:(float)sx scaleY:(float)sy 
+{
+       return [[[self alloc] initWithDuration: t scaleX:sx scaleY:sy] autorelease];
+}
+
+-(id) initWithDuration: (ccTime) t scaleX:(float)sx scaleY:(float)sy
+{
+       if( (self=[super initWithDuration: t]) ) {      
+               endScaleX_ = sx;
+               endScaleY_ = sy;
+       }
+       return self;
+}
+
+-(id) copyWithZone: (NSZone*) zone
+{
+       CCAction *copy = [[[self class] allocWithZone: zone] initWithDuration:[self duration] scaleX:endScaleX_ scaleY:endScaleY_];
+       return copy;
+}
+
+-(void) startWithTarget:(CCNode *)aTarget
+{
+       [super startWithTarget:aTarget];
+       startScaleX_ = [target_ scaleX];
+       startScaleY_ = [target_ scaleY];
+       deltaX_ = endScaleX_ - startScaleX_;
+       deltaY_ = endScaleY_ - startScaleY_;
+}
+
+-(void) update: (ccTime) t
+{
+       [target_ setScaleX: (startScaleX_ + deltaX_ * t ) ];
+       [target_ setScaleY: (startScaleY_ + deltaY_ * t ) ];
+}
+@end
+
+//
+// ScaleBy
+//
+#pragma mark -
+#pragma mark ScaleBy
+@implementation CCScaleBy
+-(void) startWithTarget:(CCNode *)aTarget
+{
+       [super startWithTarget:aTarget];
+       deltaX_ = startScaleX_ * endScaleX_ - startScaleX_;
+       deltaY_ = startScaleY_ * endScaleY_ - startScaleY_;
+}
+
+-(CCActionInterval*) reverse
+{
+       return [[self class] actionWithDuration:duration_ scaleX:1/endScaleX_ scaleY:1/endScaleY_];
+}
+@end
+
+//
+// Blink
+//
+#pragma mark -
+#pragma mark Blink
+@implementation CCBlink
++(id) actionWithDuration: (ccTime) t blinks: (NSUInteger) b
+{
+       return [[[ self alloc] initWithDuration: t blinks: b] autorelease];
+}
+
+-(id) initWithDuration: (ccTime) t blinks: (NSUInteger) b
+{
+       if( (self=[super initWithDuration: t] ) )
+               times_ = b;
+       
+       return self;
+}
+
+-(id) copyWithZone: (NSZone*) zone
+{
+       CCAction *copy = [[[self class] allocWithZone: zone] initWithDuration: [self duration] blinks: times_];
+       return copy;
+}
+
+-(void) update: (ccTime) t
+{
+       if( ! [self isDone] ) {
+               ccTime slice = 1.0f / times_;
+               ccTime m = fmodf(t, slice);
+               [target_ setVisible: (m > slice/2) ? YES : NO];
+       }
+}
+
+-(CCActionInterval*) reverse
+{
+       // return 'self'
+       return [[self class] actionWithDuration:duration_ blinks: times_];
+}
+@end
+
+//
+// FadeIn
+//
+#pragma mark -
+#pragma mark FadeIn
+@implementation CCFadeIn
+-(void) update: (ccTime) t
+{
+       [(id<CCRGBAProtocol>) target_ setOpacity: 255 *t];
+}
+
+-(CCActionInterval*) reverse
+{
+       return [CCFadeOut actionWithDuration:duration_];
+}
+@end
+
+//
+// FadeOut
+//
+#pragma mark -
+#pragma mark FadeOut
+@implementation CCFadeOut
+-(void) update: (ccTime) t
+{
+       [(id<CCRGBAProtocol>) target_ setOpacity: 255 *(1-t)];
+}
+
+-(CCActionInterval*) reverse
+{
+       return [CCFadeIn actionWithDuration:duration_];
+}
+@end
+
+//
+// FadeTo
+//
+#pragma mark -
+#pragma mark FadeTo
+@implementation CCFadeTo
++(id) actionWithDuration: (ccTime) t opacity: (GLubyte) o
+{
+       return [[[ self alloc] initWithDuration: t opacity: o] autorelease];
+}
+
+-(id) initWithDuration: (ccTime) t opacity: (GLubyte) o
+{
+       if( (self=[super initWithDuration: t] ) )
+               toOpacity_ = o;
+       
+       return self;
+}
+
+-(id) copyWithZone: (NSZone*) zone
+{
+       CCAction *copy = [[[self class] allocWithZone: zone] initWithDuration:[self duration] opacity:toOpacity_];
+       return copy;
+}
+
+-(void) startWithTarget:(CCNode *)aTarget
+{
+       [super startWithTarget:aTarget];
+       fromOpacity_ = [(id<CCRGBAProtocol>)target_ opacity];
+}
+
+-(void) update: (ccTime) t
+{
+       [(id<CCRGBAProtocol>)target_ setOpacity:fromOpacity_ + ( toOpacity_ - fromOpacity_ ) * t];
+}
+@end
+
+//
+// TintTo
+//
+#pragma mark -
+#pragma mark TintTo
+@implementation CCTintTo
++(id) actionWithDuration:(ccTime)t red:(GLubyte)r green:(GLubyte)g blue:(GLubyte)b
+{
+       return [[(CCTintTo*)[ self alloc] initWithDuration:t red:r green:g blue:b] autorelease];
+}
+
+-(id) initWithDuration: (ccTime) t red:(GLubyte)r green:(GLubyte)g blue:(GLubyte)b
+{
+       if( (self=[super initWithDuration:t] ) )
+               to_ = ccc3(r,g,b);
+       
+       return self;
+}
+
+-(id) copyWithZone: (NSZone*) zone
+{
+       CCAction *copy = [(CCTintTo*)[[self class] allocWithZone: zone] initWithDuration:[self duration] red:to_.r green:to_.g blue:to_.b];
+       return copy;
+}
+
+-(void) startWithTarget:(id)aTarget
+{
+       [super startWithTarget:aTarget];
+       
+       id<CCRGBAProtocol> tn = (id<CCRGBAProtocol>) target_;
+       from_ = [tn color];
+}
+
+-(void) update: (ccTime) t
+{
+       id<CCRGBAProtocol> tn = (id<CCRGBAProtocol>) target_;
+       [tn setColor:ccc3(from_.r + (to_.r - from_.r) * t, from_.g + (to_.g - from_.g) * t, from_.b + (to_.b - from_.b) * t)];
+}
+@end
+
+//
+// TintBy
+//
+#pragma mark -
+#pragma mark TintBy
+@implementation CCTintBy
++(id) actionWithDuration:(ccTime)t red:(GLshort)r green:(GLshort)g blue:(GLshort)b
+{
+       return [[(CCTintBy*)[ self alloc] initWithDuration:t red:r green:g blue:b] autorelease];
+}
+
+-(id) initWithDuration:(ccTime)t red:(GLshort)r green:(GLshort)g blue:(GLshort)b
+{
+       if( (self=[super initWithDuration: t] ) ) {
+               deltaR_ = r;
+               deltaG_ = g;
+               deltaB_ = b;
+       }
+       return self;
+}
+
+-(id) copyWithZone: (NSZone*) zone
+{
+       return[(CCTintBy*)[[self class] allocWithZone: zone] initWithDuration: [self duration] red:deltaR_ green:deltaG_ blue:deltaB_];
+}
+
+-(void) startWithTarget:(id)aTarget
+{
+       [super startWithTarget:aTarget];
+       
+       id<CCRGBAProtocol> tn = (id<CCRGBAProtocol>) target_;
+       ccColor3B color = [tn color];
+       fromR_ = color.r;
+       fromG_ = color.g;
+       fromB_ = color.b;
+}
+
+-(void) update: (ccTime) t
+{
+       id<CCRGBAProtocol> tn = (id<CCRGBAProtocol>) target_;
+       [tn setColor:ccc3( fromR_ + deltaR_ * t, fromG_ + deltaG_ * t, fromB_ + deltaB_ * t)];
+}
+
+- (CCActionInterval*) reverse
+{
+       return [CCTintBy actionWithDuration:duration_ red:-deltaR_ green:-deltaG_ blue:-deltaB_];
+}
+@end
+
+//
+// DelayTime
+//
+#pragma mark -
+#pragma mark DelayTime
+@implementation CCDelayTime
+-(void) update: (ccTime) t
+{
+       return;
+}
+
+-(id)reverse
+{
+       return [[self class] actionWithDuration:duration_];
+}
+@end
+
+//
+// ReverseTime
+//
+#pragma mark -
+#pragma mark ReverseTime
+@implementation CCReverseTime
++(id) actionWithAction: (CCFiniteTimeAction*) action
+{
+       // casting to prevent warnings
+       CCReverseTime *a = [super alloc];
+       return [[a initWithAction:action] autorelease];
+}
+
+-(id) initWithAction: (CCFiniteTimeAction*) action
+{
+       NSAssert(action != nil, @"CCReverseTime: action should not be nil");
+       NSAssert(action != other_, @"CCReverseTime: re-init doesn't support using the same arguments");
+
+       if( (self=[super initWithDuration: [action duration]]) ) {
+               // Don't leak if action is reused
+               [other_ release];
+               other_ = [action retain];
+       }
+       
+       return self;
+}
+
+-(id) copyWithZone: (NSZone*) zone
+{
+       return [[[self class] allocWithZone: zone] initWithAction:[[other_ copy] autorelease] ];
+}
+
+-(void) dealloc
+{
+       [other_ release];
+       [super dealloc];
+}
+
+-(void) startWithTarget:(id)aTarget
+{
+       [super startWithTarget:aTarget];
+       [other_ startWithTarget:target_];
+}
+
+-(void) stop
+{
+       [other_ stop];
+       [super stop];
+}
+
+-(void) update:(ccTime)t
+{
+       [other_ update:1-t];
+}
+
+-(CCActionInterval*) reverse
+{
+       return [[other_ copy] autorelease];
+}
+@end
+
+//
+// Animate
+//
+
+#pragma mark -
+#pragma mark Animate
+@implementation CCAnimate
+
+@synthesize animation = animation_;
+
++(id) actionWithAnimation: (CCAnimation*)anim
+{
+       return [[[self alloc] initWithAnimation:anim restoreOriginalFrame:YES] autorelease];
+}
+
++(id) actionWithAnimation: (CCAnimation*)anim restoreOriginalFrame:(BOOL)b
+{
+       return [[[self alloc] initWithAnimation:anim restoreOriginalFrame:b] autorelease];
+}
+
++(id) actionWithDuration:(ccTime)duration animation: (CCAnimation*)anim restoreOriginalFrame:(BOOL)b
+{
+       return [[[self alloc] initWithDuration:duration animation:anim restoreOriginalFrame:b] autorelease];
+}
+
+-(id) initWithAnimation: (CCAnimation*)anim
+{
+       NSAssert( anim!=nil, @"Animate: argument Animation must be non-nil");
+       return [self initWithAnimation:anim restoreOriginalFrame:YES];
+}
+
+-(id) initWithAnimation: (CCAnimation*)anim restoreOriginalFrame:(BOOL) b
+{
+       NSAssert( anim!=nil, @"Animate: argument Animation must be non-nil");
+
+       if( (self=[super initWithDuration: [[anim frames] count] * [anim delay]]) ) {
+
+               restoreOriginalFrame_ = b;
+               self.animation = anim;
+               origFrame_ = nil;
+       }
+       return self;
+}
+
+-(id) initWithDuration:(ccTime)aDuration animation: (CCAnimation*)anim restoreOriginalFrame:(BOOL) b
+{
+       NSAssert( anim!=nil, @"Animate: argument Animation must be non-nil");
+       
+       if( (self=[super initWithDuration:aDuration] ) ) {
+               
+               restoreOriginalFrame_ = b;
+               self.animation = anim;
+               origFrame_ = nil;
+       }
+       return self;
+}
+
+
+-(id) copyWithZone: (NSZone*) zone
+{
+       return [[[self class] allocWithZone: zone] initWithDuration:duration_ animation:animation_ restoreOriginalFrame:restoreOriginalFrame_];
+}
+
+-(void) dealloc
+{
+       [animation_ release];
+       [origFrame_ release];
+       [super dealloc];
+}
+
+-(void) startWithTarget:(id)aTarget
+{
+       [super startWithTarget:aTarget];
+       CCSprite *sprite = target_;
+
+       [origFrame_ release];
+
+       if( restoreOriginalFrame_ )
+               origFrame_ = [[sprite displayedFrame] retain];
+}
+
+-(void) stop
+{
+       if( restoreOriginalFrame_ ) {
+               CCSprite *sprite = target_;
+               [sprite setDisplayFrame:origFrame_];
+       }
+       
+       [super stop];
+}
+
+-(void) update: (ccTime) t
+{
+       NSArray *frames = [animation_ frames];
+       NSUInteger numberOfFrames = [frames count];
+       
+       NSUInteger idx = t * numberOfFrames;
+
+       if( idx >= numberOfFrames )
+               idx = numberOfFrames -1;
+       
+       CCSprite *sprite = target_;
+       if (! [sprite isFrameDisplayed: [frames objectAtIndex: idx]] )
+               [sprite setDisplayFrame: [frames objectAtIndex:idx]];
+}
+
+- (CCActionInterval *) reverse
+{
+       NSArray *oldArray = [animation_ frames];
+       NSMutableArray *newArray = [NSMutableArray arrayWithCapacity:[oldArray count]];
+    NSEnumerator *enumerator = [oldArray reverseObjectEnumerator];
+    for (id element in enumerator)
+        [newArray addObject:[[element copy] autorelease]];
+       
+       CCAnimation *newAnim = [CCAnimation animationWithFrames:newArray delay:animation_.delay];
+       return [[self class] actionWithDuration:duration_ animation:newAnim restoreOriginalFrame:restoreOriginalFrame_];
+}
+
+@end
diff --git a/libs/cocos2d/CCActionManager.h b/libs/cocos2d/CCActionManager.h
new file mode 100644 (file)
index 0000000..0eeda91
--- /dev/null
@@ -0,0 +1,111 @@
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2009 Valentin Milea
+ *
+ * Copyright (c) 2008-2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+
+#import "CCAction.h"
+#import "Support/ccCArray.h"
+#import "Support/uthash.h"
+
+typedef struct _hashElement
+{
+       struct ccArray  *actions;
+       id                              target;
+       NSUInteger              actionIndex;
+       CCAction                *currentAction;
+       BOOL                    currentActionSalvaged;
+       BOOL                    paused; 
+       UT_hash_handle  hh;
+} tHashElement;
+
+
+/** CCActionManager is a singleton that manages all the actions.
+ Normally you won't need to use this singleton directly. 99% of the cases you will use the CCNode interface,
+ which uses this singleton.
+ But there are some cases where you might need to use this singleton.
+ Examples:
+       - When you want to run an action where the target is different from a CCNode. 
+       - When you want to pause / resume the actions
+ @since v0.8
+ */
+@interface CCActionManager : NSObject
+{
+       tHashElement    *targets;
+       tHashElement    *currentTarget;
+       BOOL                    currentTargetSalvaged;
+}
+
+/** returns a shared instance of the CCActionManager */
++ (CCActionManager *)sharedManager;
+
+/** purges the shared action manager. It releases the retained instance.
+ @since v0.99.0
+ */
++(void)purgeSharedManager;
+
+// actions
+
+/** Adds an action with a target.
+ If the target is already present, then the action will be added to the existing target.
+ If the target is not present, a new instance of this target will be created either paused or paused, and the action will be added to the newly created target.
+ When the target is paused, the queued actions won't be 'ticked'.
+ */
+-(void) addAction: (CCAction*) action target:(id)target paused:(BOOL)paused;
+/** Removes all actions from all the targers.
+ */
+-(void) removeAllActions;
+
+/** Removes all actions from a certain target.
+ All the actions that belongs to the target will be removed.
+ */
+-(void) removeAllActionsFromTarget:(id)target;
+/** Removes an action given an action reference.
+ */
+-(void) removeAction: (CCAction*) action;
+/** Removes an action given its tag and the target */
+-(void) removeActionByTag:(NSInteger)tag target:(id)target;
+/** Gets an action given its tag an a target
+ @return the Action the with the given tag
+ */
+-(CCAction*) getActionByTag:(NSInteger) tag target:(id)target;
+/** Returns the numbers of actions that are running in a certain target
+ * Composable actions are counted as 1 action. Example:
+ *    If you are running 1 Sequence of 7 actions, it will return 1.
+ *    If you are running 7 Sequences of 2 actions, it will return 7.
+ */
+-(NSUInteger) numberOfRunningActionsInTarget:(id)target;
+
+/** Pauses the target: all running actions and newly added actions will be paused.
+ */
+-(void) pauseTarget:(id)target;
+/** Resumes the target. All queued actions will be resumed.
+ */
+-(void) resumeTarget:(id)target;
+
+@end
+
diff --git a/libs/cocos2d/CCActionManager.m b/libs/cocos2d/CCActionManager.m
new file mode 100644 (file)
index 0000000..9dc5a9e
--- /dev/null
@@ -0,0 +1,346 @@
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2009 Valentin Milea
+ *
+ * Copyright (c) 2008-2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+
+#import "CCActionManager.h"
+#import "CCScheduler.h"
+#import "ccMacros.h"
+
+
+//
+// singleton stuff
+//
+static CCActionManager *sharedManager_ = nil;
+
+@interface CCActionManager (Private)
+-(void) removeActionAtIndex:(NSUInteger)index hashElement:(tHashElement*)element;
+-(void) deleteHashElement:(tHashElement*)element;
+-(void) actionAllocWithHashElement:(tHashElement*)element;
+@end
+
+
+@implementation CCActionManager
+
+#pragma mark ActionManager - init
++ (CCActionManager *)sharedManager
+{
+       if (!sharedManager_)
+               sharedManager_ = [[self alloc] init];
+               
+       return sharedManager_;
+}
+
++(id)alloc
+{
+       NSAssert(sharedManager_ == nil, @"Attempted to allocate a second instance of a singleton.");
+       return [super alloc];
+}
+
++(void)purgeSharedManager
+{
+       [[CCScheduler sharedScheduler] unscheduleUpdateForTarget:self];
+       [sharedManager_ release];
+       sharedManager_ = nil;
+}
+
+-(id) init
+{
+       if ((self=[super init]) ) {
+               [[CCScheduler sharedScheduler] scheduleUpdateForTarget:self priority:0 paused:NO];
+               targets = NULL;
+       }
+       
+       return self;
+}
+
+- (void) dealloc
+{
+       CCLOGINFO( @"cocos2d: deallocing %@", self);
+       
+       [self removeAllActions];
+
+       sharedManager_ = nil;
+
+       [super dealloc];
+}
+
+#pragma mark ActionManager - Private
+
+-(void) deleteHashElement:(tHashElement*)element
+{
+       ccArrayFree(element->actions);
+       HASH_DEL(targets, element);
+//     CCLOG(@"cocos2d: ---- buckets: %d/%d - %@", targets->entries, targets->size, element->target);
+       [element->target release];
+       free(element);
+}
+
+-(void) actionAllocWithHashElement:(tHashElement*)element
+{
+       // 4 actions per Node by default
+       if( element->actions == nil )
+               element->actions = ccArrayNew(4);
+       else if( element->actions->num == element->actions->max )
+               ccArrayDoubleCapacity(element->actions);        
+}
+
+-(void) removeActionAtIndex:(NSUInteger)index hashElement:(tHashElement*)element
+{      
+       id action = element->actions->arr[index];
+
+       if( action == element->currentAction && !element->currentActionSalvaged ) {
+               [element->currentAction retain];
+               element->currentActionSalvaged = YES;
+       }
+       
+       ccArrayRemoveObjectAtIndex(element->actions, index);
+
+       // update actionIndex in case we are in tick:, looping over the actions
+       if( element->actionIndex >= index )
+               element->actionIndex--;
+       
+       if( element->actions->num == 0 ) {
+               if( currentTarget == element )
+                       currentTargetSalvaged = YES;
+               else
+                       [self deleteHashElement: element];
+       }
+}
+
+#pragma mark ActionManager - Pause / Resume
+
+-(void) pauseTarget:(id)target
+{
+       tHashElement *element = NULL;
+       HASH_FIND_INT(targets, &target, element);
+       if( element )
+               element->paused = YES;
+//     else
+//             CCLOG(@"cocos2d: pauseAllActions: Target not found");
+}
+
+-(void) resumeTarget:(id)target
+{
+       tHashElement *element = NULL;
+       HASH_FIND_INT(targets, &target, element);
+       if( element )
+               element->paused = NO;
+//     else
+//             CCLOG(@"cocos2d: resumeAllActions: Target not found");
+}
+
+#pragma mark ActionManager - run
+
+-(void) addAction:(CCAction*)action target:(id)target paused:(BOOL)paused
+{
+       NSAssert( action != nil, @"Argument action must be non-nil");
+       NSAssert( target != nil, @"Argument target must be non-nil");   
+       
+       tHashElement *element = NULL;
+       HASH_FIND_INT(targets, &target, element);
+       if( ! element ) {
+               element = calloc( sizeof( *element ), 1 );
+               element->paused = paused;
+               element->target = [target retain];
+               HASH_ADD_INT(targets, target, element);
+//             CCLOG(@"cocos2d: ---- buckets: %d/%d - %@", targets->entries, targets->size, element->target);
+
+       }
+       
+       [self actionAllocWithHashElement:element];
+
+       NSAssert( !ccArrayContainsObject(element->actions, action), @"runAction: Action already running");      
+       ccArrayAppendObject(element->actions, action);
+       
+       [action startWithTarget:target];
+}
+
+#pragma mark ActionManager - remove
+
+-(void) removeAllActions
+{
+       for(tHashElement *element=targets; element != NULL; ) { 
+               id target = element->target;
+               element = element->hh.next;
+               [self removeAllActionsFromTarget:target];
+       }
+}
+-(void) removeAllActionsFromTarget:(id)target
+{
+       // explicit nil handling
+       if( target == nil )
+               return;
+       
+       tHashElement *element = NULL;
+       HASH_FIND_INT(targets, &target, element);
+       if( element ) {
+               if( ccArrayContainsObject(element->actions, element->currentAction) && !element->currentActionSalvaged ) {
+                       [element->currentAction retain];
+                       element->currentActionSalvaged = YES;
+               }
+               ccArrayRemoveAllObjects(element->actions);
+               if( currentTarget == element )
+                       currentTargetSalvaged = YES;
+               else
+                       [self deleteHashElement:element];
+       }
+//     else {
+//             CCLOG(@"cocos2d: removeAllActionsFromTarget: Target not found");
+//     }
+}
+
+-(void) removeAction: (CCAction*) action
+{
+       // explicit nil handling
+       if (action == nil)
+               return;
+       
+       tHashElement *element = NULL;
+       id target = [action originalTarget];
+       HASH_FIND_INT(targets, &target, element );
+       if( element ) {
+               NSUInteger i = ccArrayGetIndexOfObject(element->actions, action);
+               if( i != NSNotFound )
+                       [self removeActionAtIndex:i hashElement:element];
+       }
+//     else {
+//             CCLOG(@"cocos2d: removeAction: Target not found");
+//     }
+}
+
+-(void) removeActionByTag:(NSInteger)aTag target:(id)target
+{
+       NSAssert( aTag != kCCActionTagInvalid, @"Invalid tag");
+       NSAssert( target != nil, @"Target should be ! nil");
+       
+       tHashElement *element = NULL;
+       HASH_FIND_INT(targets, &target, element);
+       
+       if( element ) {
+               NSUInteger limit = element->actions->num;
+               for( NSUInteger i = 0; i < limit; i++) {
+                       CCAction *a = element->actions->arr[i];
+                       
+                       if( a.tag == aTag && [a originalTarget]==target)
+                               return [self removeActionAtIndex:i hashElement:element];
+               }
+//             CCLOG(@"cocos2d: removeActionByTag: Action not found!");
+       }
+//     else {
+//             CCLOG(@"cocos2d: removeActionByTag: Target not found!");
+//     }
+}
+
+#pragma mark ActionManager - get
+
+-(CCAction*) getActionByTag:(NSInteger)aTag target:(id)target
+{
+       NSAssert( aTag != kCCActionTagInvalid, @"Invalid tag");
+
+       tHashElement *element = NULL;
+       HASH_FIND_INT(targets, &target, element);
+
+       if( element ) {
+               if( element->actions != nil ) {
+                       NSUInteger limit = element->actions->num;
+                       for( NSUInteger i = 0; i < limit; i++) {
+                               CCAction *a = element->actions->arr[i];
+                       
+                               if( a.tag == aTag )
+                                       return a; 
+                       }
+               }
+//             CCLOG(@"cocos2d: getActionByTag: Action not found");
+       }
+//     else {
+//             CCLOG(@"cocos2d: getActionByTag: Target not found");
+//     }
+       return nil;
+}
+
+-(NSUInteger) numberOfRunningActionsInTarget:(id) target
+{
+       tHashElement *element = NULL;
+       HASH_FIND_INT(targets, &target, element);
+       if( element )
+               return element->actions ? element->actions->num : 0;
+
+//     CCLOG(@"cocos2d: numberOfRunningActionsInTarget: Target not found");
+       return 0;
+}
+
+#pragma mark ActionManager - main loop
+
+-(void) update: (ccTime) dt
+{
+       for(tHashElement *elt = targets; elt != NULL; ) {       
+
+               currentTarget = elt;
+               currentTargetSalvaged = NO;
+               
+               if( ! currentTarget->paused ) {
+                       
+                       // The 'actions' ccArray may change while inside this loop.
+                       for( currentTarget->actionIndex = 0; currentTarget->actionIndex < currentTarget->actions->num; currentTarget->actionIndex++) {
+                               currentTarget->currentAction = currentTarget->actions->arr[currentTarget->actionIndex];
+                               currentTarget->currentActionSalvaged = NO;
+                               
+                               [currentTarget->currentAction step: dt];
+
+                               if( currentTarget->currentActionSalvaged ) {
+                                       // The currentAction told the node to remove it. To prevent the action from
+                                       // accidentally deallocating itself before finishing its step, we retained
+                                       // it. Now that step is done, it's safe to release it.
+                                       [currentTarget->currentAction release];
+
+                               } else if( [currentTarget->currentAction isDone] ) {
+                                       [currentTarget->currentAction stop];
+                                       
+                                       CCAction *a = currentTarget->currentAction;
+                                       // Make currentAction nil to prevent removeAction from salvaging it.
+                                       currentTarget->currentAction = nil;
+                                       [self removeAction:a];
+                               }
+                               
+                               currentTarget->currentAction = nil;
+                       }
+               }
+
+               // elt, at this moment, is still valid
+               // so it is safe to ask this here (issue #490)
+               elt = elt->hh.next;
+       
+               // only delete currentTarget if no actions were scheduled during the cycle (issue #481)
+               if( currentTargetSalvaged && currentTarget->actions->num == 0 )
+                       [self deleteHashElement:currentTarget];
+       }
+       
+       // issue #635
+       currentTarget = nil;
+}
+@end
diff --git a/libs/cocos2d/CCActionPageTurn3D.h b/libs/cocos2d/CCActionPageTurn3D.h
new file mode 100644 (file)
index 0000000..39eb31d
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2009 Sindesso Pty Ltd http://www.sindesso.com/
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+
+#import "CCActionGrid3D.h"
+
+/**
+ * This action simulates a page turn from the bottom right hand corner of the screen
+ * It's not much use by itself but is used by the PageTurnTransition.
+ *
+ * Based on an original paper by L Hong et al.
+ * http://www.parc.com/publication/1638/turning-pages-of-3d-electronic-books.html
+ * 
+ * @since v0.8.2
+ */
+@interface CCPageTurn3D : CCGrid3DAction
+{
+}
+
+@end
diff --git a/libs/cocos2d/CCActionPageTurn3D.m b/libs/cocos2d/CCActionPageTurn3D.m
new file mode 100644 (file)
index 0000000..ee59500
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2009 Sindesso Pty Ltd http://www.sindesso.com/
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+#import "CCActionPageTurn3D.h"
+
+@implementation CCPageTurn3D
+
+/*
+ * Update each tick
+ * Time is the percentage of the way through the duration
+ */
+-(void)update:(ccTime)time
+{
+       float tt = MAX( 0, time - 0.25f );
+       float deltaAy = ( tt * tt * 500);
+       float ay = -100 - deltaAy;
+       
+       float deltaTheta = - (float) M_PI_2 * sqrtf( time) ;
+       float theta = /*0.01f*/ + (float) M_PI_2 +deltaTheta;
+       
+       float sinTheta = sinf(theta);
+       float cosTheta = cosf(theta);
+       
+       for( int i = 0; i <=gridSize_.x; i++ )
+       {
+               for( int j = 0; j <= gridSize_.y; j++ )
+               {
+                       // Get original vertex
+                       ccVertex3F      p = [self originalVertex:ccg(i,j)];
+                       
+                       float R = sqrtf(p.x*p.x + (p.y - ay) * (p.y - ay));
+                       float r = R * sinTheta;
+                       float alpha = asinf( p.x / R );
+                       float beta = alpha / sinTheta;
+                       float cosBeta = cosf( beta );
+                       
+                       // If beta > PI then we've wrapped around the cone
+                       // Reduce the radius to stop these points interfering with others
+                       if( beta <= M_PI)
+                               p.x = ( r * sinf(beta));
+                       else
+                       {
+                               // Force X = 0 to stop wrapped
+                               // points
+                               p.x = 0;
+                       }
+                       
+                       p.y = ( R + ay - ( r*(1 - cosBeta)*sinTheta));
+                       
+                       // We scale z here to avoid the animation being
+                       // too much bigger than the screen due to perspectve transform
+                       p.z = (r * ( 1 - cosBeta ) * cosTheta) / 7; // "100" didn't work for
+                               
+                       // Stop z coord from dropping beneath underlying page in a transition
+                       // issue #751                           
+                       if( p.z<0.5f )
+                               p.z = 0.5f;
+                       
+                       // Set new coords
+                       [self setVertex:ccg(i,j) vertex:p];
+               }
+       }
+}
+@end
diff --git a/libs/cocos2d/CCActionProgressTimer.h b/libs/cocos2d/CCActionProgressTimer.h
new file mode 100644 (file)
index 0000000..500631b
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (C) 2010 Lam Pham
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+
+#import <Foundation/Foundation.h>
+#import "CCProgressTimer.h"
+#import "CCActionInterval.h"
+
+/**
+ Progress to percentage
+@since v0.99.1
+*/
+@interface CCProgressTo : CCActionInterval <NSCopying>
+{
+       float to_;
+       float from_;
+}
+/** Creates and initializes with a duration and a percent */
++(id) actionWithDuration:(ccTime)duration percent:(float)percent;
+/** Initializes with a duration and a percent */
+-(id) initWithDuration:(ccTime)duration percent:(float)percent;
+@end
+
+/**
+ Progress from a percentage to another percentage
+ @since v0.99.1
+ */
+@interface CCProgressFromTo : CCActionInterval <NSCopying>
+{
+       float to_;
+       float from_;
+}
+/** Creates and initializes the action with a duration, a "from" percentage and a "to" percentage */
++(id) actionWithDuration:(ccTime)duration from:(float)fromPercentage to:(float) toPercentage;
+/** Initializes the action with a duration, a "from" percentage and a "to" percentage */
+-(id) initWithDuration:(ccTime)duration from:(float)fromPercentage to:(float) toPercentage;
+@end
diff --git a/libs/cocos2d/CCActionProgressTimer.m b/libs/cocos2d/CCActionProgressTimer.m
new file mode 100644 (file)
index 0000000..c242570
--- /dev/null
@@ -0,0 +1,103 @@
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (C) 2010 Lam Pham
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+
+#import "CCActionProgressTimer.h"
+
+#define kProgressTimerCast CCProgressTimer*
+
+@implementation CCProgressTo
++(id) actionWithDuration: (ccTime) t percent: (float) v
+{
+       return [[[ self alloc] initWithDuration: t percent: v] autorelease];
+}
+
+-(id) initWithDuration: (ccTime) t percent: (float) v
+{
+       if( (self=[super initWithDuration: t] ) )
+               to_ = v;
+       
+       return self;
+}
+
+-(id) copyWithZone: (NSZone*) zone
+{
+       CCAction *copy = [[[self class] allocWithZone: zone] initWithDuration:duration_ percent:to_];
+       return copy;
+}
+
+-(void) startWithTarget:(id) aTarget;
+{
+       [super startWithTarget:aTarget];
+       from_ = [(kProgressTimerCast)target_ percentage];
+       
+       // XXX: Is this correct ?
+       // Adding it to support CCRepeat
+       if( from_ == 100)
+               from_ = 0;
+}
+
+-(void) update: (ccTime) t
+{
+       [(kProgressTimerCast)target_ setPercentage: from_ + ( to_ - from_ ) * t];
+}
+@end
+
+@implementation CCProgressFromTo
++(id) actionWithDuration: (ccTime) t from:(float)fromPercentage to:(float) toPercentage
+{
+       return [[[self alloc] initWithDuration: t from: fromPercentage to: toPercentage] autorelease];
+}
+
+-(id) initWithDuration: (ccTime) t from:(float)fromPercentage to:(float) toPercentage
+{
+       if( (self=[super initWithDuration: t] ) ){
+               to_ = toPercentage;
+               from_ = fromPercentage;
+       }
+       return self;
+}
+
+-(id) copyWithZone: (NSZone*) zone
+{
+       CCAction *copy = [[[self class] allocWithZone: zone] initWithDuration:duration_ from:from_ to:to_];
+       return copy;
+}
+
+- (CCActionInterval *) reverse
+{
+       return [[self class] actionWithDuration:duration_ from:to_ to:from_];
+}
+
+-(void) startWithTarget:(id) aTarget;
+{
+       [super startWithTarget:aTarget];
+}
+
+-(void) update: (ccTime) t
+{
+       [(kProgressTimerCast)target_ setPercentage: from_ + ( to_ - from_ ) * t];
+}
+@end
diff --git a/libs/cocos2d/CCActionTiledGrid.h b/libs/cocos2d/CCActionTiledGrid.h
new file mode 100644 (file)
index 0000000..d66132d
--- /dev/null
@@ -0,0 +1,211 @@
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2009 On-Core
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+#import "CCActionGrid.h"
+
+/** CCShakyTiles3D action */
+@interface CCShakyTiles3D : CCTiledGrid3DAction
+{
+       int             randrange;
+       BOOL    shakeZ;
+}
+
+/** creates the action with a range, whether or not to shake Z vertices, a grid size, and duration */
++(id)actionWithRange:(int)range shakeZ:(BOOL)shakeZ grid:(ccGridSize)gridSize duration:(ccTime)d;
+/** initializes the action with a range, whether or not to shake Z vertices, a grid size, and duration */
+-(id)initWithRange:(int)range shakeZ:(BOOL)shakeZ grid:(ccGridSize)gridSize duration:(ccTime)d;
+
+@end
+
+////////////////////////////////////////////////////////////
+
+/** CCShatteredTiles3D action */
+@interface CCShatteredTiles3D : CCTiledGrid3DAction
+{
+       int             randrange;
+       BOOL    once;
+       BOOL    shatterZ;
+}
+
+/** creates the action with a range, whether of not to shatter Z vertices, a grid size and duration */
++(id)actionWithRange:(int)range shatterZ:(BOOL)shatterZ grid:(ccGridSize)gridSize duration:(ccTime)d;
+/** initializes the action with a range, whether or not to shatter Z vertices, a grid size and duration */
+-(id)initWithRange:(int)range shatterZ:(BOOL)shatterZ grid:(ccGridSize)gridSize duration:(ccTime)d;
+
+@end
+
+////////////////////////////////////////////////////////////
+
+/** CCShuffleTiles action
+ Shuffle the tiles in random order
+ */
+@interface CCShuffleTiles : CCTiledGrid3DAction
+{
+       int     seed;
+       NSUInteger tilesCount;
+       int *tilesOrder;
+       void *tiles;
+}
+
+/** creates the action with a random seed, the grid size and the duration */
++(id)actionWithSeed:(int)s grid:(ccGridSize)gridSize duration:(ccTime)d;
+/** initializes the action with a random seed, the grid size and the duration */
+-(id)initWithSeed:(int)s grid:(ccGridSize)gridSize duration:(ccTime)d;
+
+@end
+
+////////////////////////////////////////////////////////////
+
+/** CCFadeOutTRTiles action
+ Fades out the tiles in a Top-Right direction
+ */
+@interface CCFadeOutTRTiles : CCTiledGrid3DAction
+{
+}
+@end
+
+////////////////////////////////////////////////////////////
+
+/** CCFadeOutBLTiles action.
+ Fades out the tiles in a Bottom-Left direction
+ */
+@interface CCFadeOutBLTiles : CCFadeOutTRTiles
+{
+}
+@end
+
+////////////////////////////////////////////////////////////
+
+/** CCFadeOutUpTiles action.
+ Fades out the tiles in upwards direction
+ */
+@interface CCFadeOutUpTiles : CCFadeOutTRTiles
+{
+}
+@end
+
+////////////////////////////////////////////////////////////
+
+/** CCFadeOutDownTiles action.
+ Fades out the tiles in downwards direction
+ */
+@interface CCFadeOutDownTiles : CCFadeOutUpTiles
+{
+}
+@end
+
+////////////////////////////////////////////////////////////
+
+/** CCTurnOffTiles action.
+ Turn off the files in random order
+ */
+@interface CCTurnOffTiles : CCTiledGrid3DAction
+{
+       int     seed;
+       NSUInteger tilesCount;
+       int *tilesOrder;
+}
+
+/** creates the action with a random seed, the grid size and the duration */
++(id)actionWithSeed:(int)s grid:(ccGridSize)gridSize duration:(ccTime)d;
+/** initializes the action with a random seed, the grid size and the duration */
+-(id)initWithSeed:(int)s grid:(ccGridSize)gridSize duration:(ccTime)d;
+@end
+
+////////////////////////////////////////////////////////////
+
+/** CCWavesTiles3D action. */
+@interface CCWavesTiles3D : CCTiledGrid3DAction
+{
+       int waves;
+       float amplitude;
+       float amplitudeRate;
+}
+
+/** waves amplitude */
+@property (nonatomic,readwrite) float amplitude;
+/** waves amplitude rate */
+@property (nonatomic,readwrite) float amplitudeRate;
+
+/** creates the action with a number of waves, the waves amplitude, the grid size and the duration */
++(id)actionWithWaves:(int)wav amplitude:(float)amp grid:(ccGridSize)gridSize duration:(ccTime)d;
+/** initializes the action with a number of waves, the waves amplitude, the grid size and the duration */
+-(id)initWithWaves:(int)wav amplitude:(float)amp grid:(ccGridSize)gridSize duration:(ccTime)d;
+
+@end
+
+////////////////////////////////////////////////////////////
+
+/** CCJumpTiles3D action.
+ A sin function is executed to move the tiles across the Z axis
+ */
+@interface CCJumpTiles3D : CCTiledGrid3DAction
+{
+       int jumps;
+       float amplitude;
+       float amplitudeRate;
+}
+
+/** amplitude of the sin*/
+@property (nonatomic,readwrite) float amplitude;
+/** amplitude rate */
+@property (nonatomic,readwrite) float amplitudeRate;
+
+/** creates the action with the number of jumps, the sin amplitude, the grid size and the duration */
++(id)actionWithJumps:(int)j amplitude:(float)amp grid:(ccGridSize)gridSize duration:(ccTime)d;
+/** initializes the action with the number of jumps, the sin amplitude, the grid size and the duration */
+-(id)initWithJumps:(int)j amplitude:(float)amp grid:(ccGridSize)gridSize duration:(ccTime)d;
+
+@end
+
+////////////////////////////////////////////////////////////
+
+/** CCSplitRows action */
+@interface CCSplitRows : CCTiledGrid3DAction
+{
+       int             rows;
+       CGSize  winSize;
+}
+/** creates the action with the number of rows to split and the duration */
++(id)actionWithRows:(int)rows duration:(ccTime)duration;
+/** initializes the action with the number of rows to split and the duration */
+-(id)initWithRows:(int)rows duration:(ccTime)duration;
+
+@end
+
+////////////////////////////////////////////////////////////
+
+/** CCSplitCols action */
+@interface CCSplitCols : CCTiledGrid3DAction
+{
+       int             cols;
+       CGSize  winSize;
+}
+/** creates the action with the number of columns to split and the duration */
++(id)actionWithCols:(int)cols duration:(ccTime)duration;
+/** initializes the action with the number of columns to split and the duration */
+-(id)initWithCols:(int)cols duration:(ccTime)duration;
+
+@end
diff --git a/libs/cocos2d/CCActionTiledGrid.m b/libs/cocos2d/CCActionTiledGrid.m
new file mode 100644 (file)
index 0000000..75965ec
--- /dev/null
@@ -0,0 +1,768 @@
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2009 On-Core
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+
+#import "CCActionTiledGrid.h"
+#import "CCDirector.h"
+#import "ccMacros.h"
+#import "Support/CGPointExtension.h"
+
+typedef struct
+{
+       CGPoint position;
+       CGPoint startPosition;
+       ccGridSize      delta;
+} Tile;
+
+#pragma mark -
+#pragma mark ShakyTiles3D
+
+@implementation CCShakyTiles3D
+
++(id)actionWithRange:(int)range shakeZ:(BOOL)shakeZ grid:(ccGridSize)gridSize duration:(ccTime)d
+{
+       return [[[self alloc] initWithRange:range shakeZ:shakeZ grid:gridSize duration:d] autorelease];
+}
+
+-(id)initWithRange:(int)range shakeZ:(BOOL)sz grid:(ccGridSize)gSize duration:(ccTime)d
+{
+       if ( (self = [super initWithSize:gSize duration:d]) )
+       {
+               randrange = range;
+               shakeZ = sz;
+       }
+       
+       return self;
+}
+
+-(id) copyWithZone: (NSZone*) zone
+{
+       CCGridAction *copy = [[[self class] allocWithZone:zone] initWithRange:randrange shakeZ:shakeZ grid:gridSize_ duration:duration_];
+       return copy;
+}
+
+
+-(void)update:(ccTime)time
+{
+       int i, j;
+       
+       for( i = 0; i < gridSize_.x; i++ )
+       {
+               for( j = 0; j < gridSize_.y; j++ )
+               {
+                       ccQuad3 coords = [self originalTile:ccg(i,j)];
+
+                       // X
+                       coords.bl.x += ( rand() % (randrange*2) ) - randrange;
+                       coords.br.x += ( rand() % (randrange*2) ) - randrange;
+                       coords.tl.x += ( rand() % (randrange*2) ) - randrange;
+                       coords.tr.x += ( rand() % (randrange*2) ) - randrange;
+
+                       // Y
+                       coords.bl.y += ( rand() % (randrange*2) ) - randrange;
+                       coords.br.y += ( rand() % (randrange*2) ) - randrange;
+                       coords.tl.y += ( rand() % (randrange*2) ) - randrange;
+                       coords.tr.y += ( rand() % (randrange*2) ) - randrange;
+
+                       if( shakeZ ) {
+                               coords.bl.z += ( rand() % (randrange*2) ) - randrange;
+                               coords.br.z += ( rand() % (randrange*2) ) - randrange;
+                               coords.tl.z += ( rand() % (randrange*2) ) - randrange;
+                               coords.tr.z += ( rand() % (randrange*2) ) - randrange;
+                       }
+                                               
+                       [self setTile:ccg(i,j) coords:coords];
+               }
+       }
+}
+
+@end
+
+////////////////////////////////////////////////////////////
+
+#pragma mark -
+#pragma mark CCShatteredTiles3D
+
+@implementation CCShatteredTiles3D
+
++(id)actionWithRange:(int)range shatterZ:(BOOL)sz grid:(ccGridSize)gridSize duration:(ccTime)d
+{
+       return [[[self alloc] initWithRange:range shatterZ:sz grid:gridSize duration:d] autorelease];
+}
+
+-(id)initWithRange:(int)range shatterZ:(BOOL)sz grid:(ccGridSize)gSize duration:(ccTime)d
+{
+       if ( (self = [super initWithSize:gSize duration:d]) )
+       {
+               once = NO;
+               randrange = range;
+               shatterZ = sz;
+       }
+       
+       return self;
+}
+
+-(id) copyWithZone: (NSZone*) zone
+{
+       CCGridAction *copy = [[[self class] allocWithZone:zone] initWithRange:randrange shatterZ:shatterZ grid:gridSize_ duration:duration_];
+       return copy;
+}
+
+
+-(void)update:(ccTime)time
+{
+       int i, j;
+       
+       if ( once == NO )
+       {
+               for( i = 0; i < gridSize_.x; i++ )
+               {
+                       for( j = 0; j < gridSize_.y; j++ )
+                       {
+                               ccQuad3 coords = [self originalTile:ccg(i,j)];
+                               
+                               // X
+                               coords.bl.x += ( rand() % (randrange*2) ) - randrange;
+                               coords.br.x += ( rand() % (randrange*2) ) - randrange;
+                               coords.tl.x += ( rand() % (randrange*2) ) - randrange;
+                               coords.tr.x += ( rand() % (randrange*2) ) - randrange;
+                               
+                               // Y
+                               coords.bl.y += ( rand() % (randrange*2) ) - randrange;
+                               coords.br.y += ( rand() % (randrange*2) ) - randrange;
+                               coords.tl.y += ( rand() % (randrange*2) ) - randrange;
+                               coords.tr.y += ( rand() % (randrange*2) ) - randrange;
+
+                               if( shatterZ ) {
+                                       coords.bl.z += ( rand() % (randrange*2) ) - randrange;
+                                       coords.br.z += ( rand() % (randrange*2) ) - randrange;                          
+                                       coords.tl.z += ( rand() % (randrange*2) ) - randrange;
+                                       coords.tr.z += ( rand() % (randrange*2) ) - randrange;
+                               }
+                               
+                               [self setTile:ccg(i,j) coords:coords];
+                       }
+               }
+               
+               once = YES;
+       }
+}
+
+@end
+
+////////////////////////////////////////////////////////////
+
+#pragma mark -
+#pragma mark CCShuffleTiles
+
+@implementation CCShuffleTiles
+
++(id)actionWithSeed:(int)s grid:(ccGridSize)gridSize duration:(ccTime)d
+{
+       return [[[self alloc] initWithSeed:s grid:gridSize duration:d] autorelease];
+}
+
+-(id)initWithSeed:(int)s grid:(ccGridSize)gSize duration:(ccTime)d
+{
+       if ( (self = [super initWithSize:gSize duration:d]) )
+       {
+               seed = s;
+               tilesOrder = nil;
+               tiles = nil;
+       }
+       
+       return self;
+}
+
+-(id) copyWithZone: (NSZone*) zone
+{
+       CCGridAction *copy = [[[self class] allocWithZone:zone] initWithSeed:seed grid:gridSize_ duration:duration_];
+       return copy;
+}
+
+
+-(void)dealloc
+{
+       if ( tilesOrder ) free(tilesOrder);
+       if ( tiles ) free(tiles);
+       [super dealloc];
+}
+
+-(void)shuffle:(int*)array count:(NSUInteger)len
+{
+       NSInteger i;
+       for( i = len - 1; i >= 0; i-- )
+       {
+               NSInteger j = rand() % (i+1);
+               int v = array[i];
+               array[i] = array[j];
+               array[j] = v;
+       }
+}
+
+-(ccGridSize)getDelta:(ccGridSize)pos
+{
+       CGPoint pos2;
+       
+       NSInteger idx = pos.x * gridSize_.y + pos.y;
+       
+       pos2.x = tilesOrder[idx] / (int)gridSize_.y;
+       pos2.y = tilesOrder[idx] % (int)gridSize_.y;
+       
+       return ccg(pos2.x - pos.x, pos2.y - pos.y);
+}
+
+-(void)placeTile:(ccGridSize)pos tile:(Tile)t
+{
+       ccQuad3 coords = [self originalTile:pos];
+       
+       CGPoint step = [[target_ grid] step];
+       coords.bl.x += (int)(t.position.x * step.x);
+       coords.bl.y += (int)(t.position.y * step.y);
+
+       coords.br.x += (int)(t.position.x * step.x);
+       coords.br.y += (int)(t.position.y * step.y);
+
+       coords.tl.x += (int)(t.position.x * step.x);
+       coords.tl.y += (int)(t.position.y * step.y);
+
+       coords.tr.x += (int)(t.position.x * step.x);
+       coords.tr.y += (int)(t.position.y * step.y);
+
+       [self setTile:pos coords:coords];
+}
+
+-(void)startWithTarget:(id)aTarget
+{
+       [super startWithTarget:aTarget];
+       
+       if ( seed != -1 )
+               srand(seed);
+       
+       tilesCount = gridSize_.x * gridSize_.y;
+       tilesOrder = (int*)malloc(tilesCount*sizeof(int));
+       int i, j;
+       
+       for( i = 0; i < tilesCount; i++ )
+               tilesOrder[i] = i;
+       
+       [self shuffle:tilesOrder count:tilesCount];
+       
+       tiles = malloc(tilesCount*sizeof(Tile));
+       Tile *tileArray = (Tile*)tiles;
+       
+       for( i = 0; i < gridSize_.x; i++ )
+       {
+               for( j = 0; j < gridSize_.y; j++ )
+               {
+                       tileArray->position = ccp(i,j);
+                       tileArray->startPosition = ccp(i,j);
+                       tileArray->delta = [self getDelta:ccg(i,j)];
+                       tileArray++;
+               }
+       }
+}
+
+-(void)update:(ccTime)time
+{
+       int i, j;
+       
+       Tile *tileArray = (Tile*)tiles;
+       
+       for( i = 0; i < gridSize_.x; i++ )
+       {
+               for( j = 0; j < gridSize_.y; j++ )
+               {
+                       tileArray->position = ccpMult( ccp(tileArray->delta.x, tileArray->delta.y), time);
+                       [self placeTile:ccg(i,j) tile:*tileArray];
+                       tileArray++;
+               }
+       }
+}
+
+@end
+
+////////////////////////////////////////////////////////////
+
+#pragma mark -
+#pragma mark CCFadeOutTRTiles
+
+@implementation CCFadeOutTRTiles
+
+-(float)testFunc:(ccGridSize)pos time:(ccTime)time
+{
+       CGPoint n = ccpMult( ccp(gridSize_.x,gridSize_.y), time);
+       if ( (n.x+n.y) == 0.0f )
+               return 1.0f;
+       
+       return powf( (pos.x+pos.y) / (n.x+n.y), 6 );
+}
+
+-(void)turnOnTile:(ccGridSize)pos
+{
+       [self setTile:pos coords:[self originalTile:pos]];
+}
+
+-(void)turnOffTile:(ccGridSize)pos
+{
+       ccQuad3 coords; 
+       bzero(&coords, sizeof(ccQuad3));
+       [self setTile:pos coords:coords];
+}
+
+-(void)transformTile:(ccGridSize)pos distance:(float)distance
+{
+       ccQuad3 coords = [self originalTile:pos];
+       CGPoint step = [[target_ grid] step];
+       
+       coords.bl.x += (step.x / 2) * (1.0f - distance);
+       coords.bl.y += (step.y / 2) * (1.0f - distance);
+
+       coords.br.x -= (step.x / 2) * (1.0f - distance);
+       coords.br.y += (step.y / 2) * (1.0f - distance);
+
+       coords.tl.x += (step.x / 2) * (1.0f - distance);
+       coords.tl.y -= (step.y / 2) * (1.0f - distance);
+
+       coords.tr.x -= (step.x / 2) * (1.0f - distance);
+       coords.tr.y -= (step.y / 2) * (1.0f - distance);
+
+       [self setTile:pos coords:coords];
+}
+
+-(void)update:(ccTime)time
+{
+       int i, j;
+       
+       for( i = 0; i < gridSize_.x; i++ )
+       {
+               for( j = 0; j < gridSize_.y; j++ )
+               {
+                       float distance = [self testFunc:ccg(i,j) time:time];
+                       if ( distance == 0 )
+                               [self turnOffTile:ccg(i,j)];
+                       else if ( distance < 1 )
+                               [self transformTile:ccg(i,j) distance:distance];
+                       else
+                               [self turnOnTile:ccg(i,j)];
+               }
+       }
+}
+
+@end
+
+////////////////////////////////////////////////////////////
+
+#pragma mark -
+#pragma mark CCFadeOutBLTiles
+
+@implementation CCFadeOutBLTiles
+
+-(float)testFunc:(ccGridSize)pos time:(ccTime)time
+{
+       CGPoint n = ccpMult(ccp(gridSize_.x, gridSize_.y), (1.0f-time));
+       if ( (pos.x+pos.y) == 0 )
+               return 1.0f;
+       
+       return powf( (n.x+n.y) / (pos.x+pos.y), 6 );
+}
+
+@end
+
+////////////////////////////////////////////////////////////
+
+#pragma mark -
+#pragma mark CCFadeOutUpTiles
+
+@implementation CCFadeOutUpTiles
+
+-(float)testFunc:(ccGridSize)pos time:(ccTime)time
+{
+       CGPoint n = ccpMult(ccp(gridSize_.x, gridSize_.y), time);
+       if ( n.y == 0 )
+               return 1.0f;
+       
+       return powf( pos.y / n.y, 6 );
+}
+
+-(void)transformTile:(ccGridSize)pos distance:(float)distance
+{
+       ccQuad3 coords = [self originalTile:pos];
+       CGPoint step = [[target_ grid] step];
+       
+       coords.bl.y += (step.y / 2) * (1.0f - distance);
+       coords.br.y += (step.y / 2) * (1.0f - distance);
+       coords.tl.y -= (step.y / 2) * (1.0f - distance);
+       coords.tr.y -= (step.y / 2) * (1.0f - distance);
+       
+       [self setTile:pos coords:coords];
+}
+
+@end
+
+////////////////////////////////////////////////////////////
+
+#pragma mark -
+#pragma mark CCFadeOutDownTiles
+
+@implementation CCFadeOutDownTiles
+
+-(float)testFunc:(ccGridSize)pos time:(ccTime)time
+{
+       CGPoint n = ccpMult(ccp(gridSize_.x,gridSize_.y), (1.0f - time));
+       if ( pos.y == 0 )
+               return 1.0f;
+       
+       return powf( n.y / pos.y, 6 );
+}
+
+@end
+
+////////////////////////////////////////////////////////////
+
+#pragma mark -
+#pragma mark TurnOffTiles
+
+@implementation CCTurnOffTiles
+
++(id)actionWithSeed:(int)s grid:(ccGridSize)gridSize duration:(ccTime)d
+{
+       return [[[self alloc] initWithSeed:s grid:gridSize duration:d] autorelease];
+}
+
+-(id)initWithSeed:(int)s grid:(ccGridSize)gSize duration:(ccTime)d
+{
+       if ( (self = [super initWithSize:gSize duration:d]) )
+       {
+               seed = s;
+               tilesOrder = nil;
+       }
+       
+       return self;
+}
+
+-(id) copyWithZone: (NSZone*) zone
+{
+       CCGridAction *copy = [[[self class] allocWithZone:zone] initWithSeed:seed grid:gridSize_ duration:duration_];
+       return copy;
+}
+
+-(void)dealloc
+{
+       if ( tilesOrder ) free(tilesOrder);
+       [super dealloc];
+}
+
+-(void)shuffle:(int*)array count:(NSUInteger)len
+{
+       NSInteger i;
+       for( i = len - 1; i >= 0; i-- )
+       {
+               NSInteger j = rand() % (i+1);
+               int v = array[i];
+               array[i] = array[j];
+               array[j] = v;
+       }
+}
+
+-(void)turnOnTile:(ccGridSize)pos
+{
+       [self setTile:pos coords:[self originalTile:pos]];
+}
+
+-(void)turnOffTile:(ccGridSize)pos
+{
+       ccQuad3 coords;
+       
+       bzero(&coords, sizeof(ccQuad3));
+       [self setTile:pos coords:coords];
+}
+
+-(void)startWithTarget:(id)aTarget
+{
+       int i;
+       
+       [super startWithTarget:aTarget];
+       
+       if ( seed != -1 )
+               srand(seed);
+       
+       tilesCount = gridSize_.x * gridSize_.y;
+       tilesOrder = (int*)malloc(tilesCount*sizeof(int));
+
+       for( i = 0; i < tilesCount; i++ )
+               tilesOrder[i] = i;
+       
+       [self shuffle:tilesOrder count:tilesCount];
+}
+
+-(void)update:(ccTime)time
+{
+       int i, l, t;
+       
+       l = (int)(time * (float)tilesCount);
+       
+       for( i = 0; i < tilesCount; i++ )
+       {
+               t = tilesOrder[i];
+               ccGridSize tilePos = ccg( t / gridSize_.y, t % gridSize_.y );
+               
+               if ( i < l )
+                       [self turnOffTile:tilePos];
+               else
+                       [self turnOnTile:tilePos];
+       }
+}
+
+@end
+
+////////////////////////////////////////////////////////////
+
+#pragma mark -
+#pragma mark CCWavesTiles3D
+
+@implementation CCWavesTiles3D
+
+@synthesize amplitude;
+@synthesize amplitudeRate;
+
++(id)actionWithWaves:(int)wav amplitude:(float)amp grid:(ccGridSize)gridSize duration:(ccTime)d
+{
+       return [[[self alloc] initWithWaves:wav amplitude:amp grid:gridSize duration:d] autorelease];
+}
+
+-(id)initWithWaves:(int)wav amplitude:(float)amp grid:(ccGridSize)gSize duration:(ccTime)d
+{
+       if ( (self = [super initWithSize:gSize duration:d]) )
+       {
+               waves = wav;
+               amplitude = amp;
+               amplitudeRate = 1.0f;
+       }
+       
+       return self;
+}
+
+-(id) copyWithZone: (NSZone*) zone
+{
+       CCGridAction *copy = [[[self class] allocWithZone:zone] initWithWaves:waves amplitude:amplitude grid:gridSize_ duration:duration_];
+       return copy;
+}
+
+
+-(void)update:(ccTime)time
+{
+       int i, j;
+       
+       for( i = 0; i < gridSize_.x; i++ )
+       {
+               for( j = 0; j < gridSize_.y; j++ )
+               {
+                       ccQuad3 coords = [self originalTile:ccg(i,j)];
+                       
+                       coords.bl.z = (sinf(time*(CGFloat)M_PI*waves*2 + (coords.bl.y+coords.bl.x) * .01f) * amplitude * amplitudeRate );
+                       coords.br.z     = coords.bl.z;
+                       coords.tl.z = coords.bl.z;
+                       coords.tr.z = coords.bl.z;
+                       
+                       [self setTile:ccg(i,j) coords:coords];
+               }
+       }
+}
+@end
+
+////////////////////////////////////////////////////////////
+
+#pragma mark -
+#pragma mark CCJumpTiles3D
+
+@implementation CCJumpTiles3D
+
+@synthesize amplitude;
+@synthesize amplitudeRate;
+
++(id)actionWithJumps:(int)j amplitude:(float)amp grid:(ccGridSize)gridSize duration:(ccTime)d
+{
+       return [[[self alloc] initWithJumps:j amplitude:amp grid:gridSize duration:d] autorelease];
+}
+
+-(id)initWithJumps:(int)j amplitude:(float)amp grid:(ccGridSize)gSize duration:(ccTime)d
+{
+       if ( (self = [super initWithSize:gSize duration:d]) )
+       {
+               jumps = j;
+               amplitude = amp;
+               amplitudeRate = 1.0f;
+       }
+       
+       return self;
+}
+
+-(id) copyWithZone: (NSZone*) zone
+{
+       CCGridAction *copy = [[[self class] allocWithZone:zone] initWithJumps:jumps amplitude:amplitude grid:gridSize_ duration:duration_];
+       return copy;
+}
+
+
+-(void)update:(ccTime)time
+{
+       int i, j;
+       
+       float sinz =  (sinf((CGFloat)M_PI*time*jumps*2) * amplitude * amplitudeRate );
+       float sinz2 = (sinf((CGFloat)M_PI*(time*jumps*2 + 1)) * amplitude * amplitudeRate );
+       
+       for( i = 0; i < gridSize_.x; i++ )
+       {
+               for( j = 0; j < gridSize_.y; j++ )
+               {
+                       ccQuad3 coords = [self originalTile:ccg(i,j)];
+                       
+                       if ( ((i+j) % 2) == 0 )
+                       {
+                               coords.bl.z += sinz;
+                               coords.br.z += sinz;
+                               coords.tl.z += sinz;
+                               coords.tr.z += sinz;
+                       }
+                       else
+                       {
+                               coords.bl.z += sinz2;
+                               coords.br.z += sinz2;
+                               coords.tl.z += sinz2;
+                               coords.tr.z += sinz2;
+                       }
+                       
+                       [self setTile:ccg(i,j) coords:coords];
+               }
+       }
+}
+@end
+
+////////////////////////////////////////////////////////////
+
+#pragma mark -
+#pragma mark SplitRows
+
+@implementation CCSplitRows
+
++(id)actionWithRows:(int)r duration:(ccTime)d
+{
+       return [[[self alloc] initWithRows:r duration:d] autorelease];
+}
+
+-(id)initWithRows:(int)r duration:(ccTime)d
+{
+       rows = r;
+       return [super initWithSize:ccg(1,r) duration:d];
+}
+
+-(id) copyWithZone: (NSZone*) zone
+{
+       CCGridAction *copy = [[[self class] allocWithZone:zone] initWithRows:rows duration:duration_];
+       return copy;
+}
+
+-(void)startWithTarget:(id)aTarget
+{
+       [super startWithTarget:aTarget];
+       winSize = [[CCDirector sharedDirector] winSizeInPixels];
+}
+
+-(void)update:(ccTime)time
+{
+       int j;
+       
+       for( j = 0; j < gridSize_.y; j++ )
+       {
+               ccQuad3 coords = [self originalTile:ccg(0,j)];
+               float   direction = 1;
+               
+               if ( (j % 2 ) == 0 )
+                       direction = -1;
+               
+               coords.bl.x += direction * winSize.width * time;
+               coords.br.x += direction * winSize.width * time;
+               coords.tl.x += direction * winSize.width * time;
+               coords.tr.x += direction * winSize.width * time;
+               
+               [self setTile:ccg(0,j) coords:coords];
+       }
+}
+
+@end
+
+////////////////////////////////////////////////////////////
+
+#pragma mark -
+#pragma mark CCSplitCols
+
+@implementation CCSplitCols
+
++(id)actionWithCols:(int)c duration:(ccTime)d
+{
+       return [[[self alloc] initWithCols:c duration:d] autorelease];
+}
+
+-(id)initWithCols:(int)c duration:(ccTime)d
+{
+       cols = c;
+       return [super initWithSize:ccg(c,1) duration:d];
+}
+
+-(id) copyWithZone: (NSZone*) zone
+{
+       CCGridAction *copy = [[[self class] allocWithZone:zone] initWithCols:cols duration:duration_];
+       return copy;
+}
+
+-(void)startWithTarget:(id)aTarget
+{
+       [super startWithTarget:aTarget];
+       winSize = [[CCDirector sharedDirector] winSizeInPixels];
+}
+
+-(void)update:(ccTime)time
+{
+       int i;
+       
+       for( i = 0; i < gridSize_.x; i++ )
+       {
+               ccQuad3 coords = [self originalTile:ccg(i,0)];
+               float   direction = 1;
+               
+               if ( (i % 2 ) == 0 )
+                       direction = -1;
+               
+               coords.bl.y += direction * winSize.height * time;
+               coords.br.y += direction * winSize.height * time;
+               coords.tl.y += direction * winSize.height * time;
+               coords.tr.y += direction * winSize.height * time;
+               
+               [self setTile:ccg(i,0) coords:coords];
+       }
+}
+
+@end
diff --git a/libs/cocos2d/CCActionTween.h b/libs/cocos2d/CCActionTween.h
new file mode 100644 (file)
index 0000000..69fdea5
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright 2009 lhunath (Maarten Billemont)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+
+#import <Foundation/Foundation.h>
+#import "CCActionInterval.h"
+
+/** CCActionTween
+ CCActionTween is an action that lets you update any property of an object.
+ For example, if you want to modify the "width" property of a target from 200 to 300 in 2 senconds, then:
+       id modifyWidth = [CCActionTween actionWithDuration:2 key:@"width" from:200 to:300];
+       [target runAction:modifyWidth];
+
+ Another example: CCScaleTo action could be rewriten using CCPropertyAction:
+       // scaleA and scaleB are equivalents
+       id scaleA = [CCScaleTo actionWithDuration:2 scale:3];
+       id scaleB = [CCActionTween actionWithDuration:2 key:@"scale" from:1 to:3];
+
+ @since v0.99.2
+ */
+@interface CCActionTween : CCActionInterval
+{
+       NSString                *key_;
+    
+       float                   from_, to_;
+       float                   delta_;
+}
+
+/** creates an initializes the action with the property name (key), and the from and to parameters. */
++ (id)actionWithDuration:(ccTime)aDuration key:(NSString *)key from:(float)from to:(float)to;
+
+/** initializes the action with the property name (key), and the from and to parameters. */
+- (id)initWithDuration:(ccTime)aDuration key:(NSString *)key from:(float)from to:(float)to;
+    
+@end
diff --git a/libs/cocos2d/CCActionTween.m b/libs/cocos2d/CCActionTween.m
new file mode 100644 (file)
index 0000000..95ae572
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright 2009 lhunath (Maarten Billemont)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+#import "CCActionTween.h"
+
+
+@implementation CCActionTween
+
++ (id)actionWithDuration:(ccTime)aDuration key:(NSString *)aKey from:(float)aFrom to:(float)aTo {
+
+       return [[[[self class] alloc] initWithDuration:aDuration key:aKey from:aFrom to:aTo] autorelease];
+}
+
+- (id)initWithDuration:(ccTime)aDuration key:(NSString *)key from:(float)from to:(float)to {
+    
+       if ((self = [super initWithDuration:aDuration])) {
+    
+               key_    = [key copy];
+               to_             = to;
+               from_   = from;
+
+       }
+    
+       return self;
+}
+
+- (void) dealloc
+{
+       [key_ release];
+       [super dealloc];
+}
+
+- (void)startWithTarget:aTarget
+{
+       [super startWithTarget:aTarget];
+       delta_ = to_ - from_;
+}
+
+- (void) update:(ccTime) dt
+{    
+       [target_ setValue:[NSNumber numberWithFloat:to_  - delta_ * (1 - dt)] forKey:key_];
+}
+
+- (CCActionInterval *) reverse
+{
+       return [[self class] actionWithDuration:duration_ key:key_ from:to_ to:from_];
+}
+
+
+@end
diff --git a/libs/cocos2d/CCAnimation.h b/libs/cocos2d/CCAnimation.h
new file mode 100644 (file)
index 0000000..24b3d96
--- /dev/null
@@ -0,0 +1,136 @@
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2008-2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+#import <Foundation/Foundation.h>
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+#import <CoreGraphics/CoreGraphics.h>
+#endif // IPHONE
+
+@class CCSpriteFrame;
+@class CCTexture2D;
+
+/** A CCAnimation object is used to perform animations on the CCSprite objects.
+ The CCAnimation object contains CCSpriteFrame objects, and a possible delay between the frames.
+ You can animate a CCAnimation object by using the CCAnimate action. Example:
+  [sprite runAction:[CCAnimate actionWithAnimation:animation]];
+ */
+@interface CCAnimation : NSObject
+{
+       NSString                        *name_;
+       float                           delay_;
+       NSMutableArray          *frames_;
+}
+
+/** name of the animation */
+@property (nonatomic,readwrite,retain) NSString *name;
+/** delay between frames in seconds. */
+@property (nonatomic,readwrite,assign) float delay;
+/** array of frames */
+@property (nonatomic,readwrite,retain) NSMutableArray *frames;
+
+/** Creates an animation
+ @since v0.99.5
+ */
++(id) animation;
+
+/** Creates an animation with frames.
+ @since v0.99.5
+ */
++(id) animationWithFrames:(NSArray*)frames;
+
+/* Creates an animation with frames and a delay between frames.
+ @since v0.99.5
+ */
++(id) animationWithFrames:(NSArray*)frames delay:(float)delay;
+
+/** Creates a CCAnimation with a name
+ @since v0.99.3
+ @deprecated Will be removed in 1.0.1. Use "animation" instead.
+ */
++(id) animationWithName:(NSString*)name DEPRECATED_ATTRIBUTE;
+
+/** Creates a CCAnimation with a name and frames
+ @since v0.99.3
+ @deprecated Will be removed in 1.0.1. Use "animationWithFrames" instead.
+ */
++(id) animationWithName:(NSString*)name frames:(NSArray*)frames DEPRECATED_ATTRIBUTE;
+
+/** Creates a CCAnimation with a name and delay between frames. */
++(id) animationWithName:(NSString*)name delay:(float)delay DEPRECATED_ATTRIBUTE;
+
+/** Creates a CCAnimation with a name, delay and an array of CCSpriteFrames. */
++(id) animationWithName:(NSString*)name delay:(float)delay frames:(NSArray*)frames DEPRECATED_ATTRIBUTE;
+
+
+/** Initializes a CCAnimation with frames.
+ @since v0.99.5
+*/
+-(id) initWithFrames:(NSArray*)frames;
+
+/** Initializes a CCAnimation with frames and a delay between frames
+ @since v0.99.5
+ */
+-(id) initWithFrames:(NSArray *)frames delay:(float)delay;
+
+/** Initializes a CCAnimation with a name
+ @since v0.99.3
+ @deprecated Will be removed in 1.0.1. Use "init" instead.
+ */
+-(id) initWithName:(NSString*)name DEPRECATED_ATTRIBUTE;
+
+/** Initializes a CCAnimation with a name and frames
+ @since v0.99.3
+ @deprecated Will be removed in 1.0.1. Use "initWithFrames" instead.
+ */
+-(id) initWithName:(NSString*)name frames:(NSArray*)frames DEPRECATED_ATTRIBUTE;
+
+/** Initializes a CCAnimation with a name and delay between frames.
+ @deprecated Will be removed in 1.0.1. Use "initWithFrames:nil delay:delay" instead.
+*/
+-(id) initWithName:(NSString*)name delay:(float)delay DEPRECATED_ATTRIBUTE;
+
+/** Initializes a CCAnimation with a name, delay and an array of CCSpriteFrames.
+ @deprecated Will be removed in 1.0.1. Use "initWithFrames:frames delay:delay" instead.
+*/
+-(id) initWithName:(NSString*)name delay:(float)delay frames:(NSArray*)frames DEPRECATED_ATTRIBUTE;
+
+/** Adds a frame to a CCAnimation. */
+-(void) addFrame:(CCSpriteFrame*)frame;
+
+/** Adds a frame with an image filename. Internally it will create a CCSpriteFrame and it will add it.
+ Added to facilitate the migration from v0.8 to v0.9.
+ */
+-(void) addFrameWithFilename:(NSString*)filename;
+
+/** Adds a frame with a texture and a rect. Internally it will create a CCSpriteFrame and it will add it.
+ Added to facilitate the migration from v0.8 to v0.9.
+ */
+-(void) addFrameWithTexture:(CCTexture2D*)texture rect:(CGRect)rect;
+
+@end
diff --git a/libs/cocos2d/CCAnimation.m b/libs/cocos2d/CCAnimation.m
new file mode 100644 (file)
index 0000000..eb674c6
--- /dev/null
@@ -0,0 +1,153 @@
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2008-2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+#import "ccMacros.h"
+#import "CCAnimation.h"
+#import "CCSpriteFrame.h"
+#import "CCTexture2D.h"
+#import "CCTextureCache.h"
+
+@implementation CCAnimation
+@synthesize name = name_, delay = delay_, frames = frames_;
+
++(id) animation
+{
+       return [[[self alloc] init] autorelease];
+}
+
++(id) animationWithFrames:(NSArray*)frames
+{
+       return [[[self alloc] initWithFrames:frames] autorelease];
+}
+
++(id) animationWithFrames:(NSArray*)frames delay:(float)delay
+{
+       return [[[self alloc] initWithFrames:frames delay:delay] autorelease];
+}
+
++(id) animationWithName:(NSString*)name
+{
+       return [[[self alloc] initWithName:name] autorelease];
+}
+
++(id) animationWithName:(NSString*)name frames:(NSArray*)frames
+{
+       return [[[self alloc] initWithName:name frames:frames] autorelease];
+}
+
++(id) animationWithName:(NSString*)aname delay:(float)d frames:(NSArray*)array
+{
+       return [[[self alloc] initWithName:aname delay:d frames:array] autorelease];
+}
+
++(id) animationWithName:(NSString*)aname delay:(float)d
+{
+       return [[[self alloc] initWithName:aname delay:d] autorelease];
+}
+
+-(id) init
+{
+       return [self initWithFrames:nil delay:0];
+}
+
+-(id) initWithFrames:(NSArray*)frames
+{
+       return [self initWithFrames:frames delay:0];
+}
+
+-(id) initWithFrames:(NSArray*)array delay:(float)delay
+{
+       if( (self=[super init]) ) {
+               
+               delay_ = delay;
+               self.frames = [NSMutableArray arrayWithArray:array];
+       }
+       return self;
+}
+
+-(id) initWithName:(NSString*)name
+{
+       return [self initWithName:name delay:0 frames:nil];
+}
+
+-(id) initWithName:(NSString*)name frames:(NSArray*)frames
+{
+       return [self initWithName:name delay:0 frames:frames];
+}
+
+-(id) initWithName:(NSString*)t delay:(float)d
+{
+       return [self initWithName:t delay:d frames:nil];
+}
+
+-(id) initWithName:(NSString*)name delay:(float)delay frames:(NSArray*)array
+{
+       if( (self=[super init]) ) {
+               
+               delay_ = delay;
+               self.name = name;
+               self.frames = [NSMutableArray arrayWithArray:array];
+       }
+       return self;
+}
+
+- (NSString*) description
+{
+       return [NSString stringWithFormat:@"<%@ = %08X | frames=%d, delay:%f>", [self class], self,
+                       [frames_ count],
+                       delay_
+                       ];
+}
+
+-(void) dealloc
+{
+       CCLOGINFO( @"cocos2d: deallocing %@",self);
+       [name_ release];
+       [frames_ release];
+       [super dealloc];
+}
+
+-(void) addFrame:(CCSpriteFrame*)frame
+{
+       [frames_ addObject:frame];
+}
+
+-(void) addFrameWithFilename:(NSString*)filename
+{
+       CCTexture2D *texture = [[CCTextureCache sharedTextureCache] addImage:filename];
+       CGRect rect = CGRectZero;
+       rect.size = texture.contentSize;
+       CCSpriteFrame *frame = [CCSpriteFrame frameWithTexture:texture rect:rect];
+       [frames_ addObject:frame];
+}
+
+-(void) addFrameWithTexture:(CCTexture2D*)texture rect:(CGRect)rect
+{
+       CCSpriteFrame *frame = [CCSpriteFrame frameWithTexture:texture rect:rect];
+       [frames_ addObject:frame];
+}
+
+@end
diff --git a/libs/cocos2d/CCAnimationCache.h b/libs/cocos2d/CCAnimationCache.h
new file mode 100644 (file)
index 0000000..075c836
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+#import <Foundation/Foundation.h>
+
+@class CCAnimation;
+
+/** Singleton that manages the Animations.
+ It saves in a cache the animations. You should use this class if you want to save your animations in a cache.
+
+ Before v0.99.5, the recommend way was to save them on the CCSprite. Since v0.99.5, you should use this class instead.
+ @since v0.99.5
+ */
+@interface CCAnimationCache : NSObject
+{
+       NSMutableDictionary *animations_;
+}
+
+/** Retruns ths shared instance of the Animation cache */
++ (CCAnimationCache *) sharedAnimationCache;
+
+/** Purges the cache. It releases all the CCAnimation objects and the shared instance.
+ */
++(void)purgeSharedAnimationCache;
+
+/** Adds a CCAnimation with a name.
+ */
+-(void) addAnimation:(CCAnimation*)animation name:(NSString*)name;
+
+/** Deletes a CCAnimation from the cache.
+ */
+-(void) removeAnimationByName:(NSString*)name;
+
+/** Returns a CCAnimation that was previously added.
+ If the name is not found it will return nil.
+ You should retain the returned copy if you are going to use it.
+ */
+-(CCAnimation*) animationByName:(NSString*)name;
+
+@end
diff --git a/libs/cocos2d/CCAnimationCache.m b/libs/cocos2d/CCAnimationCache.m
new file mode 100644 (file)
index 0000000..f508227
--- /dev/null
@@ -0,0 +1,101 @@
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+#import "ccMacros.h"
+#import "CCAnimationCache.h"
+#import "CCAnimation.h"
+#import "CCSprite.h"
+
+
+@implementation CCAnimationCache
+
+#pragma mark CCAnimationCache - Alloc, Init & Dealloc
+
+static CCAnimationCache *sharedAnimationCache_=nil;
+
++ (CCAnimationCache *)sharedAnimationCache
+{
+       if (!sharedAnimationCache_)
+               sharedAnimationCache_ = [[CCAnimationCache alloc] init];
+               
+       return sharedAnimationCache_;
+}
+
++(id)alloc
+{
+       NSAssert(sharedAnimationCache_ == nil, @"Attempted to allocate a second instance of a singleton.");
+       return [super alloc];
+}
+
++(void)purgeSharedAnimationCache
+{
+       [sharedAnimationCache_ release];
+       sharedAnimationCache_ = nil;
+}
+
+-(id) init
+{
+       if( (self=[super init]) ) {
+               animations_ = [[NSMutableDictionary alloc] initWithCapacity: 20];
+       }
+       
+       return self;
+}
+
+- (NSString*) description
+{
+       return [NSString stringWithFormat:@"<%@ = %08X | num of animations =  %i>", [self class], self, [animations_ count]];
+}
+
+-(void) dealloc
+{
+       CCLOGINFO(@"cocos2d: deallocing %@", self);
+       
+       [animations_ release];
+       [super dealloc];
+}
+
+#pragma mark CCAnimationCache - load/get/del
+
+-(void) addAnimation:(CCAnimation*)animation name:(NSString*)name
+{
+       [animations_ setObject:animation forKey:name];
+}
+
+-(void) removeAnimationByName:(NSString*)name
+{
+       if( ! name )
+               return;
+       
+       [animations_ removeObjectForKey:name];
+}
+
+-(CCAnimation*) animationByName:(NSString*)name
+{
+       return [animations_ objectForKey:name];
+}
+
+@end
diff --git a/libs/cocos2d/CCAtlasNode.h b/libs/cocos2d/CCAtlasNode.h
new file mode 100644 (file)
index 0000000..c9da16f
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2008-2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ *
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+
+#import "CCTextureAtlas.h"
+#import "CCNode.h"
+#import "CCProtocols.h"
+
+/** CCAtlasNode is a subclass of CCNode that implements the CCRGBAProtocol and
+ CCTextureProtocol protocol
+ It knows how to render a TextureAtlas object.
+ If you are going to render a TextureAtlas consider subclassing CCAtlasNode (or a subclass of CCAtlasNode)
+ All features from CCNode are valid, plus the following features:
+ - opacity and RGB colors
+ */
+@interface CCAtlasNode : CCNode <CCRGBAProtocol, CCTextureProtocol>
+{
+       // texture atlas
+       CCTextureAtlas  *textureAtlas_;
+
+       // chars per row
+       NSUInteger              itemsPerRow_;
+       // chars per column
+       NSUInteger              itemsPerColumn_;
+               
+       // width of each char
+       NSUInteger              itemWidth_;
+       // height of each char
+       NSUInteger              itemHeight_;
+
+       // blend function
+       ccBlendFunc             blendFunc_;
+
+       // texture RGBA. 
+       GLubyte         opacity_;
+       ccColor3B       color_;
+       ccColor3B       colorUnmodified_;
+       BOOL opacityModifyRGB_;
+}
+
+/** conforms to CCTextureProtocol protocol */
+@property (nonatomic,readwrite,retain) CCTextureAtlas *textureAtlas;
+
+/** conforms to CCTextureProtocol protocol */
+@property (nonatomic,readwrite) ccBlendFunc blendFunc;
+
+/** conforms to CCRGBAProtocol protocol */
+@property (nonatomic,readwrite) GLubyte opacity;
+/** conforms to CCRGBAProtocol protocol */
+@property (nonatomic,readwrite) ccColor3B color;
+
+
+/** creates a CCAtlasNode  with an Atlas file the width and height of each item measured in points and the quantity of items to render*/
++(id) atlasWithTileFile:(NSString*)tile tileWidth:(NSUInteger)w tileHeight:(NSUInteger)h itemsToRender: (NSUInteger) c;
+
+/** initializes an CCAtlasNode  with an Atlas file the width and height of each item measured in points and the quantity of items to render*/
+-(id) initWithTileFile:(NSString*)tile tileWidth:(NSUInteger)w tileHeight:(NSUInteger)h itemsToRender: (NSUInteger) c;
+
+/** updates the Atlas (indexed vertex array).
+ * Shall be overriden in subclasses
+ */
+-(void) updateAtlasValues;
+@end
diff --git a/libs/cocos2d/CCAtlasNode.m b/libs/cocos2d/CCAtlasNode.m
new file mode 100644 (file)
index 0000000..f715f5b
--- /dev/null
@@ -0,0 +1,206 @@
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2008-2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+
+#import "CCAtlasNode.h"
+#import "ccMacros.h"
+
+
+@interface CCAtlasNode ()
+-(void) calculateMaxItems;
+-(void) updateBlendFunc;
+-(void) updateOpacityModifyRGB;
+@end
+
+@implementation CCAtlasNode
+
+@synthesize textureAtlas = textureAtlas_;
+@synthesize blendFunc = blendFunc_;
+
+#pragma mark CCAtlasNode - Creation & Init
++(id) atlasWithTileFile:(NSString*)tile tileWidth:(NSUInteger)w tileHeight:(NSUInteger)h itemsToRender: (NSUInteger) c
+{
+       return [[[self alloc] initWithTileFile:tile tileWidth:w tileHeight:h itemsToRender:c] autorelease];
+}
+
+-(id) initWithTileFile:(NSString*)tile tileWidth:(NSUInteger)w tileHeight:(NSUInteger)h itemsToRender: (NSUInteger) c
+{
+       if( (self=[super init]) ) {
+       
+               itemWidth_ = w * CC_CONTENT_SCALE_FACTOR();
+               itemHeight_ = h * CC_CONTENT_SCALE_FACTOR();
+
+               opacity_ = 255;
+               color_ = colorUnmodified_ = ccWHITE;
+               opacityModifyRGB_ = YES;
+               
+               blendFunc_.src = CC_BLEND_SRC;
+               blendFunc_.dst = CC_BLEND_DST;
+               
+               // double retain to avoid the autorelease pool
+               // also, using: self.textureAtlas supports re-initialization without leaking
+               self.textureAtlas = [[CCTextureAtlas alloc] initWithFile:tile capacity:c];
+               [textureAtlas_ release];
+               
+               if( ! textureAtlas_ ) {
+                       CCLOG(@"cocos2d: Could not initialize CCAtlasNode. Invalid Texture");
+                       [self release];
+                       return nil;
+               }
+               
+               [self updateBlendFunc];
+               [self updateOpacityModifyRGB];
+               
+               [self calculateMaxItems];
+               
+       }
+       return self;
+}
+
+-(void) dealloc
+{
+       [textureAtlas_ release];
+       
+       [super dealloc];
+}
+
+#pragma mark CCAtlasNode - Atlas generation
+
+-(void) calculateMaxItems
+{
+       CGSize s = [[textureAtlas_ texture] contentSizeInPixels];
+       itemsPerColumn_ = s.height / itemHeight_;
+       itemsPerRow_ = s.width / itemWidth_;
+}
+
+-(void) updateAtlasValues
+{
+       [NSException raise:@"CCAtlasNode:Abstract" format:@"updateAtlasValue not overriden"];
+}
+
+#pragma mark CCAtlasNode - draw
+- (void) draw
+{
+       // Default GL states: GL_TEXTURE_2D, GL_VERTEX_ARRAY, GL_COLOR_ARRAY, GL_TEXTURE_COORD_ARRAY
+       // Needed states: GL_TEXTURE_2D, GL_VERTEX_ARRAY, GL_TEXTURE_COORD_ARRAY
+       // Unneeded states: GL_COLOR_ARRAY
+       glDisableClientState(GL_COLOR_ARRAY);
+
+       glColor4ub( color_.r, color_.g, color_.b, opacity_);
+
+       BOOL newBlend = blendFunc_.src != CC_BLEND_SRC || blendFunc_.dst != CC_BLEND_DST;
+       if( newBlend )
+               glBlendFunc( blendFunc_.src, blendFunc_.dst );
+               
+       [textureAtlas_ drawQuads];
+               
+       if( newBlend )
+               glBlendFunc(CC_BLEND_SRC, CC_BLEND_DST);
+       
+       // is this chepear than saving/restoring color state ?
+       // XXX: There is no need to restore the color to (255,255,255,255). Objects should use the color
+       // XXX: that they need
+//     glColor4ub( 255, 255, 255, 255);
+
+       // restore default GL state
+       glEnableClientState(GL_COLOR_ARRAY);
+
+}
+
+#pragma mark CCAtlasNode - RGBA protocol
+
+- (ccColor3B) color
+{
+       if(opacityModifyRGB_)
+               return colorUnmodified_;
+       
+       return color_;
+}
+
+-(void) setColor:(ccColor3B)color3
+{
+       color_ = colorUnmodified_ = color3;
+       
+       if( opacityModifyRGB_ ){
+               color_.r = color3.r * opacity_/255;
+               color_.g = color3.g * opacity_/255;
+               color_.b = color3.b * opacity_/255;
+       }       
+}
+
+-(GLubyte) opacity
+{
+       return opacity_;
+}
+
+-(void) setOpacity:(GLubyte) anOpacity
+{
+       opacity_                        = anOpacity;
+       
+       // special opacity for premultiplied textures
+       if( opacityModifyRGB_ )
+               [self setColor: colorUnmodified_];      
+}
+
+-(void) setOpacityModifyRGB:(BOOL)modify
+{
+       ccColor3B oldColor      = self.color;
+       opacityModifyRGB_       = modify;
+       self.color                      = oldColor;
+}
+
+-(BOOL) doesOpacityModifyRGB
+{
+       return opacityModifyRGB_;
+}
+
+-(void) updateOpacityModifyRGB
+{
+       opacityModifyRGB_ = [textureAtlas_.texture hasPremultipliedAlpha];
+}
+
+#pragma mark CCAtlasNode - CocosNodeTexture protocol
+
+-(void) updateBlendFunc
+{
+       if( ! [textureAtlas_.texture hasPremultipliedAlpha] ) {
+               blendFunc_.src = GL_SRC_ALPHA;
+               blendFunc_.dst = GL_ONE_MINUS_SRC_ALPHA;
+       }
+}
+
+-(void) setTexture:(CCTexture2D*)texture
+{
+       textureAtlas_.texture = texture;
+       [self updateBlendFunc];
+       [self updateOpacityModifyRGB];
+}
+
+-(CCTexture2D*) texture
+{
+       return textureAtlas_.texture;
+}
+
+@end
diff --git a/libs/cocos2d/CCBlockSupport.h b/libs/cocos2d/CCBlockSupport.h
new file mode 100644 (file)
index 0000000..339d5aa
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2010 Stuart Carnie
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+
+#import <Foundation/Foundation.h>
+
+/** @file
+ cocos2d blocks support
+ */
+
+// To comply with Apple Objective C runtime (this is defined in NSObjCRuntime.h)
+#if !defined(NS_BLOCKS_AVAILABLE)
+       #if __BLOCKS__
+               #define NS_BLOCKS_AVAILABLE 1
+       #else
+               #define NS_BLOCKS_AVAILABLE 0
+       #endif
+#endif
+
+#if NS_BLOCKS_AVAILABLE
+
+@interface NSObject(CCBlocksAdditions)
+
+- (void)ccCallbackBlock;
+- (void)ccCallbackBlockWithSender:(id)sender;
+
+@end
+
+#endif // NS_BLOCKS_AVAILABLE
diff --git a/libs/cocos2d/CCBlockSupport.m b/libs/cocos2d/CCBlockSupport.m
new file mode 100644 (file)
index 0000000..9ac99b3
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2010 Stuart Carnie
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+
+#import "CCBlockSupport.h"
+
+#if NS_BLOCKS_AVAILABLE
+
+@implementation NSObject(CCBlocksAdditions)
+
+- (void)ccCallbackBlock {
+       void (^block)(void) = (id)self;
+       block();
+}
+
+- (void)ccCallbackBlockWithSender:(id)sender {
+       void (^block)(id) = (id)self;
+       block(sender);
+}
+
+
+@end
+
+#endif
diff --git a/libs/cocos2d/CCCamera.h b/libs/cocos2d/CCCamera.h
new file mode 100644 (file)
index 0000000..19a7712
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2008-2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+
+
+#import "CCNode.h"
+
+/** 
+    A CCCamera is used in every CCNode.
+    Useful to look at the object from different views.
+    The OpenGL gluLookAt() function is used to locate the
+    camera.
+
+    If the object is transformed by any of the scale, rotation or
+    position attributes, then they will override the camera.
+       IMPORTANT: Either your use the camera or the rotation/scale/position properties. You can't use both.
+    World coordinates won't work if you use the camera.
+
+    Limitations:
+     - Some nodes, like CCParallaxNode, CCParticle uses world node coordinates, and they won't work properly if you move them (or any of their ancestors)
+       using the camera.
+     
+     - It doesn't work on batched nodes like CCSprite objects when they are parented to a CCSpriteBatchNode object.
+        - It is recommended to use it ONLY if you are going to create 3D effects. For 2D effecs, use the action CCFollow or position/scale/rotate.
+*/
+
+@interface CCCamera : NSObject
+{
+    float eyeX_;
+    float eyeY_;
+    float eyeZ_;
+
+    float centerX_;
+    float centerY_;
+    float centerZ_;
+
+    float upX_;
+    float upY_;
+    float upZ_;
+       
+       BOOL dirty_;
+}
+
+/** whether of not the camera is dirty */
+@property (nonatomic,readwrite) BOOL dirty;
+
+/** returns the Z eye */
++(float) getZEye;
+
+/** sets the camera in the defaul position */
+-(void) restore;
+/** Sets the camera using gluLookAt using its eye, center and up_vector */
+-(void) locate;
+/** sets the eye values in points */
+-(void) setEyeX: (float)x eyeY:(float)y eyeZ:(float)z;
+/** sets the center values in points */
+-(void) setCenterX: (float)x centerY:(float)y centerZ:(float)z;
+/** sets the up values */
+-(void) setUpX: (float)x upY:(float)y upZ:(float)z;
+
+/** get the eye vector values in points */
+-(void) eyeX:(float*)x eyeY:(float*)y eyeZ:(float*)z;
+/** get the center vector values in points */
+-(void) centerX:(float*)x centerY:(float*)y centerZ:(float*)z;
+/** get the up vector values */
+-(void) upX:(float*)x upY:(float*)y upZ:(float*)z;
+
+
+@end
diff --git a/libs/cocos2d/CCCamera.m b/libs/cocos2d/CCCamera.m
new file mode 100644 (file)
index 0000000..1ef6655
--- /dev/null
@@ -0,0 +1,131 @@
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2008-2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+
+#import "Platforms/CCGL.h"
+#import "CCCamera.h"
+#import "ccMacros.h"
+#import "CCDrawingPrimitives.h"
+
+@implementation CCCamera
+
+@synthesize dirty = dirty_;
+
+-(id) init
+{
+       if( (self=[super init]) )
+               [self restore];
+       
+       return self;
+}
+
+- (NSString*) description
+{
+       return [NSString stringWithFormat:@"<%@ = %08X | center = (%.2f,%.2f,%.2f)>", [self class], self, centerX_, centerY_, centerZ_];
+}
+
+
+- (void) dealloc
+{
+       CCLOGINFO(@"cocos2d: deallocing %@", self);
+       [super dealloc];
+}
+
+-(void) restore
+{
+       eyeX_ = eyeY_ = 0;
+       eyeZ_ = [CCCamera getZEye];
+       
+       centerX_ = centerY_ = centerZ_ = 0;
+       
+       upX_ = 0.0f;
+       upY_ = 1.0f;
+       upZ_ = 0.0f;
+       
+       dirty_ = NO;
+}
+
+-(void) locate
+{
+       if( dirty_ )
+               gluLookAt( eyeX_, eyeY_, eyeZ_,
+                               centerX_, centerY_, centerZ_,
+                               upX_, upY_, upZ_
+                               );
+}
+
++(float) getZEye
+{
+       return FLT_EPSILON;
+//     CGSize s = [[CCDirector sharedDirector] displaySize];
+//     return ( s.height / 1.1566f );
+}
+
+-(void) setEyeX: (float)x eyeY:(float)y eyeZ:(float)z
+{
+       eyeX_ = x * CC_CONTENT_SCALE_FACTOR();
+       eyeY_ = y * CC_CONTENT_SCALE_FACTOR();
+       eyeZ_ = z * CC_CONTENT_SCALE_FACTOR();
+       dirty_ = YES;   
+}
+
+-(void) setCenterX: (float)x centerY:(float)y centerZ:(float)z
+{
+       centerX_ = x * CC_CONTENT_SCALE_FACTOR();
+       centerY_ = y * CC_CONTENT_SCALE_FACTOR();
+       centerZ_ = z * CC_CONTENT_SCALE_FACTOR();
+       dirty_ = YES;
+}
+
+-(void) setUpX: (float)x upY:(float)y upZ:(float)z
+{
+       upX_ = x;
+       upY_ = y;
+       upZ_ = z;
+       dirty_ = YES;
+}
+
+-(void) eyeX: (float*)x eyeY:(float*)y eyeZ:(float*)z
+{
+       *x = eyeX_ / CC_CONTENT_SCALE_FACTOR();
+       *y = eyeY_ / CC_CONTENT_SCALE_FACTOR();
+       *z = eyeZ_ / CC_CONTENT_SCALE_FACTOR();
+}
+
+-(void) centerX: (float*)x centerY:(float*)y centerZ:(float*)z
+{
+       *x = centerX_ / CC_CONTENT_SCALE_FACTOR();
+       *y = centerY_ / CC_CONTENT_SCALE_FACTOR();
+       *z = centerZ_ / CC_CONTENT_SCALE_FACTOR();
+}
+
+-(void) upX: (float*)x upY:(float*)y upZ:(float*)z
+{
+       *x = upX_;
+       *y = upY_;
+       *z = upZ_;
+}
+
+@end
diff --git a/libs/cocos2d/CCConfiguration.h b/libs/cocos2d/CCConfiguration.h
new file mode 100644 (file)
index 0000000..04e1b55
--- /dev/null
@@ -0,0 +1,116 @@
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#import <Foundation/Foundation.h>
+
+#import "Platforms/CCGL.h"
+
+/** OS version definitions. Includes both iOS and Mac OS versions
+ */
+enum {
+       kCCiOSVersion_3_0   = 0x03000000,
+       kCCiOSVersion_3_1   = 0x03010000,
+       kCCiOSVersion_3_1_1 = 0x03010100,
+       kCCiOSVersion_3_1_2 = 0x03010200,
+       kCCiOSVersion_3_1_3 = 0x03010300,
+       kCCiOSVersion_3_2   = 0x03020000,
+       kCCiOSVersion_3_2_1 = 0x03020100,
+       kCCiOSVersion_4_0   = 0x04000000,
+       kCCiOSVersion_4_0_1 = 0x04000100,
+       kCCiOSVersion_4_1   = 0x04010000,
+       kCCiOSVersion_4_2   = 0x04020000,
+       kCCiOSVersion_4_3   = 0x04030000,
+       kCCiOSVersion_4_3_1 = 0x04030100,
+       kCCiOSVersion_4_3_2 = 0x04030200,
+       kCCiOSVersion_4_3_3 = 0x04030300,
+
+       kCCMacVersion_10_5  = 0x0a050000,
+       kCCMacVersion_10_6  = 0x0a060000,
+       kCCMacVersion_10_7  = 0x0a070000,
+};
+
+/**
+ CCConfiguration contains some openGL variables
+ @since v0.99.0
+ */
+@interface CCConfiguration : NSObject {
+
+       GLint                   maxTextureSize_;
+       GLint                   maxModelviewStackDepth_;
+       BOOL                    supportsPVRTC_;
+       BOOL                    supportsNPOT_;
+       BOOL                    supportsBGRA8888_;
+       BOOL                    supportsDiscardFramebuffer_;
+       unsigned int    OSVersion_;
+       GLint                   maxSamplesAllowed_;
+}
+
+/** OpenGL Max texture size. */
+@property (nonatomic, readonly) GLint maxTextureSize;
+
+/** OpenGL Max Modelview Stack Depth. */
+@property (nonatomic, readonly) GLint maxModelviewStackDepth;
+
+/** Whether or not the GPU supports NPOT (Non Power Of Two) textures.
+ NPOT textures have the following limitations:
+ - They can't have mipmaps
+ - They only accept GL_CLAMP_TO_EDGE in GL_TEXTURE_WRAP_{S,T}
+ @since v0.99.2
+ */
+@property (nonatomic, readonly) BOOL supportsNPOT;
+
+/** Whether or not PVR Texture Compressed is supported */
+@property (nonatomic, readonly) BOOL supportsPVRTC;
+
+/** Whether or not BGRA8888 textures are supported.
+ @since v0.99.2
+ */
+@property (nonatomic, readonly) BOOL supportsBGRA8888;
+
+/** Whether or not glDiscardFramebufferEXT is supported
+ @since v0.99.2
+ */
+@property (nonatomic, readonly) BOOL supportsDiscardFramebuffer;
+
+/** returns the OS version.
+       - On iOS devices it returns the firmware version.
+       - On Mac returns the OS version
+ @since v0.99.5
+ */
+@property (nonatomic, readonly) unsigned int OSVersion;
+
+/** returns a shared instance of the CCConfiguration */
++(CCConfiguration *) sharedConfiguration;
+
+/** returns whether or not an OpenGL is supported */
+- (BOOL) checkForGLExtension:(NSString *)searchName;
+
+
+
+@end
diff --git a/libs/cocos2d/CCConfiguration.m b/libs/cocos2d/CCConfiguration.m
new file mode 100644 (file)
index 0000000..d51cd58
--- /dev/null
@@ -0,0 +1,193 @@
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#import <Availability.h>
+
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+#import <UIKit/UIKit.h>                // Needed for UIDevice
+#endif
+
+#import "Platforms/CCGL.h"
+#import "CCBlockSupport.h"
+#import "CCConfiguration.h"
+#import "ccMacros.h"
+#import "ccConfig.h"
+#import "Support/OpenGL_Internal.h"
+
+@implementation CCConfiguration
+
+@synthesize maxTextureSize = maxTextureSize_;
+@synthesize supportsPVRTC = supportsPVRTC_;
+@synthesize maxModelviewStackDepth = maxModelviewStackDepth_;
+@synthesize supportsNPOT = supportsNPOT_;
+@synthesize supportsBGRA8888 = supportsBGRA8888_;
+@synthesize supportsDiscardFramebuffer = supportsDiscardFramebuffer_;
+@synthesize OSVersion = OSVersion_;
+
+//
+// singleton stuff
+//
+static CCConfiguration *_sharedConfiguration = nil;
+
+static char * glExtensions;
+
++ (CCConfiguration *)sharedConfiguration
+{
+       if (!_sharedConfiguration)
+               _sharedConfiguration = [[self alloc] init];
+
+       return _sharedConfiguration;
+}
+
++(id)alloc
+{
+       NSAssert(_sharedConfiguration == nil, @"Attempted to allocate a second instance of a singleton.");
+       return [super alloc];
+}
+
+
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED)
+- (NSString*)getMacVersion
+{
+    SInt32 versionMajor, versionMinor, versionBugFix;
+       Gestalt(gestaltSystemVersionMajor, &versionMajor);
+       Gestalt(gestaltSystemVersionMinor, &versionMinor);
+       Gestalt(gestaltSystemVersionBugFix, &versionBugFix);
+       
+       return [NSString stringWithFormat:@"%d.%d.%d", versionMajor, versionMinor, versionBugFix];
+}
+#endif // __MAC_OS_X_VERSION_MAX_ALLOWED
+
+-(id) init
+{
+       if( (self=[super init])) {
+               
+               // Obtain iOS version
+               OSVersion_ = 0;
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+               NSString *OSVer = [[UIDevice currentDevice] systemVersion];
+#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED)
+               NSString *OSVer = [self getMacVersion];
+#endif
+               NSArray *arr = [OSVer componentsSeparatedByString:@"."];                
+               int idx=0x01000000;
+               for( NSString *str in arr ) {
+                       int value = [str intValue];
+                       OSVersion_ += value * idx;
+                       idx = idx >> 8;
+               }
+               CCLOG(@"cocos2d: OS version: %@ (0x%08x)", OSVer, OSVersion_);
+               
+               CCLOG(@"cocos2d: GL_VENDOR:   %s", glGetString(GL_VENDOR) );
+               CCLOG(@"cocos2d: GL_RENDERER: %s", glGetString ( GL_RENDERER   ) );
+               CCLOG(@"cocos2d: GL_VERSION:  %s", glGetString ( GL_VERSION    ) );
+               
+               glExtensions = (char*) glGetString(GL_EXTENSIONS);
+               
+               glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize_);
+               glGetIntegerv(GL_MAX_MODELVIEW_STACK_DEPTH, &maxModelviewStackDepth_);
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+               if( OSVersion_ >= kCCiOSVersion_4_0 )
+                       glGetIntegerv(GL_MAX_SAMPLES_APPLE, &maxSamplesAllowed_);
+               else
+                       maxSamplesAllowed_ = 0;
+#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED)
+               glGetIntegerv(GL_MAX_SAMPLES, &maxSamplesAllowed_);
+#endif
+               
+               supportsPVRTC_ = [self checkForGLExtension:@"GL_IMG_texture_compression_pvrtc"];
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+               supportsNPOT_ = [self checkForGLExtension:@"GL_APPLE_texture_2D_limited_npot"];
+#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED)
+               supportsNPOT_ = [self checkForGLExtension:@"GL_ARB_texture_non_power_of_two"];
+#endif
+               // It seems that somewhere between firmware iOS 3.0 and 4.2 Apple renamed
+               // GL_IMG_... to GL_APPLE.... So we should check both names
+               
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+               BOOL bgra8a = [self checkForGLExtension:@"GL_IMG_texture_format_BGRA8888"];
+               BOOL bgra8b = [self checkForGLExtension:@"GL_APPLE_texture_format_BGRA8888"];
+               supportsBGRA8888_ = bgra8a | bgra8b;
+#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED)
+               supportsBGRA8888_ = [self checkForGLExtension:@"GL_EXT_bgra"];
+#endif
+               
+               supportsDiscardFramebuffer_ = [self checkForGLExtension:@"GL_EXT_discard_framebuffer"];
+
+               CCLOG(@"cocos2d: GL_MAX_TEXTURE_SIZE: %d", maxTextureSize_);
+               CCLOG(@"cocos2d: GL_MAX_MODELVIEW_STACK_DEPTH: %d",maxModelviewStackDepth_);
+               CCLOG(@"cocos2d: GL_MAX_SAMPLES: %d", maxSamplesAllowed_);
+               CCLOG(@"cocos2d: GL supports PVRTC: %s", (supportsPVRTC_ ? "YES" : "NO") );
+               CCLOG(@"cocos2d: GL supports BGRA8888 textures: %s", (supportsBGRA8888_ ? "YES" : "NO") );
+               CCLOG(@"cocos2d: GL supports NPOT textures: %s", (supportsNPOT_ ? "YES" : "NO") );
+               CCLOG(@"cocos2d: GL supports discard_framebuffer: %s", (supportsDiscardFramebuffer_ ? "YES" : "NO") );
+               CCLOG(@"cocos2d: compiled with NPOT support: %s",
+#if CC_TEXTURE_NPOT_SUPPORT
+                               "YES"
+#else
+                               "NO"
+#endif
+                         );
+               CCLOG(@"cocos2d: compiled with VBO support in TextureAtlas : %s",
+#if CC_USES_VBO
+                         "YES"
+#else
+                         "NO"
+#endif
+                         );
+
+               CCLOG(@"cocos2d: compiled with Affine Matrix transformation in CCNode : %s",
+#if CC_NODE_TRANSFORM_USING_AFFINE_MATRIX
+                         "YES"
+#else
+                         "NO"
+#endif
+                         );
+               
+               CCLOG(@"cocos2d: compiled with Profiling Support: %s",
+#if CC_ENABLE_PROFILERS
+
+                         "YES - *** Disable it when you finish profiling ***"
+#else
+                         "NO"
+#endif
+                         );
+               
+               CHECK_GL_ERROR();
+       }
+       
+       return self;
+}
+
+- (BOOL) checkForGLExtension:(NSString *)searchName
+{
+       // For best results, extensionsNames should be stored in your renderer so that it does not
+       // need to be recreated on each invocation.
+    NSString *extensionsString = [NSString stringWithCString:glExtensions encoding: NSASCIIStringEncoding];
+    NSArray *extensionsNames = [extensionsString componentsSeparatedByString:@" "];
+    return [extensionsNames containsObject: searchName];
+}
+@end
diff --git a/libs/cocos2d/CCDirector.h b/libs/cocos2d/CCDirector.h
new file mode 100644 (file)
index 0000000..15d60e5
--- /dev/null
@@ -0,0 +1,305 @@
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2008-2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+
+#import "ccConfig.h"
+#import "ccTypes.h"
+
+// OpenGL related
+#import "Platforms/CCGL.h"
+#import "CCProtocols.h"
+
+/** @typedef ccDirectorProjection
+ Possible OpenGL projections used by director
+ */
+typedef enum {
+       /// sets a 2D projection (orthogonal projection).
+       kCCDirectorProjection2D,
+       
+       /// sets a 3D projection with a fovy=60, znear=0.5f and zfar=1500.
+       kCCDirectorProjection3D,
+       
+       /// it calls "updateProjection" on the projection delegate.
+       kCCDirectorProjectionCustom,
+       
+       /// Detault projection is 3D projection
+       kCCDirectorProjectionDefault = kCCDirectorProjection3D,
+       
+       // backward compatibility stuff
+       CCDirectorProjection2D = kCCDirectorProjection2D,
+       CCDirectorProjection3D = kCCDirectorProjection3D,
+       CCDirectorProjectionCustom = kCCDirectorProjectionCustom,
+
+} ccDirectorProjection;
+
+
+@class CCLabelAtlas;
+@class CCScene;
+
+/**Class that creates and handle the main Window and manages how
+and when to execute the Scenes.
+ The CCDirector is also resposible for:
+  - initializing the OpenGL ES context
+  - setting the OpenGL pixel format (default on is RGB565)
+  - setting the OpenGL buffer depth (default one is 0-bit)
+  - setting the projection (default one is 3D)
+  - setting the orientation (default one is Protrait)
+ Since the CCDirector is a singleton, the standard way to use it is by calling:
+  - [[CCDirector sharedDirector] methodName];
+ The CCDirector also sets the default OpenGL context:
+  - GL_TEXTURE_2D is enabled
+  - GL_VERTEX_ARRAY is enabled
+  - GL_COLOR_ARRAY is enabled
+  - GL_TEXTURE_COORD_ARRAY is enabled
+*/
+@interface CCDirector : NSObject
+{
+       CC_GLVIEW       *openGLView_;
+
+       // internal timer
+       NSTimeInterval animationInterval_;
+       NSTimeInterval oldAnimationInterval_;   
+       
+       /* display FPS ? */
+       BOOL displayFPS_;
+
+       NSUInteger frames_;
+       ccTime accumDt_;
+       ccTime frameRate_;
+#if    CC_DIRECTOR_FAST_FPS
+       CCLabelAtlas *FPSLabel_;
+#endif
+       
+       /* is the running scene paused */
+       BOOL isPaused_;
+       
+       /* The running scene */
+       CCScene *runningScene_;
+       
+       /* This object will be visited after the scene. Useful to hook a notification node */
+       id notificationNode_;
+       
+       /* will be the next 'runningScene' in the next frame
+        nextScene is a weak reference. */
+       CCScene *nextScene_;
+       
+       /* If YES, then "old" scene will receive the cleanup message */
+       BOOL    sendCleanupToScene_;
+
+       /* scheduled scenes */
+       NSMutableArray *scenesStack_;
+       
+       /* last time the main loop was updated */
+       struct timeval lastUpdate_;
+       /* delta time since last tick to main loop */
+       ccTime dt;
+       /* whether or not the next delta time will be zero */
+       BOOL nextDeltaTimeZero_;
+       
+       /* projection used */
+       ccDirectorProjection projection_;
+       
+       /* Projection protocol delegate */
+       id<CCProjectionProtocol>        projectionDelegate_;
+
+       /* window size in points */
+       CGSize  winSizeInPoints_;
+       
+       /* window size in pixels */
+       CGSize  winSizeInPixels_;
+
+       /* the cocos2d running thread */
+       NSThread        *runningThread_;
+
+       // profiler
+#if CC_ENABLE_PROFILERS
+       ccTime accumDtForProfiler_;
+#endif
+}
+
+/** returns the cocos2d thread.
+ If you want to run any cocos2d task, run it in this thread.
+ On iOS usually it is the main thread.
+ @since v0.99.5
+ */
+@property (readonly, nonatomic ) NSThread *runningThread;
+/** The current running Scene. Director can only run one Scene at the time */
+@property (nonatomic,readonly) CCScene* runningScene;
+/** The FPS value */
+@property (nonatomic,readwrite, assign) NSTimeInterval animationInterval;
+/** Whether or not to display the FPS on the bottom-left corner */
+@property (nonatomic,readwrite, assign) BOOL displayFPS;
+/** The OpenGLView, where everything is rendered */
+@property (nonatomic,readwrite,retain) CC_GLVIEW *openGLView;
+/** whether or not the next delta time will be zero */
+@property (nonatomic,readwrite,assign) BOOL nextDeltaTimeZero;
+/** Whether or not the Director is paused */
+@property (nonatomic,readonly) BOOL isPaused;
+/** Sets an OpenGL projection
+ @since v0.8.2
+ */
+@property (nonatomic,readwrite) ccDirectorProjection projection;
+
+/** Whether or not the replaced scene will receive the cleanup message.
+ If the new scene is pushed, then the old scene won't receive the "cleanup" message.
+ If the new scene replaces the old one, the it will receive the "cleanup" message.
+ @since v0.99.0
+ */
+@property (nonatomic, readonly) BOOL sendCleanupToScene;
+
+/** This object will be visited after the main scene is visited.
+ This object MUST implement the "visit" selector.
+ Useful to hook a notification object, like CCNotifications (http://github.com/manucorporat/CCNotifications)
+ @since v0.99.5
+ */
+@property (nonatomic, readwrite, retain) id    notificationNode;
+
+/** This object will be called when the OpenGL projection is udpated and only when the kCCDirectorProjectionCustom projection is used.
+ @since v0.99.5
+ */
+@property (nonatomic, readwrite, retain) id<CCProjectionProtocol> projectionDelegate;
+
+/** returns a shared instance of the director */
++(CCDirector *)sharedDirector;
+
+
+
+// Window size
+
+/** returns the size of the OpenGL view in points.
+ It takes into account any possible rotation (device orientation) of the window
+ */
+- (CGSize) winSize;
+
+/** returns the size of the OpenGL view in pixels.
+ It takes into account any possible rotation (device orientation) of the window.
+ On Mac winSize and winSizeInPixels return the same value.
+ */
+- (CGSize) winSizeInPixels;
+/** returns the display size of the OpenGL view in pixels.
+ It doesn't take into account any possible rotation of the window.
+ */
+-(CGSize) displaySizeInPixels;
+/** changes the projection size */
+-(void) reshapeProjection:(CGSize)newWindowSize;
+
+/** converts a UIKit coordinate to an OpenGL coordinate
+ Useful to convert (multi) touchs coordinates to the current layout (portrait or landscape)
+ */
+-(CGPoint) convertToGL: (CGPoint) p;
+/** converts an OpenGL coordinate to a UIKit coordinate
+ Useful to convert node points to window points for calls such as glScissor
+ */
+-(CGPoint) convertToUI:(CGPoint)p;
+
+/// XXX: missing description
+-(float) getZEye;
+
+// Scene Management
+
+/**Enters the Director's main loop with the given Scene. 
+ * Call it to run only your FIRST scene.
+ * Don't call it if there is already a running scene.
+ */
+- (void) runWithScene:(CCScene*) scene;
+
+/**Suspends the execution of the running scene, pushing it on the stack of suspended scenes.
+ * The new scene will be executed.
+ * Try to avoid big stacks of pushed scenes to reduce memory allocation. 
+ * ONLY call it if there is a running scene.
+ */
+- (void) pushScene:(CCScene*) scene;
+
+/**Pops out a scene from the queue.
+ * This scene will replace the running one.
+ * The running scene will be deleted. If there are no more scenes in the stack the execution is terminated.
+ * ONLY call it if there is a running scene.
+ */
+- (void) popScene;
+
+/** Replaces the running scene with a new one. The running scene is terminated.
+ * ONLY call it if there is a running scene.
+ */
+-(void) replaceScene: (CCScene*) scene;
+
+/** Ends the execution, releases the running scene.
+ It doesn't remove the OpenGL view from its parent. You have to do it manually.
+ */
+-(void) end;
+
+/** Pauses the running scene.
+ The running scene will be _drawed_ but all scheduled timers will be paused
+ While paused, the draw rate will be 4 FPS to reduce CPU consuption
+ */
+-(void) pause;
+
+/** Resumes the paused scene
+ The scheduled timers will be activated again.
+ The "delta time" will be 0 (as if the game wasn't paused)
+ */
+-(void) resume;
+
+/** Stops the animation. Nothing will be drawn. The main loop won't be triggered anymore.
+ If you wan't to pause your animation call [pause] instead.
+ */
+-(void) stopAnimation;
+
+/** The main loop is triggered again.
+ Call this function only if [stopAnimation] was called earlier
+ @warning Dont' call this function to start the main loop. To run the main loop call runWithScene
+ */
+-(void) startAnimation;
+
+/** Draw the scene.
+ This method is called every frame. Don't call it manually.
+ */
+-(void) drawScene;
+
+// Memory Helper
+
+/** Removes all the cocos2d data that was cached automatically.
+ It will purge the CCTextureCache, CCBitmapFont cache.
+ IMPORTANT: The CCSpriteFrameCache won't be purged. If you want to purge it, you have to purge it manually.
+ @since v0.99.3
+ */
+-(void) purgeCachedData; 
+
+// OpenGL Helper
+
+/** sets the OpenGL default values */
+-(void) setGLDefaultValues;
+
+/** enables/disables OpenGL alpha blending */
+- (void) setAlphaBlending: (BOOL) on;
+/** enables/disables OpenGL depth test */
+- (void) setDepthTest: (BOOL) on;
+
+// Profiler
+-(void) showProfilers;
+
+@end
diff --git a/libs/cocos2d/CCDirector.m b/libs/cocos2d/CCDirector.m
new file mode 100644 (file)
index 0000000..427620d
--- /dev/null
@@ -0,0 +1,564 @@
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2008-2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+
+/* Idea of decoupling Window from Director taken from OC3D project: http://code.google.com/p/oc3d/
+ */
+
+#import <unistd.h>
+#import <Availability.h>
+
+// cocos2d imports
+#import "CCDirector.h"
+#import "CCScheduler.h"
+#import "CCActionManager.h"
+#import "CCTextureCache.h"
+#import "CCAnimationCache.h"
+#import "CCLabelAtlas.h"
+#import "ccMacros.h"
+#import "CCTransition.h"
+#import "CCScene.h"
+#import "CCSpriteFrameCache.h"
+#import "CCTexture2D.h"
+#import "CCLabelBMFont.h"
+#import "CCLayer.h"
+
+// support imports
+#import "Platforms/CCGL.h"
+#import "Platforms/CCNS.h"
+
+#import "Support/OpenGL_Internal.h"
+#import "Support/CGPointExtension.h"
+
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+#import "Platforms/iOS/CCDirectorIOS.h"
+#define CC_DIRECTOR_DEFAULT CCDirectorTimer
+#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED)
+#import "Platforms/Mac/CCDirectorMac.h"
+#define CC_DIRECTOR_DEFAULT CCDirectorDisplayLink
+#endif
+
+#import "Support/CCProfiling.h"
+
+#define kDefaultFPS            60.0    // 60 frames per second
+
+extern NSString * cocos2dVersion(void);
+
+
+@interface CCDirector (Private)
+-(void) setNextScene;
+// shows the FPS in the screen
+-(void) showFPS;
+// calculates delta time since last time it was called
+-(void) calculateDeltaTime;
+@end
+
+@implementation CCDirector
+
+@synthesize animationInterval = animationInterval_;
+@synthesize runningScene = runningScene_;
+@synthesize displayFPS = displayFPS_;
+@synthesize nextDeltaTimeZero = nextDeltaTimeZero_;
+@synthesize isPaused = isPaused_;
+@synthesize sendCleanupToScene = sendCleanupToScene_;
+@synthesize runningThread = runningThread_;
+@synthesize notificationNode = notificationNode_;
+@synthesize projectionDelegate = projectionDelegate_;
+//
+// singleton stuff
+//
+static CCDirector *_sharedDirector = nil;
+
++ (CCDirector *)sharedDirector
+{
+       if (!_sharedDirector) {
+
+               //
+               // Default Director is TimerDirector
+               // 
+               if( [ [CCDirector class] isEqual:[self class]] )
+                       _sharedDirector = [[CC_DIRECTOR_DEFAULT alloc] init];
+               else
+                       _sharedDirector = [[self alloc] init];
+       }
+               
+       return _sharedDirector;
+}
+
++(id)alloc
+{
+       NSAssert(_sharedDirector == nil, @"Attempted to allocate a second instance of a singleton.");
+       return [super alloc];
+}
+
+- (id) init
+{  
+       CCLOG(@"cocos2d: %@", cocos2dVersion() );
+
+       if( (self=[super init]) ) {
+
+               CCLOG(@"cocos2d: Using Director Type:%@", [self class]);
+               
+               // scenes
+               runningScene_ = nil;
+               nextScene_ = nil;
+               
+               notificationNode_ = nil;
+               
+               oldAnimationInterval_ = animationInterval_ = 1.0 / kDefaultFPS;
+               scenesStack_ = [[NSMutableArray alloc] initWithCapacity:10];
+               
+               // Set default projection (3D)
+               projection_ = kCCDirectorProjectionDefault;
+
+               // projection delegate if "Custom" projection is used
+               projectionDelegate_ = nil;
+
+               // FPS
+               displayFPS_ = NO;
+               frames_ = 0;
+               
+               // paused ?
+               isPaused_ = NO;
+               
+               // running thread
+               runningThread_ = nil;
+               
+               winSizeInPixels_ = winSizeInPoints_ = CGSizeZero;
+       }
+
+       return self;
+}
+
+- (void) dealloc
+{
+       CCLOGINFO(@"cocos2d: deallocing %@", self);
+
+#if CC_DIRECTOR_FAST_FPS
+       [FPSLabel_ release];
+#endif
+       [runningScene_ release];
+       [notificationNode_ release];
+       [scenesStack_ release];
+       
+       [projectionDelegate_ release];
+       
+       _sharedDirector = nil;
+       
+       [super dealloc];
+}
+
+-(void) setGLDefaultValues
+{
+       // This method SHOULD be called only after openGLView_ was initialized
+       NSAssert( openGLView_, @"openGLView_ must be initialized");
+
+       [self setAlphaBlending: YES];
+       [self setDepthTest: YES];
+       [self setProjection: projection_];
+       
+       // set other opengl default values
+       glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
+       
+#if CC_DIRECTOR_FAST_FPS
+    if (!FPSLabel_) {
+               CCTexture2DPixelFormat currentFormat = [CCTexture2D defaultAlphaPixelFormat];
+               [CCTexture2D setDefaultAlphaPixelFormat:kCCTexture2DPixelFormat_RGBA4444];
+               FPSLabel_ = [[CCLabelAtlas labelWithString:@"00.0" charMapFile:@"fps_images.png" itemWidth:16 itemHeight:24 startCharMap:'.'] retain];
+               [CCTexture2D setDefaultAlphaPixelFormat:currentFormat];         
+       }
+#endif // CC_DIRECTOR_FAST_FPS
+}
+
+//
+// Draw the Scene
+//
+- (void) drawScene
+{ 
+       // Override me
+}
+
+-(void) calculateDeltaTime
+{
+       struct timeval now;
+       
+       if( gettimeofday( &now, NULL) != 0 ) {
+               CCLOG(@"cocos2d: error in gettimeofday");
+               dt = 0;
+               return;
+       }
+       
+       // new delta time
+       if( nextDeltaTimeZero_ ) {
+               dt = 0;
+               nextDeltaTimeZero_ = NO;
+       } else {
+               dt = (now.tv_sec - lastUpdate_.tv_sec) + (now.tv_usec - lastUpdate_.tv_usec) / 1000000.0f;
+               dt = MAX(0,dt);
+       }
+
+#ifdef DEBUG
+       // If we are debugging our code, prevent big delta time
+       if( dt > 0.2f )
+               dt = 1/60.0f;
+#endif
+       
+       lastUpdate_ = now;      
+}
+
+#pragma mark Director - Memory Helper
+
+-(void) purgeCachedData
+{
+       [CCLabelBMFont purgeCachedData];        
+       [[CCTextureCache sharedTextureCache] removeUnusedTextures];     
+}
+
+#pragma mark Director - Scene OpenGL Helper
+
+-(ccDirectorProjection) projection
+{
+       return projection_;
+}
+
+-(float) getZEye
+{
+       return ( winSizeInPixels_.height / 1.1566f );
+}
+
+-(void) setProjection:(ccDirectorProjection)projection
+{
+       CCLOG(@"cocos2d: override me");
+}
+
+- (void) setAlphaBlending: (BOOL) on
+{
+       if (on) {
+               glEnable(GL_BLEND);
+               glBlendFunc(CC_BLEND_SRC, CC_BLEND_DST);
+               
+       } else
+               glDisable(GL_BLEND);
+}
+
+- (void) setDepthTest: (BOOL) on
+{
+       if (on) {
+               ccglClearDepth(1.0f);
+               glEnable(GL_DEPTH_TEST);
+               glDepthFunc(GL_LEQUAL);
+//             glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
+       } else
+               glDisable( GL_DEPTH_TEST );
+}
+
+#pragma mark Director Integration with a UIKit view
+
+-(CC_GLVIEW*) openGLView
+{
+       return openGLView_;
+}
+
+-(void) setOpenGLView:(CC_GLVIEW *)view
+{
+       NSAssert( view, @"OpenGLView must be non-nil");
+
+       if( view != openGLView_ ) {
+               [openGLView_ release];
+               openGLView_ = [view retain];
+               
+               // set size
+               winSizeInPixels_ = winSizeInPoints_ = CCNSSizeToCGSize( [view bounds].size );
+
+               [self setGLDefaultValues];
+       }
+}
+
+#pragma mark Director Scene Landscape
+
+-(CGPoint)convertToGL:(CGPoint)uiPoint
+{
+       CCLOG(@"CCDirector#convertToGL: OVERRIDE ME.");
+       return CGPointZero;
+}
+
+-(CGPoint)convertToUI:(CGPoint)glPoint
+{
+       CCLOG(@"CCDirector#convertToUI: OVERRIDE ME.");
+       return CGPointZero;
+}
+
+-(CGSize)winSize
+{
+       return winSizeInPoints_;
+}
+
+-(CGSize)winSizeInPixels
+{
+       return winSizeInPixels_;
+}
+
+-(CGSize)displaySizeInPixels
+{
+       return winSizeInPixels_;
+}
+
+-(void) reshapeProjection:(CGSize)newWindowSize
+{
+       winSizeInPixels_ = winSizeInPoints_ = newWindowSize;
+       [self setProjection:projection_];
+}
+
+#pragma mark Director Scene Management
+
+- (void)runWithScene:(CCScene*) scene
+{
+       NSAssert( scene != nil, @"Argument must be non-nil");
+       NSAssert( runningScene_ == nil, @"You can't run an scene if another Scene is running. Use replaceScene or pushScene instead");
+       
+       [self pushScene:scene];
+       [self startAnimation];  
+}
+
+-(void) replaceScene: (CCScene*) scene
+{
+       NSAssert( scene != nil, @"Argument must be non-nil");
+
+       NSUInteger index = [scenesStack_ count];
+       
+       sendCleanupToScene_ = YES;
+       [scenesStack_ replaceObjectAtIndex:index-1 withObject:scene];
+       nextScene_ = scene;     // nextScene_ is a weak ref
+}
+
+- (void) pushScene: (CCScene*) scene
+{
+       NSAssert( scene != nil, @"Argument must be non-nil");
+
+       sendCleanupToScene_ = NO;
+
+       [scenesStack_ addObject: scene];
+       nextScene_ = scene;     // nextScene_ is a weak ref
+}
+
+-(void) popScene
+{      
+       NSAssert( runningScene_ != nil, @"A running Scene is needed");
+
+       [scenesStack_ removeLastObject];
+       NSUInteger c = [scenesStack_ count];
+       
+       if( c == 0 )
+               [self end];
+       else {
+               sendCleanupToScene_ = YES;
+               nextScene_ = [scenesStack_ objectAtIndex:c-1];
+       }
+}
+
+-(void) end
+{
+       [runningScene_ onExit];
+       [runningScene_ cleanup];
+       [runningScene_ release];
+
+       runningScene_ = nil;
+       nextScene_ = nil;
+       
+       // remove all objects, but don't release it.
+       // runWithScene might be executed after 'end'.
+       [scenesStack_ removeAllObjects];
+       
+       [self stopAnimation];
+       
+#if CC_DIRECTOR_FAST_FPS
+       [FPSLabel_ release];
+       FPSLabel_ = nil;
+#endif 
+
+       [projectionDelegate_ release];
+       projectionDelegate_ = nil;
+       
+       // Purge bitmap cache
+       [CCLabelBMFont purgeCachedData];
+
+       // Purge all managers
+       [CCAnimationCache purgeSharedAnimationCache];
+       [CCSpriteFrameCache purgeSharedSpriteFrameCache];
+       [CCScheduler purgeSharedScheduler];
+       [CCActionManager purgeSharedManager];
+       [CCTextureCache purgeSharedTextureCache];
+       
+       
+       // OpenGL view
+       
+       // Since the director doesn't attach the openglview to the window
+       // it shouldn't remove it from the window too.
+//     [openGLView_ removeFromSuperview];
+
+       [openGLView_ release];
+       openGLView_ = nil;      
+}
+
+-(void) setNextScene
+{
+       Class transClass = [CCTransitionScene class];
+       BOOL runningIsTransition = [runningScene_ isKindOfClass:transClass];
+       BOOL newIsTransition = [nextScene_ isKindOfClass:transClass];
+
+       // If it is not a transition, call onExit/cleanup
+       if( ! newIsTransition ) {
+               [runningScene_ onExit];
+
+               // issue #709. the root node (scene) should receive the cleanup message too
+               // otherwise it might be leaked.
+               if( sendCleanupToScene_)
+                       [runningScene_ cleanup];
+       }
+
+       [runningScene_ release];
+       
+       runningScene_ = [nextScene_ retain];
+       nextScene_ = nil;
+
+       if( ! runningIsTransition ) {
+               [runningScene_ onEnter];
+               [runningScene_ onEnterTransitionDidFinish];
+       }
+}
+
+-(void) pause
+{
+       if( isPaused_ )
+               return;
+
+       oldAnimationInterval_ = animationInterval_;
+       
+       // when paused, don't consume CPU
+       [self setAnimationInterval:1/4.0];
+       isPaused_ = YES;
+}
+
+-(void) resume
+{
+       if( ! isPaused_ )
+               return;
+       
+       [self setAnimationInterval: oldAnimationInterval_];
+
+       if( gettimeofday( &lastUpdate_, NULL) != 0 ) {
+               CCLOG(@"cocos2d: Director: Error in gettimeofday");
+       }
+       
+       isPaused_ = NO;
+       dt = 0;
+}
+
+- (void)startAnimation
+{
+       CCLOG(@"cocos2d: Director#startAnimation. Override me");
+}
+
+- (void)stopAnimation
+{
+       CCLOG(@"cocos2d: Director#stopAnimation. Override me");
+}
+
+- (void)setAnimationInterval:(NSTimeInterval)interval
+{
+       CCLOG(@"cocos2d: Director#setAnimationInterval. Override me");
+}
+
+#if CC_DIRECTOR_FAST_FPS
+
+// display the FPS using a LabelAtlas
+// updates the FPS every frame
+-(void) showFPS
+{
+       frames_++;
+       accumDt_ += dt;
+       
+       if ( accumDt_ > CC_DIRECTOR_FPS_INTERVAL)  {
+               frameRate_ = frames_/accumDt_;
+               frames_ = 0;
+               accumDt_ = 0;
+
+//             sprintf(format,"%.1f",frameRate);
+//             [FPSLabel setCString:format];
+
+               NSString *str = [[NSString alloc] initWithFormat:@"%.1f", frameRate_];
+               [FPSLabel_ setString:str];
+               [str release];
+       }
+
+       [FPSLabel_ draw];
+}
+#else
+// display the FPS using a manually generated Texture (very slow)
+// updates the FPS 3 times per second aprox.
+-(void) showFPS
+{
+       frames_++;
+       accumDt_ += dt;
+       
+       if ( accumDt_ > CC_DIRECTOR_FPS_INTERVAL)  {
+               frameRate_ = frames_/accumDt_;
+               frames_ = 0;
+               accumDt_ = 0;
+       }
+       
+       NSString *str = [NSString stringWithFormat:@"%.2f",frameRate_];
+       CCTexture2D *texture = [[CCTexture2D alloc] initWithString:str dimensions:CGSizeMake(100,30) alignment:CCTextAlignmentLeft fontName:@"Arial" fontSize:24];
+
+       // Default GL states: GL_TEXTURE_2D, GL_VERTEX_ARRAY, GL_COLOR_ARRAY, GL_TEXTURE_COORD_ARRAY
+       // Needed states: GL_TEXTURE_2D, GL_VERTEX_ARRAY, GL_TEXTURE_COORD_ARRAY
+       // Unneeded states: GL_COLOR_ARRAY
+       glDisableClientState(GL_COLOR_ARRAY);
+       
+       glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+       
+       glColor4ub(224,224,244,200);
+       [texture drawAtPoint: ccp(5,2)];
+       [texture release];
+       
+       glBlendFunc(CC_BLEND_SRC, CC_BLEND_DST);
+       
+       // restore default GL state
+       glEnableClientState(GL_COLOR_ARRAY);
+}
+#endif
+
+- (void) showProfilers {
+#if CC_ENABLE_PROFILERS
+       accumDtForProfiler_ += dt;
+       if (accumDtForProfiler_ > 1.0f) {
+               accumDtForProfiler_ = 0;
+               [[CCProfiler sharedProfiler] displayTimers];
+       }
+#endif // CC_ENABLE_PROFILERS
+}
+
+@end
+
diff --git a/libs/cocos2d/CCDrawingPrimitives.h b/libs/cocos2d/CCDrawingPrimitives.h
new file mode 100644 (file)
index 0000000..8d1dbe5
--- /dev/null
@@ -0,0 +1,92 @@
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2008-2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+
+#ifndef __CC_DRAWING_PRIMITIVES_H
+#define __CC_DRAWING_PRIMITIVES_H
+
+#import <Availability.h>
+#import <Foundation/Foundation.h>
+
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+#import <CoreGraphics/CGGeometry.h>    // for CGPoint
+#endif
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif 
+       
+/**
+ @file
+ Drawing OpenGL ES primitives.
+  - ccDrawPoint
+  - ccDrawLine
+  - ccDrawPoly
+  - ccDrawCircle
+  - ccDrawQuadBezier
+  - ccDrawCubicBezier
+
+ You can change the color, width and other property by calling the
+   glColor4ub(), glLineWidth(), glPointSize().
+ @warning These functions draws the Line, Point, Polygon, immediately. They aren't batched. If you are going to make a game that depends on these primitives, I suggest creating a batch.
+ */
+       
+
+/** draws a point given x and y coordinate measured in points. */
+void ccDrawPoint( CGPoint point );
+
+/** draws an array of points.
+ @since v0.7.2
+ */
+void ccDrawPoints( const CGPoint *points, NSUInteger numberOfPoints );
+
+/** draws a line given the origin and destination point measured in points. */
+void ccDrawLine( CGPoint origin, CGPoint destination );
+
+/** draws a poligon given a pointer to CGPoint coordiantes and the number of vertices measured in points.
+ The polygon can be closed or open
+ */
+void ccDrawPoly( const CGPoint *vertices, NSUInteger numOfVertices, BOOL closePolygon );
+
+/** draws a circle given the center, radius and number of segments measured in points */
+void ccDrawCircle( CGPoint center, float radius, float angle, NSUInteger segments, BOOL drawLineToCenter);
+
+/** draws a quad bezier path measured in points.
+ @since v0.8
+ */
+void ccDrawQuadBezier(CGPoint origin, CGPoint control, CGPoint destination, NSUInteger segments);
+
+/** draws a cubic bezier path measured in points.
+ @since v0.8
+ */
+void ccDrawCubicBezier(CGPoint origin, CGPoint control1, CGPoint control2, CGPoint destination, NSUInteger segments);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif //  __CC_DRAWING_PRIMITIVES_H
diff --git a/libs/cocos2d/CCDrawingPrimitives.m b/libs/cocos2d/CCDrawingPrimitives.m
new file mode 100644 (file)
index 0000000..f7df2b6
--- /dev/null
@@ -0,0 +1,272 @@
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2008-2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#import <math.h>
+#import <stdlib.h>
+#import <string.h>
+
+#import "CCDrawingPrimitives.h"
+#import "ccTypes.h"
+#import "ccMacros.h"
+#import "Platforms/CCGL.h"
+
+void ccDrawPoint( CGPoint point )
+{
+       ccVertex2F p = (ccVertex2F) {point.x * CC_CONTENT_SCALE_FACTOR(), point.y * CC_CONTENT_SCALE_FACTOR() };
+       
+       // Default GL states: GL_TEXTURE_2D, GL_VERTEX_ARRAY, GL_COLOR_ARRAY, GL_TEXTURE_COORD_ARRAY
+       // Needed states: GL_VERTEX_ARRAY, 
+       // Unneeded states: GL_TEXTURE_2D, GL_TEXTURE_COORD_ARRAY, GL_COLOR_ARRAY       
+       glDisable(GL_TEXTURE_2D);
+       glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+       glDisableClientState(GL_COLOR_ARRAY);
+       
+       glVertexPointer(2, GL_FLOAT, 0, &p);    
+       glDrawArrays(GL_POINTS, 0, 1);
+
+       // restore default state
+       glEnableClientState(GL_COLOR_ARRAY);
+       glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+       glEnable(GL_TEXTURE_2D);        
+}
+
+void ccDrawPoints( const CGPoint *points, NSUInteger numberOfPoints )
+{
+       // Default GL states: GL_TEXTURE_2D, GL_VERTEX_ARRAY, GL_COLOR_ARRAY, GL_TEXTURE_COORD_ARRAY
+       // Needed states: GL_VERTEX_ARRAY, 
+       // Unneeded states: GL_TEXTURE_2D, GL_TEXTURE_COORD_ARRAY, GL_COLOR_ARRAY       
+       glDisable(GL_TEXTURE_2D);
+       glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+       glDisableClientState(GL_COLOR_ARRAY);
+
+       ccVertex2F newPoints[numberOfPoints];
+
+       // iPhone and 32-bit machines optimization
+       if( sizeof(CGPoint) == sizeof(ccVertex2F) ) {
+
+               // points ?
+               if( CC_CONTENT_SCALE_FACTOR() != 1 ) {
+                       for( NSUInteger i=0; i<numberOfPoints;i++)
+                               newPoints[i] =  (ccVertex2F){ points[i].x * CC_CONTENT_SCALE_FACTOR(), points[i].y * CC_CONTENT_SCALE_FACTOR() };
+
+                       glVertexPointer(2, GL_FLOAT, 0, newPoints);
+
+               } else
+                       glVertexPointer(2, GL_FLOAT, 0, points);
+               
+               glDrawArrays(GL_POINTS, 0, (GLsizei) numberOfPoints);
+               
+       } else {
+               
+               // Mac on 64-bit
+               for( NSUInteger i=0; i<numberOfPoints;i++)
+                       newPoints[i] = (ccVertex2F) { points[i].x, points[i].y };
+                       
+               glVertexPointer(2, GL_FLOAT, 0, newPoints);
+               glDrawArrays(GL_POINTS, 0, (GLsizei) numberOfPoints);
+
+       }
+
+       
+       // restore default state
+       glEnableClientState(GL_COLOR_ARRAY);
+       glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+       glEnable(GL_TEXTURE_2D);        
+}
+
+
+void ccDrawLine( CGPoint origin, CGPoint destination )
+{
+       ccVertex2F vertices[2] = {
+               {origin.x * CC_CONTENT_SCALE_FACTOR(), origin.y * CC_CONTENT_SCALE_FACTOR() },
+               {destination.x * CC_CONTENT_SCALE_FACTOR(), destination.y * CC_CONTENT_SCALE_FACTOR() }
+       };
+
+       // Default GL states: GL_TEXTURE_2D, GL_VERTEX_ARRAY, GL_COLOR_ARRAY, GL_TEXTURE_COORD_ARRAY
+       // Needed states: GL_VERTEX_ARRAY, 
+       // Unneeded states: GL_TEXTURE_2D, GL_TEXTURE_COORD_ARRAY, GL_COLOR_ARRAY       
+       glDisable(GL_TEXTURE_2D);
+       glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+       glDisableClientState(GL_COLOR_ARRAY);
+       
+       glVertexPointer(2, GL_FLOAT, 0, vertices);      
+       glDrawArrays(GL_LINES, 0, 2);
+       
+       // restore default state
+       glEnableClientState(GL_COLOR_ARRAY);
+       glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+       glEnable(GL_TEXTURE_2D);                
+}
+
+
+void ccDrawPoly( const CGPoint *poli, NSUInteger numberOfPoints, BOOL closePolygon )
+{
+       ccVertex2F newPoint[numberOfPoints];
+
+       // Default GL states: GL_TEXTURE_2D, GL_VERTEX_ARRAY, GL_COLOR_ARRAY, GL_TEXTURE_COORD_ARRAY
+       // Needed states: GL_VERTEX_ARRAY, 
+       // Unneeded states: GL_TEXTURE_2D, GL_TEXTURE_COORD_ARRAY, GL_COLOR_ARRAY       
+       glDisable(GL_TEXTURE_2D);
+       glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+       glDisableClientState(GL_COLOR_ARRAY);
+
+       
+       // iPhone and 32-bit machines
+       if( sizeof(CGPoint) == sizeof(ccVertex2F) ) {
+
+               // convert to pixels ?
+               if( CC_CONTENT_SCALE_FACTOR() != 1 ) {
+                       memcpy( newPoint, poli, numberOfPoints * sizeof(ccVertex2F) );
+                       for( NSUInteger i=0; i<numberOfPoints;i++)
+                               newPoint[i] = (ccVertex2F) { poli[i].x * CC_CONTENT_SCALE_FACTOR(), poli[i].y * CC_CONTENT_SCALE_FACTOR() };
+
+                       glVertexPointer(2, GL_FLOAT, 0, newPoint);
+
+               } else
+                       glVertexPointer(2, GL_FLOAT, 0, poli);
+
+               
+       } else {
+               // 64-bit machines (Mac)
+               
+               for( NSUInteger i=0; i<numberOfPoints;i++)
+                       newPoint[i] = (ccVertex2F) { poli[i].x, poli[i].y };
+
+               glVertexPointer(2, GL_FLOAT, 0, newPoint );
+
+       }
+               
+       if( closePolygon )
+               glDrawArrays(GL_LINE_LOOP, 0, (GLsizei) numberOfPoints);
+       else
+               glDrawArrays(GL_LINE_STRIP, 0, (GLsizei) numberOfPoints);
+       
+       // restore default state
+       glEnableClientState(GL_COLOR_ARRAY);
+       glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+       glEnable(GL_TEXTURE_2D);        
+}
+
+void ccDrawCircle( CGPoint center, float r, float a, NSUInteger segs, BOOL drawLineToCenter)
+{
+       int additionalSegment = 1;
+       if (drawLineToCenter)
+               additionalSegment++;
+
+       const float coef = 2.0f * (float)M_PI/segs;
+       
+       GLfloat *vertices = calloc( sizeof(GLfloat)*2*(segs+2), 1);
+       if( ! vertices )
+               return;
+
+       for(NSUInteger i=0;i<=segs;i++)
+       {
+               float rads = i*coef;
+               GLfloat j = r * cosf(rads + a) + center.x;
+               GLfloat k = r * sinf(rads + a) + center.y;
+               
+               vertices[i*2] = j * CC_CONTENT_SCALE_FACTOR();
+               vertices[i*2+1] =k * CC_CONTENT_SCALE_FACTOR();
+       }
+       vertices[(segs+1)*2] = center.x * CC_CONTENT_SCALE_FACTOR();
+       vertices[(segs+1)*2+1] = center.y * CC_CONTENT_SCALE_FACTOR();
+       
+       // Default GL states: GL_TEXTURE_2D, GL_VERTEX_ARRAY, GL_COLOR_ARRAY, GL_TEXTURE_COORD_ARRAY
+       // Needed states: GL_VERTEX_ARRAY, 
+       // Unneeded states: GL_TEXTURE_2D, GL_TEXTURE_COORD_ARRAY, GL_COLOR_ARRAY       
+       glDisable(GL_TEXTURE_2D);
+       glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+       glDisableClientState(GL_COLOR_ARRAY);
+       
+       glVertexPointer(2, GL_FLOAT, 0, vertices);      
+       glDrawArrays(GL_LINE_STRIP, 0, (GLsizei) segs+additionalSegment);
+       
+       // restore default state
+       glEnableClientState(GL_COLOR_ARRAY);
+       glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+       glEnable(GL_TEXTURE_2D);        
+       
+       free( vertices );
+}
+
+void ccDrawQuadBezier(CGPoint origin, CGPoint control, CGPoint destination, NSUInteger segments)
+{
+       ccVertex2F vertices[segments + 1];
+       
+       float t = 0.0f;
+       for(NSUInteger i = 0; i < segments; i++)
+       {
+               GLfloat x = powf(1 - t, 2) * origin.x + 2.0f * (1 - t) * t * control.x + t * t * destination.x;
+               GLfloat y = powf(1 - t, 2) * origin.y + 2.0f * (1 - t) * t * control.y + t * t * destination.y;
+               vertices[i] = (ccVertex2F) {x * CC_CONTENT_SCALE_FACTOR(), y * CC_CONTENT_SCALE_FACTOR() };
+               t += 1.0f / segments;
+       }
+       vertices[segments] = (ccVertex2F) {destination.x * CC_CONTENT_SCALE_FACTOR(), destination.y * CC_CONTENT_SCALE_FACTOR() };
+       
+       // Default GL states: GL_TEXTURE_2D, GL_VERTEX_ARRAY, GL_COLOR_ARRAY, GL_TEXTURE_COORD_ARRAY
+       // Needed states: GL_VERTEX_ARRAY, 
+       // Unneeded states: GL_TEXTURE_2D, GL_TEXTURE_COORD_ARRAY, GL_COLOR_ARRAY       
+       glDisable(GL_TEXTURE_2D);
+       glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+       glDisableClientState(GL_COLOR_ARRAY);
+       
+       glVertexPointer(2, GL_FLOAT, 0, vertices);      
+       glDrawArrays(GL_LINE_STRIP, 0, (GLsizei) segments + 1);
+       
+       // restore default state
+       glEnableClientState(GL_COLOR_ARRAY);
+       glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+       glEnable(GL_TEXTURE_2D);        
+}
+
+void ccDrawCubicBezier(CGPoint origin, CGPoint control1, CGPoint control2, CGPoint destination, NSUInteger segments)
+{
+       ccVertex2F vertices[segments + 1];
+       
+       float t = 0;
+       for(NSUInteger i = 0; i < segments; i++)
+       {
+               GLfloat x = powf(1 - t, 3) * origin.x + 3.0f * powf(1 - t, 2) * t * control1.x + 3.0f * (1 - t) * t * t * control2.x + t * t * t * destination.x;
+               GLfloat y = powf(1 - t, 3) * origin.y + 3.0f * powf(1 - t, 2) * t * control1.y + 3.0f * (1 - t) * t * t * control2.y + t * t * t * destination.y;
+               vertices[i] = (ccVertex2F) {x * CC_CONTENT_SCALE_FACTOR(), y * CC_CONTENT_SCALE_FACTOR() };
+               t += 1.0f / segments;
+       }
+       vertices[segments] = (ccVertex2F) {destination.x * CC_CONTENT_SCALE_FACTOR(), destination.y * CC_CONTENT_SCALE_FACTOR() };
+       
+       // Default GL states: GL_TEXTURE_2D, GL_VERTEX_ARRAY, GL_COLOR_ARRAY, GL_TEXTURE_COORD_ARRAY
+       // Needed states: GL_VERTEX_ARRAY, 
+       // Unneeded states: GL_TEXTURE_2D, GL_TEXTURE_COORD_ARRAY, GL_COLOR_ARRAY       
+       glDisable(GL_TEXTURE_2D);
+       glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+       glDisableClientState(GL_COLOR_ARRAY);
+       
+       glVertexPointer(2, GL_FLOAT, 0, vertices);      
+       glDrawArrays(GL_LINE_STRIP, 0, (GLsizei) segments + 1);
+       
+       // restore default state
+       glEnableClientState(GL_COLOR_ARRAY);
+       glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+       glEnable(GL_TEXTURE_2D);        
+}
diff --git a/libs/cocos2d/CCGrabber.h b/libs/cocos2d/CCGrabber.h
new file mode 100644 (file)
index 0000000..f1ce6cb
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2009 On-Core
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+
+#import "Platforms/CCGL.h"
+#import <Foundation/Foundation.h>
+
+@class CCTexture2D;
+
+/** FBO class that grabs the the contents of the screen */
+@interface CCGrabber : NSObject
+{
+       GLuint  fbo;
+       GLint   oldFBO;
+}
+
+-(void)grab:(CCTexture2D*)texture;
+-(void)beforeRender:(CCTexture2D*)texture;
+-(void)afterRender:(CCTexture2D*)texture;
+
+@end
diff --git a/libs/cocos2d/CCGrabber.m b/libs/cocos2d/CCGrabber.m
new file mode 100644 (file)
index 0000000..a259091
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2009 On-Core
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+
+#import "Platforms/CCGL.h"
+#import "CCGrabber.h"
+#import "ccMacros.h"
+#import "CCTexture2D.h"
+#import "Support/OpenGL_Internal.h"
+
+@implementation CCGrabber
+
+-(id) init
+{
+       if(( self = [super init] )) {
+               // generate FBO
+               ccglGenFramebuffers(1, &fbo);           
+       }
+       return self;
+}
+
+-(void)grab:(CCTexture2D*)texture
+{
+       glGetIntegerv(CC_GL_FRAMEBUFFER_BINDING, &oldFBO);
+       
+       // bind
+       ccglBindFramebuffer(CC_GL_FRAMEBUFFER, fbo);
+
+       // associate texture with FBO
+       ccglFramebufferTexture2D(CC_GL_FRAMEBUFFER, CC_GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture.name, 0);
+       
+       // check if it worked (probably worth doing :) )
+       GLuint status = ccglCheckFramebufferStatus(CC_GL_FRAMEBUFFER);
+       if (status != CC_GL_FRAMEBUFFER_COMPLETE)
+               [NSException raise:@"Frame Grabber" format:@"Could not attach texture to framebuffer"];
+       
+       ccglBindFramebuffer(CC_GL_FRAMEBUFFER, oldFBO);
+}
+
+-(void)beforeRender:(CCTexture2D*)texture
+{
+       glGetIntegerv(CC_GL_FRAMEBUFFER_BINDING, &oldFBO);
+       ccglBindFramebuffer(CC_GL_FRAMEBUFFER, fbo);
+
+       // BUG XXX: doesn't work with RGB565.
+
+
+       glClearColor(0,0,0,0);
+       
+       // BUG #631: To fix #631, uncomment the lines with #631
+       // Warning: But it CCGrabber won't work with 2 effects at the same time
+//     glClearColor(0.0f,0.0f,0.0f,1.0f);      // #631
+       
+       glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);     
+       
+//     glColorMask(TRUE, TRUE, TRUE, FALSE);   // #631
+
+}
+
+-(void)afterRender:(CCTexture2D*)texture
+{
+       ccglBindFramebuffer(CC_GL_FRAMEBUFFER, oldFBO);
+//     glColorMask(TRUE, TRUE, TRUE, TRUE);    // #631
+}
+
+- (void) dealloc
+{
+       CCLOGINFO(@"cocos2d: deallocing %@", self);
+       ccglDeleteFramebuffers(1, &fbo);
+       [super dealloc];
+}
+
+@end
diff --git a/libs/cocos2d/CCGrid.h b/libs/cocos2d/CCGrid.h
new file mode 100644 (file)
index 0000000..e5e77e8
--- /dev/null
@@ -0,0 +1,121 @@
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2009 On-Core
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+
+#import <Foundation/Foundation.h>
+
+#import "CCNode.h"
+#import "CCCamera.h"
+#import "ccTypes.h"
+
+@class CCTexture2D;
+@class CCGrabber;
+
+/** Base class for other
+ */
+@interface CCGridBase : NSObject
+{
+       BOOL            active_;
+       int                     reuseGrid_;
+       ccGridSize      gridSize_;
+       CCTexture2D *texture_;
+       CGPoint         step_;
+       CCGrabber       *grabber_;
+       BOOL            isTextureFlipped_;
+}
+
+/** wheter or not the grid is active */
+@property (nonatomic,readwrite) BOOL active;
+/** number of times that the grid will be reused */
+@property (nonatomic,readwrite) int reuseGrid;
+/** size of the grid */
+@property (nonatomic,readonly) ccGridSize gridSize;
+/** pixels between the grids */
+@property (nonatomic,readwrite) CGPoint step;
+/** texture used */
+@property (nonatomic, retain) CCTexture2D *texture;
+/** grabber used */
+@property (nonatomic, retain) CCGrabber *grabber;
+/** is texture flipped */
+@property (nonatomic, readwrite) BOOL isTextureFlipped;
+
++(id) gridWithSize:(ccGridSize)gridSize texture:(CCTexture2D*)texture flippedTexture:(BOOL)flipped;
++(id) gridWithSize:(ccGridSize)gridSize;
+
+-(id) initWithSize:(ccGridSize)gridSize texture:(CCTexture2D*)texture flippedTexture:(BOOL)flipped;
+-(id)initWithSize:(ccGridSize)gridSize;
+-(void)beforeDraw;
+-(void)afterDraw:(CCNode*)target;
+-(void)blit;
+-(void)reuse;
+
+-(void)calculateVertexPoints;
+
+@end
+
+////////////////////////////////////////////////////////////
+
+/**
+ CCGrid3D is a 3D grid implementation. Each vertex has 3 dimensions: x,y,z
+ */
+@interface CCGrid3D : CCGridBase
+{
+       GLvoid          *texCoordinates;
+       GLvoid          *vertices;
+       GLvoid          *originalVertices;
+       GLushort        *indices;
+}
+
+/** returns the vertex at a given position */
+-(ccVertex3F)vertex:(ccGridSize)pos;
+/** returns the original (non-transformed) vertex at a given position */
+-(ccVertex3F)originalVertex:(ccGridSize)pos;
+/** sets a new vertex at a given position */
+-(void)setVertex:(ccGridSize)pos vertex:(ccVertex3F)vertex;
+
+@end
+
+////////////////////////////////////////////////////////////
+
+/**
+ CCTiledGrid3D is a 3D grid implementation. It differs from Grid3D in that
+ the tiles can be separated from the grid.
+*/
+@interface CCTiledGrid3D : CCGridBase
+{
+       GLvoid          *texCoordinates;
+       GLvoid          *vertices;
+       GLvoid          *originalVertices;
+       GLushort        *indices;
+}
+
+/** returns the tile at the given position */
+-(ccQuad3)tile:(ccGridSize)pos;
+/** returns the original tile (untransformed) at the given position */
+-(ccQuad3)originalTile:(ccGridSize)pos;
+/** sets a new tile */
+-(void)setTile:(ccGridSize)pos coords:(ccQuad3)coords;
+
+@end
diff --git a/libs/cocos2d/CCGrid.m b/libs/cocos2d/CCGrid.m
new file mode 100644 (file)
index 0000000..c2ed19d
--- /dev/null
@@ -0,0 +1,571 @@
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2009 On-Core
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+
+#import <Availability.h>
+
+#import "ccMacros.h"
+#import "CCGrid.h"
+#import "CCTexture2D.h"
+#import "CCDirector.h"
+#import "CCGrabber.h"
+
+#import "Platforms/CCGL.h"
+#import "Support/CGPointExtension.h"
+#import "Support/ccUtils.h"
+
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+#import "Platforms/iOS/CCDirectorIOS.h"
+#endif // __IPHONE_OS_VERSION_MAX_ALLOWED
+
+#pragma mark -
+#pragma mark CCGridBase
+
+@implementation CCGridBase
+
+@synthesize reuseGrid = reuseGrid_;
+@synthesize texture = texture_;
+@synthesize grabber = grabber_;
+@synthesize gridSize = gridSize_;
+@synthesize step = step_;
+
++(id) gridWithSize:(ccGridSize)gridSize texture:(CCTexture2D*)texture flippedTexture:(BOOL)flipped
+{
+       return [[[self alloc] initWithSize:gridSize texture:texture flippedTexture:flipped] autorelease];
+}
+
++(id) gridWithSize:(ccGridSize)gridSize
+{
+       return [[(CCGridBase*)[self alloc] initWithSize:gridSize] autorelease];
+}
+
+-(id) initWithSize:(ccGridSize)gridSize texture:(CCTexture2D*)texture flippedTexture:(BOOL)flipped
+{
+       if( (self=[super init]) ) {
+               
+               active_ = NO;
+               reuseGrid_ = 0;
+               gridSize_ = gridSize;
+
+               self.texture = texture;
+               isTextureFlipped_ = flipped;
+               
+               CGSize texSize = [texture_ contentSizeInPixels];
+               step_.x = texSize.width / gridSize_.x;
+               step_.y = texSize.height / gridSize_.y;
+               
+               grabber_ = [[CCGrabber alloc] init];
+               [grabber_ grab:texture_];
+               
+               [self calculateVertexPoints];
+       }
+       return self;
+}
+
+-(id)initWithSize:(ccGridSize)gSize
+{
+       CCDirector *director = [CCDirector sharedDirector];
+       CGSize s = [director winSizeInPixels];
+       
+       unsigned long POTWide = ccNextPOT(s.width);
+       unsigned long POTHigh = ccNextPOT(s.height);
+       
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+       EAGLView *glview = [[CCDirector sharedDirector] openGLView];
+       NSString *pixelFormat = [glview pixelFormat];
+
+       CCTexture2DPixelFormat format = [pixelFormat isEqualToString: kEAGLColorFormatRGB565] ? kCCTexture2DPixelFormat_RGB565 : kCCTexture2DPixelFormat_RGBA8888;
+#else
+       CCTexture2DPixelFormat format = kCCTexture2DPixelFormat_RGBA8888;
+#endif
+       
+       void *data = calloc((int)(POTWide * POTHigh * 4), 1);
+       if( ! data ) {
+               CCLOG(@"cocos2d: CCGrid: not enough memory");
+               [self release];
+               return nil;
+       }
+       
+       CCTexture2D *texture = [[CCTexture2D alloc] initWithData:data pixelFormat:format pixelsWide:POTWide pixelsHigh:POTHigh contentSize:s];
+       free( data );
+
+       if( ! texture ) {
+               CCLOG(@"cocos2d: CCGrid: error creating texture");
+               [self release];
+               return nil;
+       }
+       
+       self = [self initWithSize:gSize texture:texture flippedTexture:NO];
+       
+       [texture release];
+       
+       return self;
+}
+- (NSString*) description
+{
+       return [NSString stringWithFormat:@"<%@ = %08X | Dimensions = %ix%i>", [self class], self, gridSize_.x, gridSize_.y];
+}
+
+- (void) dealloc
+{
+       CCLOGINFO(@"cocos2d: deallocing %@", self);
+
+       [self setActive: NO];
+
+       [texture_ release];
+       [grabber_ release];
+       [super dealloc];
+}
+
+// properties
+-(BOOL) active
+{
+       return active_;
+}
+
+-(void) setActive:(BOOL)active
+{
+       active_ = active;
+       if( ! active ) {
+               CCDirector *director = [CCDirector sharedDirector];
+               ccDirectorProjection proj = [director projection];
+               [director setProjection:proj];
+       }
+}
+
+-(BOOL) isTextureFlipped
+{
+       return isTextureFlipped_;
+}
+
+-(void) setIsTextureFlipped:(BOOL)flipped
+{
+       if( isTextureFlipped_ != flipped ) {
+               isTextureFlipped_ = flipped;
+               [self calculateVertexPoints];
+       }
+}
+
+// This routine can be merged with Director
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+-(void)applyLandscape
+{
+       CCDirector *director = [CCDirector sharedDirector];
+       
+       CGSize winSize = [director displaySizeInPixels];
+       float w = winSize.width / 2;
+       float h = winSize.height / 2;
+
+       ccDeviceOrientation orientation  = [director deviceOrientation];
+
+       switch (orientation) {
+               case CCDeviceOrientationLandscapeLeft:
+                       glTranslatef(w,h,0);
+                       glRotatef(-90,0,0,1);
+                       glTranslatef(-h,-w,0);
+                       break;
+               case CCDeviceOrientationLandscapeRight:
+                       glTranslatef(w,h,0);
+                       glRotatef(90,0,0,1);
+                       glTranslatef(-h,-w,0);
+                       break;
+               case CCDeviceOrientationPortraitUpsideDown:
+                       glTranslatef(w,h,0);
+                       glRotatef(180,0,0,1);
+                       glTranslatef(-w,-h,0);
+                       break;
+               default:
+                       break;
+       }
+}
+#endif
+
+-(void)set2DProjection
+{
+       CGSize  winSize = [[CCDirector sharedDirector] winSizeInPixels];
+       
+       glLoadIdentity();
+       glViewport(0, 0, winSize.width, winSize.height);
+       glMatrixMode(GL_PROJECTION);
+       glLoadIdentity();
+       ccglOrtho(0, winSize.width, 0, winSize.height, -1024, 1024);
+       glMatrixMode(GL_MODELVIEW);
+}
+
+// This routine can be merged with Director
+-(void)set3DProjection
+{
+       CCDirector *director = [CCDirector sharedDirector];
+       
+       CGSize  winSize = [director displaySizeInPixels];
+       
+       glViewport(0, 0, winSize.width, winSize.height);
+       glMatrixMode(GL_PROJECTION);
+       glLoadIdentity();
+       gluPerspective(60, (GLfloat)winSize.width/winSize.height, 0.5f, 1500.0f);
+       
+       glMatrixMode(GL_MODELVIEW);     
+       glLoadIdentity();
+       gluLookAt( winSize.width/2, winSize.height/2, [director getZEye],
+                         winSize.width/2, winSize.height/2, 0,
+                         0.0f, 1.0f, 0.0f
+                         );
+}
+
+-(void)beforeDraw
+{
+       [self set2DProjection];
+       [grabber_ beforeRender:texture_];
+}
+
+-(void)afterDraw:(CCNode *)target
+{
+       [grabber_ afterRender:texture_];
+       
+       [self set3DProjection];
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+       [self applyLandscape];
+#endif
+
+       if( target.camera.dirty ) {
+
+               CGPoint offset = [target anchorPointInPixels];
+
+               //
+               // XXX: Camera should be applied in the AnchorPoint
+               //
+               ccglTranslate(offset.x, offset.y, 0);
+               [target.camera locate];
+               ccglTranslate(-offset.x, -offset.y, 0);
+       }
+               
+       glBindTexture(GL_TEXTURE_2D, texture_.name);
+
+       [self blit];
+}
+
+-(void)blit
+{
+       [NSException raise:@"GridBase" format:@"Abstract class needs implementation"];
+}
+
+-(void)reuse
+{
+       [NSException raise:@"GridBase" format:@"Abstract class needs implementation"];
+}
+
+-(void)calculateVertexPoints
+{
+       [NSException raise:@"GridBase" format:@"Abstract class needs implementation"];
+}
+
+@end
+
+////////////////////////////////////////////////////////////
+
+#pragma mark -
+#pragma mark CCGrid3D
+@implementation CCGrid3D
+
+-(void)dealloc
+{
+       free(texCoordinates);
+       free(vertices);
+       free(indices);
+       free(originalVertices);
+       [super dealloc];
+}
+
+-(void)blit
+{
+       NSInteger n = gridSize_.x * gridSize_.y;
+       
+       // Default GL states: GL_TEXTURE_2D, GL_VERTEX_ARRAY, GL_COLOR_ARRAY, GL_TEXTURE_COORD_ARRAY
+       // Needed states: GL_TEXTURE_2D, GL_VERTEX_ARRAY, GL_TEXTURE_COORD_ARRAY
+       // Unneeded states: GL_COLOR_ARRAY
+       glDisableClientState(GL_COLOR_ARRAY);   
+       
+       glVertexPointer(3, GL_FLOAT, 0, vertices);
+       glTexCoordPointer(2, GL_FLOAT, 0, texCoordinates);
+       glDrawElements(GL_TRIANGLES, (GLsizei) n*6, GL_UNSIGNED_SHORT, indices);
+       
+       // restore GL default state
+       glEnableClientState(GL_COLOR_ARRAY);
+}
+
+-(void)calculateVertexPoints
+{
+       float width = (float)texture_.pixelsWide;
+       float height = (float)texture_.pixelsHigh;
+       float imageH = texture_.contentSizeInPixels.height;
+       
+       int x, y, i;
+       
+       vertices = malloc((gridSize_.x+1)*(gridSize_.y+1)*sizeof(ccVertex3F));
+       originalVertices = malloc((gridSize_.x+1)*(gridSize_.y+1)*sizeof(ccVertex3F));
+       texCoordinates = malloc((gridSize_.x+1)*(gridSize_.y+1)*sizeof(CGPoint));
+       indices = malloc(gridSize_.x*gridSize_.y*sizeof(GLushort)*6);
+       
+       float *vertArray = (float*)vertices;
+       float *texArray = (float*)texCoordinates;
+       GLushort *idxArray = (GLushort *)indices;
+       
+       for( x = 0; x < gridSize_.x; x++ )
+       {
+               for( y = 0; y < gridSize_.y; y++ )
+               {
+                       NSInteger idx = (y * gridSize_.x) + x;
+                       
+                       float x1 = x * step_.x;
+                       float x2 = x1 + step_.x;
+                       float y1 = y * step_.y;
+                       float y2 = y1 + step_.y;
+                       
+                       GLushort a = x * (gridSize_.y+1) + y;
+                       GLushort b = (x+1) * (gridSize_.y+1) + y;
+                       GLushort c = (x+1) * (gridSize_.y+1) + (y+1);
+                       GLushort d = x * (gridSize_.y+1) + (y+1);
+                       
+                       GLushort        tempidx[6] = { a, b, d, b, c, d };
+                       
+                       memcpy(&idxArray[6*idx], tempidx, 6*sizeof(GLushort));
+                       
+                       int l1[4] = { a*3, b*3, c*3, d*3 };
+                       ccVertex3F      e = {x1,y1,0};
+                       ccVertex3F      f = {x2,y1,0};
+                       ccVertex3F      g = {x2,y2,0};
+                       ccVertex3F      h = {x1,y2,0};
+                       
+                       ccVertex3F l2[4] = { e, f, g, h };
+                       
+                       int tex1[4] = { a*2, b*2, c*2, d*2 };
+                       CGPoint tex2[4] = { ccp(x1, y1), ccp(x2, y1), ccp(x2, y2), ccp(x1, y2) };
+                       
+                       for( i = 0; i < 4; i++ )
+                       {
+                               vertArray[ l1[i] ] = l2[i].x;
+                               vertArray[ l1[i] + 1 ] = l2[i].y;
+                               vertArray[ l1[i] + 2 ] = l2[i].z;
+                               
+                               texArray[ tex1[i] ] = tex2[i].x / width;
+                               if( isTextureFlipped_ )
+                                       texArray[ tex1[i] + 1 ] = (imageH - tex2[i].y) / height;
+                               else
+                                       texArray[ tex1[i] + 1 ] = tex2[i].y / height;
+                       }
+               }
+       }
+       
+       memcpy(originalVertices, vertices, (gridSize_.x+1)*(gridSize_.y+1)*sizeof(ccVertex3F));
+}
+
+-(ccVertex3F)vertex:(ccGridSize)pos
+{
+       NSInteger index = (pos.x * (gridSize_.y+1) + pos.y) * 3;
+       float *vertArray = (float *)vertices;
+       
+       ccVertex3F      vert = { vertArray[index], vertArray[index+1], vertArray[index+2] };
+       
+       return vert;
+}
+
+-(ccVertex3F)originalVertex:(ccGridSize)pos
+{
+       NSInteger index = (pos.x * (gridSize_.y+1) + pos.y) * 3;
+       float *vertArray = (float *)originalVertices;
+       
+       ccVertex3F      vert = { vertArray[index], vertArray[index+1], vertArray[index+2] };
+       
+       return vert;
+}
+
+-(void)setVertex:(ccGridSize)pos vertex:(ccVertex3F)vertex
+{
+       NSInteger index = (pos.x * (gridSize_.y+1) + pos.y) * 3;
+       float *vertArray = (float *)vertices;
+       vertArray[index] = vertex.x;
+       vertArray[index+1] = vertex.y;
+       vertArray[index+2] = vertex.z;
+}
+
+-(void)reuse
+{
+       if ( reuseGrid_ > 0 )
+       {
+               memcpy(originalVertices, vertices, (gridSize_.x+1)*(gridSize_.y+1)*sizeof(ccVertex3F));
+               reuseGrid_--;
+       }
+}
+
+@end
+
+////////////////////////////////////////////////////////////
+
+#pragma mark -
+#pragma mark CCTiledGrid3D
+
+@implementation CCTiledGrid3D
+
+-(void)dealloc
+{
+       free(texCoordinates);
+       free(vertices);
+       free(indices);
+       free(originalVertices);
+       [super dealloc];
+}
+
+-(void)blit
+{
+       NSInteger n = gridSize_.x * gridSize_.y;
+       
+       // Default GL states: GL_TEXTURE_2D, GL_VERTEX_ARRAY, GL_COLOR_ARRAY, GL_TEXTURE_COORD_ARRAY
+       // Needed states: GL_TEXTURE_2D, GL_VERTEX_ARRAY, GL_TEXTURE_COORD_ARRAY
+       // Unneeded states: GL_COLOR_ARRAY
+       glDisableClientState(GL_COLOR_ARRAY);   
+       
+       glVertexPointer(3, GL_FLOAT, 0, vertices);
+       glTexCoordPointer(2, GL_FLOAT, 0, texCoordinates);
+       glDrawElements(GL_TRIANGLES, (GLsizei) n*6, GL_UNSIGNED_SHORT, indices);
+
+       // restore default GL state
+       glEnableClientState(GL_COLOR_ARRAY);
+}
+
+-(void)calculateVertexPoints
+{
+       float width = (float)texture_.pixelsWide;
+       float height = (float)texture_.pixelsHigh;
+       float imageH = texture_.contentSizeInPixels.height;
+       
+       NSInteger numQuads = gridSize_.x * gridSize_.y;
+       
+       vertices = malloc(numQuads*12*sizeof(GLfloat));
+       originalVertices = malloc(numQuads*12*sizeof(GLfloat));
+       texCoordinates = malloc(numQuads*8*sizeof(GLfloat));
+       indices = malloc(numQuads*6*sizeof(GLushort));
+       
+       float *vertArray = (float*)vertices;
+       float *texArray = (float*)texCoordinates;
+       GLushort *idxArray = (GLushort *)indices;
+       
+       int x, y;
+       
+       for( x = 0; x < gridSize_.x; x++ )
+       {
+               for( y = 0; y < gridSize_.y; y++ )
+               {
+                       float x1 = x * step_.x;
+                       float x2 = x1 + step_.x;
+                       float y1 = y * step_.y;
+                       float y2 = y1 + step_.y;
+                       
+                       *vertArray++ = x1;
+                       *vertArray++ = y1;
+                       *vertArray++ = 0;
+                       *vertArray++ = x2;
+                       *vertArray++ = y1;
+                       *vertArray++ = 0;
+                       *vertArray++ = x1;
+                       *vertArray++ = y2;
+                       *vertArray++ = 0;
+                       *vertArray++ = x2;
+                       *vertArray++ = y2;
+                       *vertArray++ = 0;
+                       
+                       float newY1 = y1;
+                       float newY2 = y2;
+                       
+                       if( isTextureFlipped_ ) {
+                               newY1 = imageH - y1;
+                               newY2 = imageH - y2;
+                       }
+
+                       *texArray++ = x1 / width;
+                       *texArray++ = newY1 / height;
+                       *texArray++ = x2 / width;
+                       *texArray++ = newY1 / height;
+                       *texArray++ = x1 / width;
+                       *texArray++ = newY2 / height;
+                       *texArray++ = x2 / width;
+                       *texArray++ = newY2 / height;
+               }
+       }
+       
+       for( x = 0; x < numQuads; x++)
+       {
+               idxArray[x*6+0] = x*4+0;
+               idxArray[x*6+1] = x*4+1;
+               idxArray[x*6+2] = x*4+2;
+               
+               idxArray[x*6+3] = x*4+1;
+               idxArray[x*6+4] = x*4+2;
+               idxArray[x*6+5] = x*4+3;
+       }
+       
+       memcpy(originalVertices, vertices, numQuads*12*sizeof(GLfloat));
+}
+
+-(void)setTile:(ccGridSize)pos coords:(ccQuad3)coords
+{
+       NSInteger idx = (gridSize_.y * pos.x + pos.y) * 4 * 3;
+       float *vertArray = (float*)vertices;
+       memcpy(&vertArray[idx], &coords, sizeof(ccQuad3));
+}
+
+-(ccQuad3)originalTile:(ccGridSize)pos
+{
+       NSInteger idx = (gridSize_.y * pos.x + pos.y) * 4 * 3;
+       float *vertArray = (float*)originalVertices;
+       
+       ccQuad3 ret;
+       memcpy(&ret, &vertArray[idx], sizeof(ccQuad3));
+       
+       return ret;
+}
+
+-(ccQuad3)tile:(ccGridSize)pos
+{
+       NSInteger idx = (gridSize_.y * pos.x + pos.y) * 4 * 3;
+       float *vertArray = (float*)vertices;
+       
+       ccQuad3 ret;
+       memcpy(&ret, &vertArray[idx], sizeof(ccQuad3));
+       
+       return ret;
+}
+
+-(void)reuse
+{
+       if ( reuseGrid_ > 0 )
+       {
+               NSInteger numQuads = gridSize_.x * gridSize_.y;
+               
+               memcpy(originalVertices, vertices, numQuads*12*sizeof(GLfloat));
+               reuseGrid_--;
+       }
+}
+
+@end
diff --git a/libs/cocos2d/CCLabelAtlas.h b/libs/cocos2d/CCLabelAtlas.h
new file mode 100644 (file)
index 0000000..18d9ce4
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2008-2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+
+#import "CCAtlasNode.h"
+#import "CCTextureAtlas.h"
+
+/** CCLabelAtlas is a subclass of CCAtlasNode.
+ It can be as a replacement of CCLabel since it is MUCH faster.
+ CCLabelAtlas versus CCLabel:
+ - CCLabelAtlas is MUCH faster than CCLabel
+ - CCLabelAtlas "characters" have a fixed height and width
+ - CCLabelAtlas "characters" can be anything you want since they are taken from an image file
+ A more flexible class is CCBitmapFontAtlas. It supports variable width characters and it also has a nice editor.
+ */
+@interface CCLabelAtlas : CCAtlasNode  <CCLabelProtocol>
+{              
+       // string to render
+       NSString                *string_;
+       
+       // the first char in the charmap
+       char                    mapStartChar;
+}
+
+
+/** creates the CCLabelAtlas with a string, a char map file(the atlas), the width and height of each element in points and the starting char of the atlas */
++(id) labelWithString:(NSString*) string charMapFile: (NSString*) charmapfile itemWidth:(int)w itemHeight:(int)h startCharMap:(char)c;
+
+/** creates the CCLabelAtlas with a string, a char map file(the atlas), the width and height of each element in points and the starting char of the atlas.
+ @deprecated Will be removed in 1.0.1. Use "labelWithString:" instead
+ */
++(id) labelAtlasWithString:(NSString*) string charMapFile: (NSString*) charmapfile itemWidth:(int)w itemHeight:(int)h startCharMap:(char)c DEPRECATED_ATTRIBUTE;
+
+/** initializes the CCLabelAtlas with a string, a char map file(the atlas), the width and height in points of each element and the starting char of the atlas */
+-(id) initWithString:(NSString*) string charMapFile: (NSString*) charmapfile itemWidth:(int)w itemHeight:(int)h startCharMap:(char)c;
+@end
diff --git a/libs/cocos2d/CCLabelAtlas.m b/libs/cocos2d/CCLabelAtlas.m
new file mode 100644 (file)
index 0000000..ef953f5
--- /dev/null
@@ -0,0 +1,191 @@
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2008-2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+
+#import "ccConfig.h"
+#import "ccMacros.h"
+#import "CCDrawingPrimitives.h"
+#import "CCLabelAtlas.h"
+#import "Support/CGPointExtension.h"
+
+
+
+@implementation CCLabelAtlas
+
+#pragma mark CCLabelAtlas - Creation & Init
++(id) labelWithString:(NSString*)string charMapFile:(NSString*)charmapfile itemWidth:(int)w itemHeight:(int)h startCharMap:(char)c
+{
+       return [[[self alloc] initWithString:string charMapFile:charmapfile itemWidth:w itemHeight:h startCharMap:c] autorelease];
+}
+
+// XXX DEPRECATED. Remove it in 1.0.1
++(id) labelAtlasWithString:(NSString*) string charMapFile: (NSString*) charmapfile itemWidth:(int)w itemHeight:(int)h startCharMap:(char)c
+{
+       return [self labelWithString:string charMapFile:charmapfile itemWidth:w itemHeight:h startCharMap:c];
+}
+
+
+-(id) initWithString:(NSString*) theString charMapFile: (NSString*) charmapfile itemWidth:(int)w itemHeight:(int)h startCharMap:(char)c
+{
+
+       if ((self=[super initWithTileFile:charmapfile tileWidth:w tileHeight:h itemsToRender:[theString length] ]) ) {
+
+               mapStartChar = c;               
+               [self setString: theString];
+       }
+
+       return self;
+}
+
+-(void) dealloc
+{
+       [string_ release];
+
+       [super dealloc];
+}
+
+#pragma mark CCLabelAtlas - Atlas generation
+
+-(void) updateAtlasValues
+{
+       NSInteger n = [string_ length];
+       
+       ccV3F_C4B_T2F_Quad quad;
+
+       const char *s = [string_ UTF8String];
+
+       CCTexture2D *texture = [textureAtlas_ texture];
+       float textureWide = [texture pixelsWide];
+       float textureHigh = [texture pixelsHigh];
+
+       for( NSUInteger i=0; i<n; i++) {
+               unsigned char a = s[i] - mapStartChar;
+               float row = (a % itemsPerRow_);
+               float col = (a / itemsPerRow_);
+               
+#if CC_FIX_ARTIFACTS_BY_STRECHING_TEXEL
+               // Issue #938. Don't use texStepX & texStepY
+               float left              = (2*row*itemWidth_+1)/(2*textureWide);
+               float right             = left+(itemWidth_*2-2)/(2*textureWide);
+               float top               = (2*col*itemHeight_+1)/(2*textureHigh);
+               float bottom    = top+(itemHeight_*2-2)/(2*textureHigh);
+#else
+               float left              = row*itemWidth_/textureWide;
+               float right             = left+itemWidth_/textureWide;
+               float top               = col*itemHeight_/textureHigh;
+               float bottom    = top+itemHeight_/textureHigh;
+#endif // ! CC_FIX_ARTIFACTS_BY_STRECHING_TEXEL
+               
+               quad.tl.texCoords.u = left;
+               quad.tl.texCoords.v = top;
+               quad.tr.texCoords.u = right;
+               quad.tr.texCoords.v = top;
+               quad.bl.texCoords.u = left;
+               quad.bl.texCoords.v = bottom;
+               quad.br.texCoords.u = right;
+               quad.br.texCoords.v = bottom;
+               
+               quad.bl.vertices.x = (int) (i * itemWidth_);
+               quad.bl.vertices.y = 0;
+               quad.bl.vertices.z = 0.0f;
+               quad.br.vertices.x = (int)(i * itemWidth_ + itemWidth_);
+               quad.br.vertices.y = 0;
+               quad.br.vertices.z = 0.0f;
+               quad.tl.vertices.x = (int)(i * itemWidth_);
+               quad.tl.vertices.y = (int)(itemHeight_);
+               quad.tl.vertices.z = 0.0f;
+               quad.tr.vertices.x = (int)(i * itemWidth_ + itemWidth_);
+               quad.tr.vertices.y = (int)(itemHeight_);
+               quad.tr.vertices.z = 0.0f;
+               
+               [textureAtlas_ updateQuad:&quad atIndex:i];
+       }
+}
+
+#pragma mark CCLabelAtlas - CCLabelProtocol
+
+- (void) setString:(NSString*) newString
+{
+       NSUInteger len = [newString length];
+       if( len > textureAtlas_.capacity )
+               [textureAtlas_ resizeCapacity:len];
+
+       [string_ release];
+       string_ = [newString copy];
+       [self updateAtlasValues];
+
+       CGSize s;
+       s.width = len * itemWidth_;
+       s.height = itemHeight_;
+       [self setContentSizeInPixels:s];
+}
+
+-(NSString*) string
+{
+       return string_;
+}
+
+#pragma mark CCLabelAtlas - draw
+
+// XXX: overriding draw from AtlasNode
+- (void) draw
+{
+       // Default GL states: GL_TEXTURE_2D, GL_VERTEX_ARRAY, GL_COLOR_ARRAY, GL_TEXTURE_COORD_ARRAY
+       // Needed states: GL_TEXTURE_2D, GL_VERTEX_ARRAY, GL_TEXTURE_COORD_ARRAY
+       // Unneeded states: GL_COLOR_ARRAY
+       glDisableClientState(GL_COLOR_ARRAY);
+
+       glColor4ub( color_.r, color_.g, color_.b, opacity_);
+       
+       BOOL newBlend = blendFunc_.src != CC_BLEND_SRC || blendFunc_.dst != CC_BLEND_DST;
+       if( newBlend )
+               glBlendFunc( blendFunc_.src, blendFunc_.dst );
+       
+       [textureAtlas_ drawNumberOfQuads:string_.length fromIndex:0];
+       
+       if( newBlend )
+               glBlendFunc(CC_BLEND_SRC, CC_BLEND_DST);
+       
+       // is this chepear than saving/restoring color state ?
+       // XXX: There is no need to restore the color to (255,255,255,255). Objects should use the color
+       // XXX: that they need
+//     glColor4ub( 255, 255, 255, 255);
+
+       // Restore Default GL state. Enable GL_COLOR_ARRAY
+       glEnableClientState(GL_COLOR_ARRAY);
+       
+       
+#if CC_LABELATLAS_DEBUG_DRAW
+       CGSize s = [self contentSize];
+       CGPoint vertices[4]={
+               ccp(0,0),ccp(s.width,0),
+               ccp(s.width,s.height),ccp(0,s.height),
+       };
+       ccDrawPoly(vertices, 4, YES);
+#endif // CC_LABELATLAS_DEBUG_DRAW
+
+}
+@end
diff --git a/libs/cocos2d/CCLabelBMFont.h b/libs/cocos2d/CCLabelBMFont.h
new file mode 100644 (file)
index 0000000..0fd2fce
--- /dev/null
@@ -0,0 +1,190 @@
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2008-2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * Portions of this code are based and inspired on:
+ *   http://www.71squared.co.uk/2009/04/iphone-game-programming-tutorial-4-bitmap-font-class
+ *   by Michael Daley
+ * Use any of these editors to generate bitmap font atlas:
+ *   http://www.n4te.com/hiero/hiero.jnlp
+ *   http://slick.cokeandcode.com/demos/hiero.jnlp
+ *   http://www.angelcode.com/products/bmfont/
+ *
+ */
+
+#import "CCSpriteBatchNode.h"
+#import "Support/uthash.h"
+
+struct _KerningHashElement;
+
+/** @struct ccBMFontDef
+ BMFont definition
+ */
+typedef struct _BMFontDef {
+       //! ID of the character
+       unsigned int charID;
+       //! origin and size of the font
+       CGRect rect;
+       //! The X amount the image should be offset when drawing the image (in pixels)
+       int xOffset;
+       //! The Y amount the image should be offset when drawing the image (in pixels)
+       int yOffset;
+       //! The amount to move the current position after drawing the character (in pixels)
+       int xAdvance;
+} ccBMFontDef;
+
+/** @struct ccBMFontPadding
+ BMFont padding
+ @since v0.8.2
+ */
+typedef struct _BMFontPadding {
+       /// padding left
+       int     left;
+       /// padding top
+       int top;
+       /// padding right
+       int right;
+       /// padding bottom
+       int bottom;
+} ccBMFontPadding;
+
+enum {
+       // how many characters are supported
+       kCCBMFontMaxChars = 2048, //256,
+};
+
+/** CCBMFontConfiguration has parsed configuration of the the .fnt file
+ @since v0.8
+ */
+@interface CCBMFontConfiguration : NSObject
+{
+// XXX: Creating a public interface so that the bitmapFontArray[] is accesible
+@public
+       // The characters building up the font
+       ccBMFontDef     BMFontArray_[kCCBMFontMaxChars];
+       
+       // FNTConfig: Common Height
+       NSUInteger              commonHeight_;
+       
+       // Padding
+       ccBMFontPadding padding_;
+       
+       // atlas name
+       NSString                *atlasName_;
+
+       // values for kerning
+       struct _KerningHashElement      *kerningDictionary_;
+}
+
+/** allocates a CCBMFontConfiguration with a FNT file */
++(id) configurationWithFNTFile:(NSString*)FNTfile;
+/** initializes a CCBMFontConfiguration with a FNT file */
+-(id) initWithFNTfile:(NSString*)FNTfile;
+@end
+
+
+/** CCLabelBMFont is a subclass of CCSpriteBatchNode
+  
+ Features:
+ - Treats each character like a CCSprite. This means that each individual character can be:
+   - rotated
+   - scaled
+   - translated
+   - tinted
+   - chage the opacity
+ - It can be used as part of a menu item.
+ - anchorPoint can be used to align the "label"
+ - Supports AngelCode text format
+ Limitations:
+  - All inner characters are using an anchorPoint of (0.5f, 0.5f) and it is not recommend to change it
+    because it might affect the rendering
+ CCLabelBMFont implements the protocol CCLabelProtocol, like CCLabel and CCLabelAtlas.
+ CCLabelBMFont has the flexibility of CCLabel, the speed of CCLabelAtlas and all the features of CCSprite.
+ If in doubt, use CCLabelBMFont instead of CCLabelAtlas / CCLabel.
+ Supported editors:
+  - http://www.n4te.com/hiero/hiero.jnlp
+  - http://slick.cokeandcode.com/demos/hiero.jnlp
+  - http://www.angelcode.com/products/bmfont/
+ @since v0.8
+ */
+
+@interface CCLabelBMFont : CCSpriteBatchNode <CCLabelProtocol, CCRGBAProtocol>
+{
+       // string to render
+       NSString                *string_;
+       
+       CCBMFontConfiguration   *configuration_;
+
+       // texture RGBA
+       GLubyte         opacity_;
+       ccColor3B       color_;
+       BOOL opacityModifyRGB_;
+}
+
+/** Purges the cached data.
+ Removes from memory the cached configurations and the atlas name dictionary.
+ @since v0.99.3
+ */
++(void) purgeCachedData;
+
+/** conforms to CCRGBAProtocol protocol */
+@property (nonatomic,readwrite) GLubyte opacity;
+/** conforms to CCRGBAProtocol protocol */
+@property (nonatomic,readwrite) ccColor3B color;
+
+
+/** creates a BMFont label with an initial string and the FNT file */
++(id) labelWithString:(NSString*)string fntFile:(NSString*)fntFile;
+
+/** creates a BMFont label with an initial string and the FNT file
+ @deprecated Will be removed in 1.0.1. Use "labelWithString" instead.
+ */
++(id) bitmapFontAtlasWithString:(NSString*)string fntFile:(NSString*)fntFile DEPRECATED_ATTRIBUTE;
+
+/** init a BMFont label with an initial string and the FNT file */
+-(id) initWithString:(NSString*)string fntFile:(NSString*)fntFile;
+
+/** updates the font chars based on the string to render */
+-(void) createFontChars;
+@end
+
+/** Free function that parses a FNT file a place it on the cache
+*/
+CCBMFontConfiguration * FNTConfigLoadFile( NSString *file );
+/** Purges the FNT config cache
+ */
+void FNTConfigRemoveCache( void );
+
+
+
+/** CCBitmapFontAtlas
+ @deprecated Use CCLabelBMFont instead. Will be removed 1.0.1
+ */
+DEPRECATED_ATTRIBUTE @interface CCBitmapFontAtlas : CCLabelBMFont
+@end
+
diff --git a/libs/cocos2d/CCLabelBMFont.m b/libs/cocos2d/CCLabelBMFont.m
new file mode 100644 (file)
index 0000000..78d7876
--- /dev/null
@@ -0,0 +1,673 @@
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2008-2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * Portions of this code are based and inspired on:
+ *   http://www.71squared.co.uk/2009/04/iphone-game-programming-tutorial-4-bitmap-font-class
+ *   by Michael Daley
+ *
+ *
+ * Use any of these editors to generate bitmap font atlas:
+ *   http://www.n4te.com/hiero/hiero.jnlp
+ *   http://slick.cokeandcode.com/demos/hiero.jnlp
+ *   http://www.angelcode.com/products/bmfont/
+ */
+
+#import "ccConfig.h"
+#import "CCLabelBMFont.h"
+#import "CCSprite.h"
+#import "CCDrawingPrimitives.h"
+#import "CCConfiguration.h"
+#import "Support/CCFileUtils.h"
+#import "Support/CGPointExtension.h"
+#import "Support/uthash.h"
+
+#pragma mark -
+#pragma mark FNTConfig Cache - free functions
+
+NSMutableDictionary *configurations = nil;
+CCBMFontConfiguration* FNTConfigLoadFile( NSString *fntFile)
+{
+       CCBMFontConfiguration *ret = nil;
+       
+       if( configurations == nil )
+               configurations = [[NSMutableDictionary dictionaryWithCapacity:3] retain];
+       
+       ret = [configurations objectForKey:fntFile];
+       if( ret == nil ) {
+               ret = [CCBMFontConfiguration configurationWithFNTFile:fntFile];
+               [configurations setObject:ret forKey:fntFile];
+       }
+       
+       return ret;
+}
+
+void FNTConfigRemoveCache( void )
+{
+       [configurations removeAllObjects];
+}
+
+#pragma mark - Hash Element
+
+// Equal function for targetSet.
+typedef struct _KerningHashElement
+{      
+       int                             key;            // key for the hash. 16-bit for 1st element, 16-bit for 2nd element
+       int                             amount;
+       UT_hash_handle  hh;
+} tKerningHashElement;
+
+#pragma mark -
+#pragma mark BitmapFontConfiguration
+
+
+@interface CCBMFontConfiguration (Private)
+-(void) parseConfigFile:(NSString*)controlFile;
+-(void) parseCharacterDefinition:(NSString*)line charDef:(ccBMFontDef*)characterDefinition;
+-(void) parseInfoArguments:(NSString*)line;
+-(void) parseCommonArguments:(NSString*)line;
+-(void) parseImageFileName:(NSString*)line fntFile:(NSString*)fntFile;
+-(void) parseKerningCapacity:(NSString*)line;
+-(void) parseKerningEntry:(NSString*)line;
+-(void) purgeKerningDictionary;
+@end
+
+@implementation CCBMFontConfiguration
+
++(id) configurationWithFNTFile:(NSString*)FNTfile
+{
+       return [[[self alloc] initWithFNTfile:FNTfile] autorelease];
+}
+
+-(id) initWithFNTfile:(NSString*)fntFile
+{
+       if((self=[super init])) {
+               
+               kerningDictionary_ = NULL;
+
+               [self parseConfigFile:fntFile];
+       }
+       return self;
+}
+
+- (void) dealloc
+{
+       CCLOGINFO( @"cocos2d: deallocing %@", self);
+       [self purgeKerningDictionary];
+       [atlasName_ release];
+       [super dealloc];
+}
+
+- (NSString*) description
+{
+       return [NSString stringWithFormat:@"<%@ = %08X | Kernings:%d | Image = %@>", [self class], self,
+                       HASH_COUNT(kerningDictionary_),
+                       atlasName_];
+}
+
+
+-(void) purgeKerningDictionary
+{
+       tKerningHashElement *current;
+       
+       while(kerningDictionary_) {
+               current = kerningDictionary_; 
+               HASH_DEL(kerningDictionary_,current);
+               free(current);
+       }
+}
+
+- (void)parseConfigFile:(NSString*)fntFile
+{      
+       NSString *fullpath = [CCFileUtils fullPathFromRelativePath:fntFile];
+       NSError *error;
+       NSString *contents = [NSString stringWithContentsOfFile:fullpath encoding:NSUTF8StringEncoding error:&error];
+
+       NSAssert1( contents, @"cocos2d: Error parsing FNTfile: %@", error);
+       
+       
+       // Move all lines in the string, which are denoted by \n, into an array
+       NSArray *lines = [[NSArray alloc] initWithArray:[contents componentsSeparatedByString:@"\n"]];
+       
+       // Create an enumerator which we can use to move through the lines read from the control file
+       NSEnumerator *nse = [lines objectEnumerator];
+       
+       // Create a holder for each line we are going to work with
+       NSString *line;
+       
+       // Loop through all the lines in the lines array processing each one
+       while( (line = [nse nextObject]) ) {
+               // parse spacing / padding
+               if([line hasPrefix:@"info face"]) {
+                       // XXX: info parsing is incomplete
+                       // Not needed for the Hiero editors, but needed for the AngelCode editor
+//                     [self parseInfoArguments:line];
+               }
+               // Check to see if the start of the line is something we are interested in
+               else if([line hasPrefix:@"common lineHeight"]) {
+                       [self parseCommonArguments:line];
+               }
+               else if([line hasPrefix:@"page id"]) {
+                       [self parseImageFileName:line fntFile:fntFile];
+               }
+               else if([line hasPrefix:@"chars c"]) {
+                       // Ignore this line
+               }
+               else if([line hasPrefix:@"char"]) {
+                       // Parse the current line and create a new CharDef
+                       ccBMFontDef characterDefinition;
+                       [self parseCharacterDefinition:line charDef:&characterDefinition];
+
+                       // Add the CharDef returned to the charArray
+                       BMFontArray_[ characterDefinition.charID ] = characterDefinition;
+               }
+               else if([line hasPrefix:@"kernings count"]) {
+                       [self parseKerningCapacity:line];
+               }
+               else if([line hasPrefix:@"kerning first"]) {
+                       [self parseKerningEntry:line];
+               }
+       }
+       // Finished with lines so release it
+       [lines release];
+}
+
+-(void) parseImageFileName:(NSString*)line fntFile:(NSString*)fntFile
+{
+       NSString *propertyValue = nil;
+
+       // Break the values for this line up using =
+       NSArray *values = [line componentsSeparatedByString:@"="];
+       
+       // Get the enumerator for the array of components which has been created
+       NSEnumerator *nse = [values objectEnumerator];
+       
+       // We need to move past the first entry in the array before we start assigning values
+       [nse nextObject];
+       
+       // page ID. Sanity check
+       propertyValue = [nse nextObject];
+       NSAssert( [propertyValue intValue] == 0, @"XXX: BitmapFontAtlas only supports 1 page");
+       
+       // file 
+       propertyValue = [nse nextObject];
+       NSArray *array = [propertyValue componentsSeparatedByString:@"\""];
+       propertyValue = [array objectAtIndex:1];
+       NSAssert(propertyValue,@"BitmapFontAtlas file could not be found");
+       
+       // Supports subdirectories
+       NSString *dir = [fntFile stringByDeletingLastPathComponent];
+       atlasName_ = [dir stringByAppendingPathComponent:propertyValue];
+
+       [atlasName_ retain];
+}
+
+-(void) parseInfoArguments:(NSString*)line
+{
+       //
+       // possible lines to parse:
+       // info face="Script" size=32 bold=0 italic=0 charset="" unicode=1 stretchH=100 smooth=1 aa=1 padding=1,4,3,2 spacing=0,0 outline=0
+       // info face="Cracked" size=36 bold=0 italic=0 charset="" unicode=0 stretchH=100 smooth=1 aa=1 padding=0,0,0,0 spacing=1,1
+       //
+       NSArray *values = [line componentsSeparatedByString:@"="];
+       NSEnumerator *nse = [values objectEnumerator];  
+       NSString *propertyValue = nil;
+       
+       // We need to move past the first entry in the array before we start assigning values
+       [nse nextObject];
+       
+       // face (ignore)
+       [nse nextObject];
+       
+       // size (ignore)
+       [nse nextObject];
+
+       // bold (ignore)
+       [nse nextObject];
+
+       // italic (ignore)
+       [nse nextObject];
+       
+       // charset (ignore)
+       [nse nextObject];
+
+       // unicode (ignore)
+       [nse nextObject];
+
+       // strechH (ignore)
+       [nse nextObject];
+
+       // smooth (ignore)
+       [nse nextObject];
+       
+       // aa (ignore)
+       [nse nextObject];
+       
+       // padding (ignore)
+       propertyValue = [nse nextObject];
+       {
+               
+               NSArray *paddingValues = [propertyValue componentsSeparatedByString:@","];
+               NSEnumerator *paddingEnum = [paddingValues objectEnumerator];
+               // padding top
+               propertyValue = [paddingEnum nextObject];
+               padding_.top = [propertyValue intValue];
+               
+               // padding right
+               propertyValue = [paddingEnum nextObject];
+               padding_.right = [propertyValue intValue];
+
+               // padding bottom
+               propertyValue = [paddingEnum nextObject];
+               padding_.bottom = [propertyValue intValue];
+               
+               // padding left
+               propertyValue = [paddingEnum nextObject];
+               padding_.left = [propertyValue intValue];
+               
+               CCLOG(@"cocos2d: padding: %d,%d,%d,%d", padding_.left, padding_.top, padding_.right, padding_.bottom);
+       }
+
+       // spacing (ignore)
+       [nse nextObject];       
+}
+
+-(void) parseCommonArguments:(NSString*)line
+{
+       //
+       // line to parse:
+       // common lineHeight=104 base=26 scaleW=1024 scaleH=512 pages=1 packed=0
+       //
+       NSArray *values = [line componentsSeparatedByString:@"="];
+       NSEnumerator *nse = [values objectEnumerator];  
+       NSString *propertyValue = nil;
+       
+       // We need to move past the first entry in the array before we start assigning values
+       [nse nextObject];
+       
+       // Character ID
+       propertyValue = [nse nextObject];
+       commonHeight_ = [propertyValue intValue];
+       
+       // base (ignore)
+       [nse nextObject];
+       
+       
+       // scaleW. sanity check
+       propertyValue = [nse nextObject];       
+       NSAssert( [propertyValue intValue] <= [[CCConfiguration sharedConfiguration] maxTextureSize], @"CCLabelBMFont: page can't be larger than supported");
+       
+       // scaleH. sanity check
+       propertyValue = [nse nextObject];
+       NSAssert( [propertyValue intValue] <= [[CCConfiguration sharedConfiguration] maxTextureSize], @"CCLabelBMFont: page can't be larger than supported");
+       
+       // pages. sanity check
+       propertyValue = [nse nextObject];
+       NSAssert( [propertyValue intValue] == 1, @"CCBitfontAtlas: only supports 1 page");
+       
+       // packed (ignore) What does this mean ??
+}
+- (void)parseCharacterDefinition:(NSString*)line charDef:(ccBMFontDef*)characterDefinition
+{      
+       // Break the values for this line up using =
+       NSArray *values = [line componentsSeparatedByString:@"="];
+       NSEnumerator *nse = [values objectEnumerator];  
+       NSString *propertyValue;
+       
+       // We need to move past the first entry in the array before we start assigning values
+       [nse nextObject];
+       
+       // Character ID
+       propertyValue = [nse nextObject];
+       propertyValue = [propertyValue substringToIndex: [propertyValue rangeOfString: @" "].location];
+       characterDefinition->charID = [propertyValue intValue];
+       NSAssert(characterDefinition->charID < kCCBMFontMaxChars, @"BitmpaFontAtlas: CharID bigger than supported");
+
+       // Character x
+       propertyValue = [nse nextObject];
+       characterDefinition->rect.origin.x = [propertyValue intValue];
+       // Character y
+       propertyValue = [nse nextObject];
+       characterDefinition->rect.origin.y = [propertyValue intValue];
+       // Character width
+       propertyValue = [nse nextObject];
+       characterDefinition->rect.size.width = [propertyValue intValue];
+       // Character height
+       propertyValue = [nse nextObject];
+       characterDefinition->rect.size.height = [propertyValue intValue];
+       // Character xoffset
+       propertyValue = [nse nextObject];
+       characterDefinition->xOffset = [propertyValue intValue];
+       // Character yoffset
+       propertyValue = [nse nextObject];
+       characterDefinition->yOffset = [propertyValue intValue];
+       // Character xadvance
+       propertyValue = [nse nextObject];
+       characterDefinition->xAdvance = [propertyValue intValue];
+}
+
+-(void) parseKerningCapacity:(NSString*) line
+{
+       // When using uthash there is not need to parse the capacity.
+
+//     NSAssert(!kerningDictionary, @"dictionary already initialized");
+//     
+//     // Break the values for this line up using =
+//     NSArray *values = [line componentsSeparatedByString:@"="];
+//     NSEnumerator *nse = [values objectEnumerator];  
+//     NSString *propertyValue;
+//     
+//     // We need to move past the first entry in the array before we start assigning values
+//     [nse nextObject];
+//     
+//     // count
+//     propertyValue = [nse nextObject];
+//     int capacity = [propertyValue intValue];
+//     
+//     if( capacity != -1 )
+//             kerningDictionary = ccHashSetNew(capacity, targetSetEql);
+}
+
+-(void) parseKerningEntry:(NSString*) line
+{
+       NSArray *values = [line componentsSeparatedByString:@"="];
+       NSEnumerator *nse = [values objectEnumerator];  
+       NSString *propertyValue;
+       
+       // We need to move past the first entry in the array before we start assigning values
+       [nse nextObject];
+       
+       // first
+       propertyValue = [nse nextObject];
+       int first = [propertyValue intValue];
+       
+       // second
+       propertyValue = [nse nextObject];
+       int second = [propertyValue intValue];
+       
+       // second
+       propertyValue = [nse nextObject];
+       int amount = [propertyValue intValue];
+
+       tKerningHashElement *element = calloc( sizeof( *element ), 1 );
+       element->amount = amount;
+       element->key = (first<<16) | (second&0xffff);
+       HASH_ADD_INT(kerningDictionary_,key, element);
+}
+
+@end
+
+#pragma mark -
+#pragma mark CCLabelBMFont
+
+@interface CCLabelBMFont (Private)
+-(NSString*) atlasNameFromFntFile:(NSString*)fntFile;
+
+-(int) kerningAmountForFirst:(unichar)first second:(unichar)second;
+
+@end
+
+@implementation CCLabelBMFont
+
+@synthesize opacity = opacity_, color = color_;
+
+#pragma mark BitmapFontAtlas - Purge Cache
++(void) purgeCachedData
+{
+       FNTConfigRemoveCache();
+}
+
+#pragma mark BitmapFontAtlas - Creation & Init
+
++(id) labelWithString:(NSString *)string fntFile:(NSString *)fntFile
+{
+       return [[[self alloc] initWithString:string fntFile:fntFile] autorelease];
+}
+
+// XXX - deprecated - Will be removed in 1.0.1
++(id) bitmapFontAtlasWithString:(NSString*)string fntFile:(NSString*)fntFile
+{
+       return [self labelWithString:string fntFile:fntFile];
+}
+
+-(id) initWithString:(NSString*)theString fntFile:(NSString*)fntFile
+{      
+       
+       [configuration_ release]; // allow re-init
+
+       configuration_ = FNTConfigLoadFile(fntFile);
+       [configuration_ retain];
+
+       NSAssert( configuration_, @"Error creating config for BitmapFontAtlas");
+
+       
+       if ((self=[super initWithFile:configuration_->atlasName_ capacity:[theString length]])) {
+
+               opacity_ = 255;
+               color_ = ccWHITE;
+
+               contentSize_ = CGSizeZero;
+               
+               opacityModifyRGB_ = [[textureAtlas_ texture] hasPremultipliedAlpha];
+
+               anchorPoint_ = ccp(0.5f, 0.5f);
+
+               [self setString:theString];
+       }
+
+       return self;
+}
+
+-(void) dealloc
+{
+       [string_ release];
+       [configuration_ release];
+       [super dealloc];
+}
+
+#pragma mark BitmapFontAtlas - Atlas generation
+
+-(int) kerningAmountForFirst:(unichar)first second:(unichar)second
+{
+       int ret = 0;
+       unsigned int key = (first<<16) | (second & 0xffff);
+       
+       if( configuration_->kerningDictionary_ ) {
+               tKerningHashElement *element = NULL;
+               HASH_FIND_INT(configuration_->kerningDictionary_, &key, element);               
+               if(element)
+                       ret = element->amount;
+       }
+               
+       return ret;
+}
+
+-(void) createFontChars
+{
+       NSInteger nextFontPositionX = 0;
+       NSInteger nextFontPositionY = 0;
+       unichar prev = -1;
+       NSInteger kerningAmount = 0;
+       
+       CGSize tmpSize = CGSizeZero;
+
+       NSInteger longestLine = 0;
+       NSUInteger totalHeight = 0;
+       
+       NSUInteger quantityOfLines = 1;
+
+       NSUInteger stringLen = [string_ length];
+       if( ! stringLen )
+               return;
+
+       // quantity of lines NEEDS to be calculated before parsing the lines,
+       // since the Y position needs to be calcualted before hand
+       for(NSUInteger i=0; i < stringLen-1;i++) {
+               unichar c = [string_ characterAtIndex:i];
+               if( c=='\n')
+                       quantityOfLines++;
+       }
+       
+       totalHeight = configuration_->commonHeight_ * quantityOfLines;
+       nextFontPositionY = -(configuration_->commonHeight_ - configuration_->commonHeight_*quantityOfLines);
+       
+       for(NSUInteger i=0; i<stringLen; i++) {
+               unichar c = [string_ characterAtIndex:i];
+               NSAssert( c < kCCBMFontMaxChars, @"BitmapFontAtlas: character outside bounds");
+               
+               if (c == '\n') {
+                       nextFontPositionX = 0;
+                       nextFontPositionY -= configuration_->commonHeight_;
+                       continue;
+               }
+
+               kerningAmount = [self kerningAmountForFirst:prev second:c];
+               
+               ccBMFontDef fontDef = configuration_->BMFontArray_[c];
+               
+               CGRect rect = fontDef.rect;
+               
+               CCSprite *fontChar;
+               
+               fontChar = (CCSprite*) [self getChildByTag:i];
+               if( ! fontChar ) {
+                       fontChar = [[CCSprite alloc] initWithBatchNode:self rectInPixels:rect];
+                       [self addChild:fontChar z:0 tag:i];
+                       [fontChar release];
+               }
+               else {
+                       // reusing fonts
+                       [fontChar setTextureRectInPixels:rect rotated:NO untrimmedSize:rect.size];
+                       
+                       // restore to default in case they were modified
+                       fontChar.visible = YES;
+                       fontChar.opacity = 255;
+               }
+               
+               float yOffset = configuration_->commonHeight_ - fontDef.yOffset;
+               fontChar.positionInPixels = ccp( (float)nextFontPositionX + fontDef.xOffset + fontDef.rect.size.width*0.5f + kerningAmount,
+                                                               (float)nextFontPositionY + yOffset - rect.size.height*0.5f );
+
+               // update kerning
+               nextFontPositionX += configuration_->BMFontArray_[c].xAdvance + kerningAmount;
+               prev = c;
+
+               // Apply label properties
+               [fontChar setOpacityModifyRGB:opacityModifyRGB_];
+               // Color MUST be set before opacity, since opacity might change color if OpacityModifyRGB is on
+               [fontChar setColor:color_];
+
+               // only apply opacity if it is different than 255 )
+               // to prevent modifying the color too (issue #610)
+               if( opacity_ != 255 )
+                       [fontChar setOpacity: opacity_];
+
+               if (longestLine < nextFontPositionX)
+                       longestLine = nextFontPositionX;
+       }
+
+       tmpSize.width = longestLine;
+       tmpSize.height = totalHeight;
+
+       [self setContentSizeInPixels:tmpSize];
+}
+
+#pragma mark BitmapFontAtlas - CCLabelProtocol protocol
+- (void) setString:(NSString*) newString
+{      
+       [string_ release];
+       string_ = [newString copy];
+
+       CCNode *child;
+       CCARRAY_FOREACH(children_, child)
+               child.visible = NO;
+
+       [self createFontChars];
+}
+
+-(NSString*) string
+{
+       return string_;
+}
+
+-(void) setCString:(char*)label
+{
+       [self setString:[NSString stringWithUTF8String:label]];
+}
+
+#pragma mark BitmapFontAtlas - CCRGBAProtocol protocol
+
+-(void) setColor:(ccColor3B)color
+{
+       color_ = color;
+       
+       CCSprite *child;
+       CCARRAY_FOREACH(children_, child)
+               [child setColor:color_];
+}
+
+-(void) setOpacity:(GLubyte)opacity
+{
+       opacity_ = opacity;
+
+       id<CCRGBAProtocol> child;
+       CCARRAY_FOREACH(children_, child)
+               [child setOpacity:opacity_];
+}
+-(void) setOpacityModifyRGB:(BOOL)modify
+{
+       opacityModifyRGB_ = modify;
+       
+       id<CCRGBAProtocol> child;
+       CCARRAY_FOREACH(children_, child)
+               [child setOpacityModifyRGB:modify];
+}
+
+-(BOOL) doesOpacityModifyRGB
+{
+       return opacityModifyRGB_;
+}
+
+#pragma mark BitmapFontAtlas - AnchorPoint
+-(void) setAnchorPoint:(CGPoint)point
+{
+       if( ! CGPointEqualToPoint(point, anchorPoint_) ) {
+               [super setAnchorPoint:point];
+               [self createFontChars];
+       }
+}
+
+#pragma mark BitmapFontAtlas - Debug draw
+#if CC_LABELBMFONT_DEBUG_DRAW
+-(void) draw
+{
+       [super draw];
+       CGSize s = [self contentSize];
+       CGPoint vertices[4]={
+               ccp(0,0),ccp(s.width,0),
+               ccp(s.width,s.height),ccp(0,s.height),
+       };
+       ccDrawPoly(vertices, 4, YES);
+}
+#endif // CC_LABELBMFONT_DEBUG_DRAW
+@end
diff --git a/libs/cocos2d/CCLabelTTF.h b/libs/cocos2d/CCLabelTTF.h
new file mode 100644 (file)
index 0000000..326e93f
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2008-2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+
+#import "CCTexture2D.h"
+#import "CCSprite.h"
+#import "Platforms/CCNS.h"
+
+
+/** CCLabel is a subclass of CCTextureNode that knows how to render text labels
+ *
+ * All features from CCTextureNode are valid in CCLabel
+ *
+ * CCLabel objects are slow. Consider using CCLabelAtlas or CCBitmapFontAtlas instead.
+ */
+
+@interface CCLabelTTF : CCSprite <CCLabelProtocol>
+{
+       CGSize dimensions_;
+       CCTextAlignment alignment_;
+       NSString * fontName_;
+       CGFloat fontSize_;
+       CCLineBreakMode lineBreakMode_;
+       NSString        *string_;
+}
+
+/** creates a CCLabel from a fontname, alignment, dimension in points, line break mode, and font size in points.
+ Supported lineBreakModes:
+ - iOS: all UILineBreakMode supported modes
+ - Mac: Only NSLineBreakByWordWrapping is supported.
+ @since v1.0
+ */
++ (id) labelWithString:(NSString*)string dimensions:(CGSize)dimensions alignment:(CCTextAlignment)alignment lineBreakMode:(CCLineBreakMode)lineBreakMode fontName:(NSString*)name fontSize:(CGFloat)size;
+/** creates a CCLabel from a fontname, alignment, dimension in points and font size in points*/
++ (id) labelWithString:(NSString*)string dimensions:(CGSize)dimensions alignment:(CCTextAlignment)alignment fontName:(NSString*)name fontSize:(CGFloat)size;
+/** creates a CCLabel from a fontname and font size in points*/
++ (id) labelWithString:(NSString*)string fontName:(NSString*)name fontSize:(CGFloat)size;
+/** initializes the CCLabel with a font name, alignment, dimension in points, line brea mode and font size in points.
+ Supported lineBreakModes:
+ - iOS: all UILineBreakMode supported modes
+ - Mac: Only NSLineBreakByWordWrapping is supported.
+ @since v1.0
+ */
+- (id) initWithString:(NSString*)str dimensions:(CGSize)dimensions alignment:(CCTextAlignment)alignment lineBreakMode:(CCLineBreakMode)lineBreakMode fontName:(NSString*)name fontSize:(CGFloat)size;
+/** initializes the CCLabel with a font name, alignment, dimension in points and font size in points */
+- (id) initWithString:(NSString*)string dimensions:(CGSize)dimensions alignment:(CCTextAlignment)alignment fontName:(NSString*)name fontSize:(CGFloat)size;
+/** initializes the CCLabel with a font name and font size in points */
+- (id) initWithString:(NSString*)string  fontName:(NSString*)name fontSize:(CGFloat)size;
+
+/** changes the string to render
+ * @warning Changing the string is as expensive as creating a new CCLabel. To obtain better performance use CCLabelAtlas
+ */
+- (void) setString:(NSString*)str;
+
+@end
diff --git a/libs/cocos2d/CCLabelTTF.m b/libs/cocos2d/CCLabelTTF.m
new file mode 100644 (file)
index 0000000..4e4c954
--- /dev/null
@@ -0,0 +1,138 @@
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2008-2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+
+#import <Availability.h>
+
+#import "CCLabelTTF.h"
+#import "Support/CGPointExtension.h"
+#import "ccMacros.h"
+
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+#import "Platforms/iOS/CCDirectorIOS.h"
+#endif
+
+@implementation CCLabelTTF
+
+- (id) init
+{
+       NSAssert(NO, @"CCLabelTTF: Init not supported. Use initWithString");
+       [self release];
+       return nil;
+}
+
++ (id) labelWithString:(NSString*)string dimensions:(CGSize)dimensions alignment:(CCTextAlignment)alignment lineBreakMode:(CCLineBreakMode)lineBreakMode fontName:(NSString*)name fontSize:(CGFloat)size;
+{
+       return [[[self alloc] initWithString: string dimensions:dimensions alignment:alignment lineBreakMode:lineBreakMode fontName:name fontSize:size]autorelease];
+}
+
++ (id) labelWithString:(NSString*)string dimensions:(CGSize)dimensions alignment:(CCTextAlignment)alignment fontName:(NSString*)name fontSize:(CGFloat)size
+{
+       return [[[self alloc] initWithString: string dimensions:dimensions alignment:alignment fontName:name fontSize:size]autorelease];
+}
+
++ (id) labelWithString:(NSString*)string fontName:(NSString*)name fontSize:(CGFloat)size
+{
+       return [[[self alloc] initWithString: string fontName:name fontSize:size]autorelease];
+}
+
+
+- (id) initWithString:(NSString*)str dimensions:(CGSize)dimensions alignment:(CCTextAlignment)alignment lineBreakMode:(CCLineBreakMode)lineBreakMode fontName:(NSString*)name fontSize:(CGFloat)size
+{
+       if( (self=[super init]) ) {
+
+               dimensions_ = CGSizeMake( dimensions.width * CC_CONTENT_SCALE_FACTOR(), dimensions.height * CC_CONTENT_SCALE_FACTOR() );
+               alignment_ = alignment;
+               fontName_ = [name retain];
+               fontSize_ = size * CC_CONTENT_SCALE_FACTOR();
+               lineBreakMode_ = lineBreakMode;
+               
+               [self setString:str];
+       }
+       return self;
+}
+
+- (id) initWithString:(NSString*)str dimensions:(CGSize)dimensions alignment:(CCTextAlignment)alignment fontName:(NSString*)name fontSize:(CGFloat)size
+{
+       return [self initWithString:str dimensions:dimensions alignment:alignment lineBreakMode:CCLineBreakModeWordWrap fontName:name fontSize:size];
+}
+
+- (id) initWithString:(NSString*)str fontName:(NSString*)name fontSize:(CGFloat)size
+{
+       if( (self=[super init]) ) {
+               
+               dimensions_ = CGSizeZero;
+               fontName_ = [name retain];
+               fontSize_ = size * CC_CONTENT_SCALE_FACTOR();
+               
+               [self setString:str];
+       }
+       return self;
+}
+
+- (void) setString:(NSString*)str
+{
+       [string_ release];
+       string_ = [str copy];
+
+       CCTexture2D *tex;
+       if( CGSizeEqualToSize( dimensions_, CGSizeZero ) )
+               tex = [[CCTexture2D alloc] initWithString:str
+                                                                                fontName:fontName_
+                                                                                fontSize:fontSize_];
+       else
+               tex = [[CCTexture2D alloc] initWithString:str
+                                                                          dimensions:dimensions_
+                                                                               alignment:alignment_
+                                                                       lineBreakMode:lineBreakMode_
+                                                                                fontName:fontName_
+                                                                                fontSize:fontSize_];
+
+       [self setTexture:tex];
+       [tex release];
+
+       CGRect rect = CGRectZero;
+       rect.size = [texture_ contentSize];
+       [self setTextureRect: rect];
+}
+
+-(NSString*) string
+{
+       return string_;
+}
+
+- (void) dealloc
+{
+       [string_ release];
+       [fontName_ release];
+       [super dealloc];
+}
+
+- (NSString*) description
+{
+       return [NSString stringWithFormat:@"<%@ = %08X | FontName = %@, FontSize = %.1f>", [self class], self, fontName_, fontSize_];
+}
+@end
diff --git a/libs/cocos2d/CCLayer.h b/libs/cocos2d/CCLayer.h
new file mode 100644 (file)
index 0000000..f4c9860
--- /dev/null
@@ -0,0 +1,293 @@
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2008-2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+
+
+#import <Availability.h>
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+#import <UIKit/UIKit.h>                                        // Needed for UIAccelerometerDelegate
+#import "Platforms/iOS/CCTouchDelegateProtocol.h"              // Touches only supported on iOS
+#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED)
+#import "Platforms/Mac/CCEventDispatcher.h"
+#endif
+
+#import "CCProtocols.h"
+#import "CCNode.h"
+
+#pragma mark -
+#pragma mark CCLayer
+
+/** CCLayer is a subclass of CCNode that implements the TouchEventsDelegate protocol.
+ All features from CCNode are valid, plus the following new features:
+ - It can receive iPhone Touches
+ - It can receive Accelerometer input
+*/
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+@interface CCLayer : CCNode <UIAccelerometerDelegate, CCStandardTouchDelegate, CCTargetedTouchDelegate>
+{
+       BOOL isTouchEnabled_;
+       BOOL isAccelerometerEnabled_;
+}
+/** If isTouchEnabled, this method is called onEnter. Override it to change the
+ way CCLayer receives touch events.
+ ( Default: [[TouchDispatcher sharedDispatcher] addStandardDelegate:self priority:0] )
+ Example:
+     -(void) registerWithTouchDispatcher
+     {
+        [[TouchDispatcher sharedDispatcher] addTargetedDelegate:self priority:INT_MIN+1 swallowsTouches:YES];
+     }
+ Valid only on iOS. Not valid on Mac.
+ @since v0.8.0
+ */
+-(void) registerWithTouchDispatcher;
+
+/** whether or not it will receive Touch events.
+ You can enable / disable touch events with this property.
+ Only the touches of this node will be affected. This "method" is not propagated to it's children.
+ Valid on iOS and Mac OS X v10.6 and later.
+
+ @since v0.8.1
+ */
+@property(nonatomic,assign) BOOL isTouchEnabled;
+/** whether or not it will receive Accelerometer events
+ You can enable / disable accelerometer events with this property.
+ Valid only on iOS. Not valid on Mac.
+
+ @since v0.8.1
+ */
+@property(nonatomic,assign) BOOL isAccelerometerEnabled;
+
+#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED)
+
+
+@interface CCLayer : CCNode <CCKeyboardEventDelegate, CCMouseEventDelegate, CCTouchEventDelegate>
+{
+       BOOL    isMouseEnabled_;
+       BOOL    isKeyboardEnabled_;
+       BOOL    isTouchEnabled_;
+}
+
+/** whether or not it will receive mouse events.
+ Valind only Mac. Not valid on iOS
+ */
+@property (nonatomic, readwrite) BOOL isMouseEnabled;
+
+/** whether or not it will receive keyboard events.
+ Valind only Mac. Not valid on iOS
+ */
+@property (nonatomic, readwrite) BOOL isKeyboardEnabled;
+
+/** whether or not it will receive touch events.
+ Valid on iOS and Mac OS X v10.6 and later.
+ */
+@property (nonatomic, readwrite) BOOL isTouchEnabled;
+
+/** priority of the mouse event delegate.
+ Default 0.
+ Override this method to set another priority.
+ Valind only Mac. Not valid on iOS 
+ */
+-(NSInteger) mouseDelegatePriority;
+
+/** priority of the keyboard event delegate.
+ Default 0.
+ Override this method to set another priority.
+ Valind only Mac. Not valid on iOS 
+ */
+-(NSInteger) keyboardDelegatePriority;
+
+/** priority of the touch event delegate.
+ Default 0.
+ Override this method to set another priority.
+ Valind only Mac. Not valid on iOS 
+ */
+-(NSInteger) touchDelegatePriority;
+
+#endif // mac
+
+
+@end
+
+#pragma mark -
+#pragma mark CCLayerColor
+
+/** CCLayerColor is a subclass of CCLayer that implements the CCRGBAProtocol protocol.
+ All features from CCLayer are valid, plus the following new features:
+ - opacity
+ - RGB colors
+ */
+@interface CCLayerColor : CCLayer <CCRGBAProtocol, CCBlendProtocol>
+{
+       GLubyte         opacity_;
+       ccColor3B       color_; 
+       ccVertex2F      squareVertices_[4];
+       ccColor4B       squareColors_[4];
+       
+       ccBlendFunc     blendFunc_;
+}
+
+/** creates a CCLayer with color, width and height in Points*/
++ (id) layerWithColor: (ccColor4B)color width:(GLfloat)w height:(GLfloat)h;
+/** creates a CCLayer with color. Width and height are the window size. */
++ (id) layerWithColor: (ccColor4B)color;
+
+/** initializes a CCLayer with color, width and height in Points */
+- (id) initWithColor:(ccColor4B)color width:(GLfloat)w height:(GLfloat)h;
+/** initializes a CCLayer with color. Width and height are the window size. */
+- (id) initWithColor:(ccColor4B)color;
+
+/** change width in Points */
+-(void) changeWidth: (GLfloat)w;
+/** change height in Points */
+-(void) changeHeight: (GLfloat)h;
+/** change width and height in Points
+ @since v0.8
+ */
+-(void) changeWidth:(GLfloat)w height:(GLfloat)h;
+
+/** Opacity: conforms to CCRGBAProtocol protocol */
+@property (nonatomic,readonly) GLubyte opacity;
+/** Opacity: conforms to CCRGBAProtocol protocol */
+@property (nonatomic,readonly) ccColor3B color;
+/** BlendFunction. Conforms to CCBlendProtocol protocol */
+@property (nonatomic,readwrite) ccBlendFunc blendFunc;
+@end
+
+/** CCColorLayer
+ It is the same as CCLayerColor.
+ @deprecated Use CCLayerColor instead. This class will be removed in v1.0.1
+ */
+DEPRECATED_ATTRIBUTE @interface CCColorLayer : CCLayerColor
+@end
+
+#pragma mark -
+#pragma mark CCLayerGradient
+
+/** CCLayerGradient is a subclass of CCLayerColor that draws gradients across
+the background.
+
+ All features from CCLayerColor are valid, plus the following new features:
+ - direction
+ - final color
+ - interpolation mode
+ Color is interpolated between the startColor and endColor along the given
+ vector (starting at the origin, ending at the terminus).  If no vector is
+ supplied, it defaults to (0, -1) -- a fade from top to bottom.
+ If 'compressedInterpolation' is disabled, you will not see either the start or end color for
+ non-cardinal vectors; a smooth gradient implying both end points will be still
+ be drawn, however.
+ If ' compressedInterpolation' is enabled (default mode) you will see both the start and end colors of the gradient.
+ @since v0.99.5
+ */
+@interface CCLayerGradient : CCLayerColor
+{
+       ccColor3B endColor_;
+       GLubyte startOpacity_;
+       GLubyte endOpacity_;
+       CGPoint vector_;
+       BOOL    compressedInterpolation_;
+}
+
+/** Creates a full-screen CCLayer with a gradient between start and end. */
++ (id) layerWithColor: (ccColor4B) start fadingTo: (ccColor4B) end;
+/** Creates a full-screen CCLayer with a gradient between start and end in the direction of v. */
++ (id) layerWithColor: (ccColor4B) start fadingTo: (ccColor4B) end alongVector: (CGPoint) v;
+
+/** Initializes the CCLayer with a gradient between start and end. */
+- (id) initWithColor: (ccColor4B) start fadingTo: (ccColor4B) end;
+/** Initializes the CCLayer with a gradient between start and end in the direction of v. */
+- (id) initWithColor: (ccColor4B) start fadingTo: (ccColor4B) end alongVector: (CGPoint) v;
+
+/** The starting color. */
+@property (nonatomic, readwrite) ccColor3B startColor;
+/** The ending color. */
+@property (nonatomic, readwrite) ccColor3B endColor;
+/** The starting opacity. */
+@property (nonatomic, readwrite) GLubyte startOpacity;
+/** The ending color. */
+@property (nonatomic, readwrite) GLubyte endOpacity;
+/** The vector along which to fade color. */
+@property (nonatomic, readwrite) CGPoint vector;
+/** Whether or not the interpolation will be compressed in order to display all the colors of the gradient both in canonical and non canonical vectors
+ Default: YES
+ */
+@property (nonatomic, readwrite) BOOL compressedInterpolation;
+@end
+
+#pragma mark -
+#pragma mark CCLayerMultiplex
+
+/** CCLayerMultiplex is a CCLayer with the ability to multiplex it's children.
+ Features:
+   - It supports one or more children
+   - Only one children will be active a time
+ */
+@interface CCLayerMultiplex : CCLayer
+{
+       unsigned int enabledLayer_;
+       NSMutableArray *layers_;
+}
+
+/** creates a CCMultiplexLayer with one or more layers using a variable argument list. */
++(id) layerWithLayers: (CCLayer*) layer, ... NS_REQUIRES_NIL_TERMINATION;
+/** initializes a MultiplexLayer with one or more layers using a variable argument list. */
+-(id) initWithLayers: (CCLayer*) layer vaList:(va_list) params;
+/** switches to a certain layer indexed by n. 
+ The current (old) layer will be removed from it's parent with 'cleanup:YES'.
+ */
+-(void) switchTo: (unsigned int) n;
+/** release the current layer and switches to another layer indexed by n.
+ The current (old) layer will be removed from it's parent with 'cleanup:YES'.
+ */
+-(void) switchToAndReleaseMe: (unsigned int) n;
+@end
+
+/** CCMultiplexLayer
+ It is the same as CCLayerMultiplex.
+ @deprecated Use CCLayerMultiplex instead. This class will be removed in v1.0.1
+ */
+DEPRECATED_ATTRIBUTE  @interface CCMultiplexLayer : CCLayerMultiplex
+@end
+
diff --git a/libs/cocos2d/CCLayer.m b/libs/cocos2d/CCLayer.m
new file mode 100644 (file)
index 0000000..68cc14f
--- /dev/null
@@ -0,0 +1,619 @@
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2008-2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+
+#import <stdarg.h>
+
+#import "Platforms/CCGL.h"
+
+#import "CCLayer.h"
+#import "CCDirector.h"
+#import "ccMacros.h"
+#import "Support/CGPointExtension.h"
+
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+#import "Platforms/iOS/CCTouchDispatcher.h"
+#import "Platforms/iOS/CCDirectorIOS.h"
+#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED)
+#import "Platforms/Mac/CCEventDispatcher.h"
+#endif
+
+#pragma mark -
+#pragma mark Layer
+
+@implementation CCLayer
+
+#pragma mark Layer - Init
+-(id) init
+{
+       if( (self=[super init]) ) {
+       
+               CGSize s = [[CCDirector sharedDirector] winSize];
+               anchorPoint_ = ccp(0.5f, 0.5f);
+               [self setContentSize:s];
+               self.isRelativeAnchorPoint = NO;
+
+               isTouchEnabled_ = NO;
+
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+               isAccelerometerEnabled_ = NO;
+#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED)
+               isMouseEnabled_ = NO;
+               isKeyboardEnabled_ = NO;
+#endif
+       }
+       
+       return self;
+}
+
+#pragma mark Layer - Touch and Accelerometer related
+
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+-(void) registerWithTouchDispatcher
+{
+       [[CCTouchDispatcher sharedDispatcher] addStandardDelegate:self priority:0];
+}
+
+-(BOOL) isAccelerometerEnabled
+{
+       return isAccelerometerEnabled_;
+}
+
+-(void) setIsAccelerometerEnabled:(BOOL)enabled
+{
+       if( enabled != isAccelerometerEnabled_ ) {
+               isAccelerometerEnabled_ = enabled;
+               if( isRunning_ ) {
+                       if( enabled )
+                               [[UIAccelerometer sharedAccelerometer] setDelegate:self];
+                       else
+                               [[UIAccelerometer sharedAccelerometer] setDelegate:nil];
+               }
+       }
+}
+
+-(BOOL) isTouchEnabled
+{
+       return isTouchEnabled_;
+}
+
+-(void) setIsTouchEnabled:(BOOL)enabled
+{
+       if( isTouchEnabled_ != enabled ) {
+               isTouchEnabled_ = enabled;
+               if( isRunning_ ) {
+                       if( enabled )
+                               [self registerWithTouchDispatcher];
+                       else
+                               [[CCTouchDispatcher sharedDispatcher] removeDelegate:self];
+               }
+       }
+}
+
+#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED)
+
+#pragma mark CCLayer - Mouse, Keyboard & Touch events
+
+-(NSInteger) mouseDelegatePriority
+{
+       return 0;
+}
+
+-(BOOL) isMouseEnabled
+{
+       return isMouseEnabled_;
+}
+
+-(void) setIsMouseEnabled:(BOOL)enabled
+{
+       if( isMouseEnabled_ != enabled ) {
+               isMouseEnabled_ = enabled;
+               
+               if( isRunning_ ) {
+                       if( enabled )
+                               [[CCEventDispatcher sharedDispatcher] addMouseDelegate:self priority:[self mouseDelegatePriority]];
+                       else
+                               [[CCEventDispatcher sharedDispatcher] removeMouseDelegate:self];
+               }
+       }
+}
+
+-(NSInteger) keyboardDelegatePriority
+{
+       return 0;
+}
+
+-(BOOL) isKeyboardEnabled
+{
+       return isKeyboardEnabled_;
+}
+
+-(void) setIsKeyboardEnabled:(BOOL)enabled
+{
+       if( isKeyboardEnabled_ != enabled ) {
+               isKeyboardEnabled_ = enabled;
+               
+               if( isRunning_ ) {
+                       if( enabled )
+                               [[CCEventDispatcher sharedDispatcher] addKeyboardDelegate:self priority:[self keyboardDelegatePriority] ];
+                       else
+                               [[CCEventDispatcher sharedDispatcher] removeKeyboardDelegate:self];
+               }
+       }
+}
+
+-(NSInteger) touchDelegatePriority
+{
+       return 0;
+}
+
+-(BOOL) isTouchEnabled
+{
+       return isTouchEnabled_;
+}
+
+-(void) setIsTouchEnabled:(BOOL)enabled
+{
+       if( isTouchEnabled_ != enabled ) {
+               isTouchEnabled_ = enabled;
+               if( isRunning_ ) {
+                       if( enabled )
+                               [[CCEventDispatcher sharedDispatcher] addTouchDelegate:self priority:[self touchDelegatePriority]];
+                       else
+                               [[CCEventDispatcher sharedDispatcher] removeTouchDelegate:self];
+               }
+       }
+}
+
+
+#endif // Mac
+
+
+#pragma mark Layer - Callbacks
+-(void) onEnter
+{
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+       // register 'parent' nodes first
+       // since events are propagated in reverse order
+       if (isTouchEnabled_)
+               [self registerWithTouchDispatcher];
+
+#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED)
+       if( isMouseEnabled_ )
+               [[CCEventDispatcher sharedDispatcher] addMouseDelegate:self priority:[self mouseDelegatePriority]];
+       
+       if( isKeyboardEnabled_)
+               [[CCEventDispatcher sharedDispatcher] addKeyboardDelegate:self priority:[self keyboardDelegatePriority]];
+
+       if( isTouchEnabled_)
+               [[CCEventDispatcher sharedDispatcher] addTouchDelegate:self priority:[self touchDelegatePriority]];
+
+#endif
+       
+       // then iterate over all the children
+       [super onEnter];
+}
+
+// issue #624.
+// Can't register mouse, touches here because of #issue #1018, and #1021
+-(void) onEnterTransitionDidFinish
+{
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+       if( isAccelerometerEnabled_ )
+               [[UIAccelerometer sharedAccelerometer] setDelegate:self];
+#endif
+       
+       [super onEnterTransitionDidFinish];
+}
+
+
+-(void) onExit
+{
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+       if( isTouchEnabled_ )
+               [[CCTouchDispatcher sharedDispatcher] removeDelegate:self];
+       
+       if( isAccelerometerEnabled_ )
+               [[UIAccelerometer sharedAccelerometer] setDelegate:nil];
+
+#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED)
+       if( isMouseEnabled_ )
+               [[CCEventDispatcher sharedDispatcher] removeMouseDelegate:self];
+       
+       if( isKeyboardEnabled_ )
+               [[CCEventDispatcher sharedDispatcher] removeKeyboardDelegate:self];
+
+       if( isTouchEnabled_ )
+               [[CCEventDispatcher sharedDispatcher] removeTouchDelegate:self];
+
+#endif
+       
+       [super onExit];
+}
+
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+-(BOOL) ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event
+{
+       NSAssert(NO, @"Layer#ccTouchBegan override me");
+       return YES;
+}
+#endif
+@end
+
+#pragma mark -
+#pragma mark LayerColor
+
+@interface CCLayerColor (Private)
+-(void) updateColor;
+@end
+
+@implementation CCLayerColor
+
+// Opacity and RGB color protocol
+@synthesize opacity = opacity_, color = color_;
+@synthesize blendFunc = blendFunc_;
+
+
++ (id) layerWithColor:(ccColor4B)color width:(GLfloat)w  height:(GLfloat) h
+{
+       return [[[self alloc] initWithColor:color width:w height:h] autorelease];
+}
+
++ (id) layerWithColor:(ccColor4B)color
+{
+       return [[(CCLayerColor*)[self alloc] initWithColor:color] autorelease];
+}
+
+- (id) initWithColor:(ccColor4B)color width:(GLfloat)w  height:(GLfloat) h
+{
+       if( (self=[super init]) ) {
+               
+               // default blend function
+               blendFunc_ = (ccBlendFunc) { CC_BLEND_SRC, CC_BLEND_DST };
+
+               color_.r = color.r;
+               color_.g = color.g;
+               color_.b = color.b;
+               opacity_ = color.a;
+               
+               for (NSUInteger i = 0; i<sizeof(squareVertices_) / sizeof( squareVertices_[0]); i++ ) {
+                       squareVertices_[i].x = 0.0f;
+                       squareVertices_[i].y = 0.0f;
+               }
+                               
+               [self updateColor];
+               [self setContentSize:CGSizeMake(w, h) ];
+       }
+       return self;
+}
+
+- (id) initWithColor:(ccColor4B)color
+{
+       CGSize s = [[CCDirector sharedDirector] winSize];
+       return [self initWithColor:color width:s.width height:s.height];
+}
+
+// override contentSize
+-(void) setContentSize: (CGSize) size
+{
+       squareVertices_[1].x = size.width * CC_CONTENT_SCALE_FACTOR();
+       squareVertices_[2].y = size.height * CC_CONTENT_SCALE_FACTOR();
+       squareVertices_[3].x = size.width * CC_CONTENT_SCALE_FACTOR();
+       squareVertices_[3].y = size.height * CC_CONTENT_SCALE_FACTOR();
+       
+       [super setContentSize:size];
+}
+
+- (void) changeWidth: (GLfloat) w height:(GLfloat) h
+{
+       [self setContentSize:CGSizeMake(w, h)];
+}
+
+-(void) changeWidth: (GLfloat) w
+{
+       [self setContentSize:CGSizeMake(w, contentSize_.height)];
+}
+
+-(void) changeHeight: (GLfloat) h
+{
+       [self setContentSize:CGSizeMake(contentSize_.width, h)];
+}
+
+- (void) updateColor
+{
+       for( NSUInteger i = 0; i < 4; i++ )
+       {
+               squareColors_[i].r = color_.r;
+               squareColors_[i].g = color_.g;
+               squareColors_[i].b = color_.b;
+               squareColors_[i].a = opacity_;
+       }
+}
+
+- (void)draw
+{              
+       // Default GL states: GL_TEXTURE_2D, GL_VERTEX_ARRAY, GL_COLOR_ARRAY, GL_TEXTURE_COORD_ARRAY
+       // Needed states: GL_VERTEX_ARRAY, GL_COLOR_ARRAY
+       // Unneeded states: GL_TEXTURE_2D, GL_TEXTURE_COORD_ARRAY
+       glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+       glDisable(GL_TEXTURE_2D);
+
+       glVertexPointer(2, GL_FLOAT, 0, squareVertices_);
+       glColorPointer(4, GL_UNSIGNED_BYTE, 0, squareColors_);
+       
+       
+       BOOL newBlend = blendFunc_.src != CC_BLEND_SRC || blendFunc_.dst != CC_BLEND_DST;
+       if( newBlend )
+               glBlendFunc( blendFunc_.src, blendFunc_.dst );
+       
+       else if( opacity_ != 255 ) {
+               newBlend = YES;
+               glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+       }
+       
+       glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
+       
+       if( newBlend )
+               glBlendFunc(CC_BLEND_SRC, CC_BLEND_DST);
+       
+       // restore default GL state
+       glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+       glEnable(GL_TEXTURE_2D);
+}
+
+#pragma mark Protocols
+// Color Protocol
+
+-(void) setColor:(ccColor3B)color
+{
+       color_ = color;
+       [self updateColor];
+}
+
+-(void) setOpacity: (GLubyte) o
+{
+       opacity_ = o;
+       [self updateColor];
+}
+@end
+
+// XXX Deprecated
+@implementation CCColorLayer
+@end
+
+
+#pragma mark -
+#pragma mark LayerGradient
+
+@implementation CCLayerGradient
+
+@synthesize startOpacity = startOpacity_;
+@synthesize endColor = endColor_, endOpacity = endOpacity_;
+@synthesize vector = vector_;
+
++ (id) layerWithColor: (ccColor4B) start fadingTo: (ccColor4B) end
+{
+    return [[[self alloc] initWithColor:start fadingTo:end] autorelease];
+}
+
++ (id) layerWithColor: (ccColor4B) start fadingTo: (ccColor4B) end alongVector: (CGPoint) v
+{
+    return [[[self alloc] initWithColor:start fadingTo:end alongVector:v] autorelease];
+}
+
+- (id) initWithColor: (ccColor4B) start fadingTo: (ccColor4B) end
+{
+    return [self initWithColor:start fadingTo:end alongVector:ccp(0, -1)];
+}
+
+- (id) initWithColor: (ccColor4B) start fadingTo: (ccColor4B) end alongVector: (CGPoint) v
+{
+       endColor_.r = end.r;
+       endColor_.g = end.g;
+       endColor_.b = end.b;
+       
+       endOpacity_             = end.a;
+       startOpacity_   = start.a;
+       vector_ = v;
+       
+       start.a = 255;
+       compressedInterpolation_ = YES;
+
+       return [super initWithColor:start];
+}
+
+- (void) updateColor
+{
+    [super updateColor];
+
+       float h = ccpLength(vector_);
+    if (h == 0)
+               return;
+
+       double c = sqrt(2);
+    CGPoint u = ccp(vector_.x / h, vector_.y / h);
+
+       // Compressed Interpolation mode
+       if( compressedInterpolation_ ) {
+               float h2 = 1 / ( fabsf(u.x) + fabsf(u.y) );
+               u = ccpMult(u, h2 * (float)c);
+       }
+       
+       float opacityf = (float)opacity_/255.0f;
+       
+    ccColor4B S = {
+               color_.r,
+               color_.g,
+               color_.b,
+               startOpacity_*opacityf
+       };
+
+    ccColor4B E = {
+               endColor_.r,
+               endColor_.g,
+               endColor_.b,
+               endOpacity_*opacityf
+       };
+
+
+    // (-1, -1)
+       squareColors_[0].r = E.r + (S.r - E.r) * ((c + u.x + u.y) / (2.0f * c));
+       squareColors_[0].g = E.g + (S.g - E.g) * ((c + u.x + u.y) / (2.0f * c));
+       squareColors_[0].b = E.b + (S.b - E.b) * ((c + u.x + u.y) / (2.0f * c));
+       squareColors_[0].a = E.a + (S.a - E.a) * ((c + u.x + u.y) / (2.0f * c));
+    // (1, -1)
+       squareColors_[1].r = E.r + (S.r - E.r) * ((c - u.x + u.y) / (2.0f * c));
+       squareColors_[1].g = E.g + (S.g - E.g) * ((c - u.x + u.y) / (2.0f * c));
+       squareColors_[1].b = E.b + (S.b - E.b) * ((c - u.x + u.y) / (2.0f * c));
+       squareColors_[1].a = E.a + (S.a - E.a) * ((c - u.x + u.y) / (2.0f * c));
+       // (-1, 1)
+       squareColors_[2].r = E.r + (S.r - E.r) * ((c + u.x - u.y) / (2.0f * c));
+       squareColors_[2].g = E.g + (S.g - E.g) * ((c + u.x - u.y) / (2.0f * c));
+       squareColors_[2].b = E.b + (S.b - E.b) * ((c + u.x - u.y) / (2.0f * c));
+       squareColors_[2].a = E.a + (S.a - E.a) * ((c + u.x - u.y) / (2.0f * c));
+       // (1, 1)
+       squareColors_[3].r = E.r + (S.r - E.r) * ((c - u.x - u.y) / (2.0f * c));
+       squareColors_[3].g = E.g + (S.g - E.g) * ((c - u.x - u.y) / (2.0f * c));
+       squareColors_[3].b = E.b + (S.b - E.b) * ((c - u.x - u.y) / (2.0f * c));
+       squareColors_[3].a = E.a + (S.a - E.a) * ((c - u.x - u.y) / (2.0f * c));
+}
+
+-(ccColor3B) startColor
+{
+       return color_;
+}
+
+-(void) setStartColor:(ccColor3B)colors
+{
+       [self setColor:colors];
+}
+
+-(void) setEndColor:(ccColor3B)colors
+{
+    endColor_ = colors;
+    [self updateColor];
+}
+
+-(void) setStartOpacity: (GLubyte) o
+{
+       startOpacity_ = o;
+    [self updateColor];
+}
+
+-(void) setEndOpacity: (GLubyte) o
+{
+    endOpacity_ = o;
+    [self updateColor];
+}
+
+-(void) setVector: (CGPoint) v
+{
+    vector_ = v;
+    [self updateColor];
+}
+
+-(BOOL) compressedInterpolation
+{
+       return compressedInterpolation_;
+}
+
+-(void) setCompressedInterpolation:(BOOL)compress
+{
+       compressedInterpolation_ = compress;
+       [self updateColor];
+}
+@end
+
+#pragma mark -
+#pragma mark MultiplexLayer
+
+@implementation CCLayerMultiplex
++(id) layerWithLayers: (CCLayer*) layer, ... 
+{
+       va_list args;
+       va_start(args,layer);
+       
+       id s = [[[self alloc] initWithLayers: layer vaList:args] autorelease];
+       
+       va_end(args);
+       return s;
+}
+
+-(id) initWithLayers: (CCLayer*) layer vaList:(va_list) params
+{
+       if( (self=[super init]) ) {
+       
+               layers_ = [[NSMutableArray arrayWithCapacity:5] retain];
+               
+               [layers_ addObject: layer];
+               
+               CCLayer *l = va_arg(params,CCLayer*);
+               while( l ) {
+                       [layers_ addObject: l];
+                       l = va_arg(params,CCLayer*);
+               }
+               
+               enabledLayer_ = 0;
+               [self addChild: [layers_ objectAtIndex: enabledLayer_]];
+       }
+       
+       return self;
+}
+
+-(void) dealloc
+{
+       [layers_ release];
+       [super dealloc];
+}
+
+-(void) switchTo: (unsigned int) n
+{
+       NSAssert( n < [layers_ count], @"Invalid index in MultiplexLayer switchTo message" );
+               
+       [self removeChild: [layers_ objectAtIndex:enabledLayer_] cleanup:YES];
+       
+       enabledLayer_ = n;
+       
+       [self addChild: [layers_ objectAtIndex:n]];             
+}
+
+-(void) switchToAndReleaseMe: (unsigned int) n
+{
+       NSAssert( n < [layers_ count], @"Invalid index in MultiplexLayer switchTo message" );
+       
+       [self removeChild: [layers_ objectAtIndex:enabledLayer_] cleanup:YES];
+       
+       [layers_ replaceObjectAtIndex:enabledLayer_ withObject:[NSNull null]];
+       
+       enabledLayer_ = n;
+       
+       [self addChild: [layers_ objectAtIndex:n]];             
+}
+@end
+
+// XXX Deprecated
+@implementation CCMultiplexLayer
+@end
diff --git a/libs/cocos2d/CCMenu.h b/libs/cocos2d/CCMenu.h
new file mode 100644 (file)
index 0000000..ef22343
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2008-2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+
+#import "CCMenuItem.h"
+#import "CCLayer.h"
+
+typedef enum  {
+       kCCMenuStateWaiting,
+       kCCMenuStateTrackingTouch
+} tCCMenuState;
+
+enum {
+       //* priority used by the menu for the touches
+       kCCMenuTouchPriority = -128,
+
+       //* priority used by the menu for the mouse
+       kCCMenuMousePriority = -128,
+};
+
+/** A CCMenu
+ * 
+ * Features and Limitation:
+ *  - You can add MenuItem objects in runtime using addChild:
+ *  - But the only accecpted children are MenuItem objects
+ */
+@interface CCMenu : CCLayer <CCRGBAProtocol>
+{
+       tCCMenuState state_;
+       CCMenuItem      *selectedItem_;
+       GLubyte         opacity_;
+       ccColor3B       color_;
+}
+
+/** creates a CCMenu with it's items */
++ (id) menuWithItems: (CCMenuItem*) item, ... NS_REQUIRES_NIL_TERMINATION;
+
+/** initializes a CCMenu with it's items */
+- (id) initWithItems: (CCMenuItem*) item vaList: (va_list) args;
+
+/** align items vertically */
+-(void) alignItemsVertically;
+/** align items vertically with padding
+ @since v0.7.2
+ */
+-(void) alignItemsVerticallyWithPadding:(float) padding;
+
+/** align items horizontally */
+-(void) alignItemsHorizontally;
+/** align items horizontally with padding
+ @since v0.7.2
+ */
+-(void) alignItemsHorizontallyWithPadding: (float) padding;
+
+
+/** align items in rows of columns */
+-(void) alignItemsInColumns: (NSNumber *) columns, ... NS_REQUIRES_NIL_TERMINATION;
+-(void) alignItemsInColumns: (NSNumber *) columns vaList: (va_list) args;
+
+/** align items in columns of rows */
+-(void) alignItemsInRows: (NSNumber *) rows, ... NS_REQUIRES_NIL_TERMINATION;
+-(void) alignItemsInRows: (NSNumber *) rows vaList: (va_list) args;
+
+
+/** conforms to CCRGBAProtocol protocol */
+@property (nonatomic,readonly) GLubyte opacity;
+/** conforms to CCRGBAProtocol protocol */
+@property (nonatomic,readonly) ccColor3B color;
+
+@end
diff --git a/libs/cocos2d/CCMenu.m b/libs/cocos2d/CCMenu.m
new file mode 100644 (file)
index 0000000..a624154
--- /dev/null
@@ -0,0 +1,523 @@
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2008-2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+
+
+#import "CCMenu.h"
+#import "CCDirector.h"
+#import "Support/CGPointExtension.h"
+#import "ccMacros.h"
+
+#import <Availability.h>
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+#import "Platforms/iOS/CCDirectorIOS.h"
+#import "Platforms/iOS/CCTouchDispatcher.h"
+#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED)
+#import "Platforms/Mac/MacGLView.h"
+#import "Platforms/Mac/CCDirectorMac.h"
+#endif
+
+enum {
+       kDefaultPadding =  5,
+};
+
+@implementation CCMenu
+
+@synthesize opacity = opacity_, color = color_;
+
+- (id) init
+{
+       NSAssert(NO, @"CCMenu: Init not supported.");
+       [self release];
+       return nil;     
+}
+
++(id) menuWithItems: (CCMenuItem*) item, ...
+{
+       va_list args;
+       va_start(args,item);
+       
+       id s = [[[self alloc] initWithItems: item vaList:args] autorelease];
+       
+       va_end(args);
+       return s;
+}
+
+-(id) initWithItems: (CCMenuItem*) item vaList: (va_list) args
+{
+       if( (self=[super init]) ) {
+
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+               self.isTouchEnabled = YES;
+#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED)
+               self.isMouseEnabled = YES;
+#endif
+               
+               // menu in the center of the screen
+               CGSize s = [[CCDirector sharedDirector] winSize];
+               
+               self.isRelativeAnchorPoint = NO;
+               anchorPoint_ = ccp(0.5f, 0.5f);
+               [self setContentSize:s];
+               
+               // XXX: in v0.7, winSize should return the visible size
+               // XXX: so the bar calculation should be done there
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+               CGRect r = [[UIApplication sharedApplication] statusBarFrame];
+               ccDeviceOrientation orientation = [[CCDirector sharedDirector] deviceOrientation];
+               if( orientation == CCDeviceOrientationLandscapeLeft || orientation == CCDeviceOrientationLandscapeRight )
+                       s.height -= r.size.width;
+               else
+                       s.height -= r.size.height;
+#endif
+               self.position = ccp(s.width/2, s.height/2);
+
+               int z=0;
+               
+               if (item) {
+                       [self addChild: item z:z];
+                       CCMenuItem *i = va_arg(args, CCMenuItem*);
+                       while(i) {
+                               z++;
+                               [self addChild: i z:z];
+                               i = va_arg(args, CCMenuItem*);
+                       }
+               }
+       //      [self alignItemsVertically];
+               
+               selectedItem_ = nil;
+               state_ = kCCMenuStateWaiting;
+       }
+       
+       return self;
+}
+
+-(void) dealloc
+{
+       [super dealloc];
+}
+
+/*
+ * override add:
+ */
+-(void) addChild:(CCMenuItem*)child z:(NSInteger)z tag:(NSInteger) aTag
+{
+       NSAssert( [child isKindOfClass:[CCMenuItem class]], @"Menu only supports MenuItem objects as children");
+       [super addChild:child z:z tag:aTag];
+}
+
+- (void) onExit
+{
+       if(state_ == kCCMenuStateTrackingTouch)
+       {
+               [selectedItem_ unselected];             
+               state_ = kCCMenuStateWaiting;
+               selectedItem_ = nil;
+       }
+       [super onExit];
+}
+       
+#pragma mark Menu - Touches
+
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+-(void) registerWithTouchDispatcher
+{
+       [[CCTouchDispatcher sharedDispatcher] addTargetedDelegate:self priority:kCCMenuTouchPriority swallowsTouches:YES];
+}
+
+-(CCMenuItem *) itemForTouch: (UITouch *) touch
+{
+       CGPoint touchLocation = [touch locationInView: [touch view]];
+       touchLocation = [[CCDirector sharedDirector] convertToGL: touchLocation];
+       
+       CCMenuItem* item;
+       CCARRAY_FOREACH(children_, item){
+               // ignore invisible and disabled items: issue #779, #866
+               if ( [item visible] && [item isEnabled] ) {
+                       
+                       CGPoint local = [item convertToNodeSpace:touchLocation];
+                       CGRect r = [item rect];
+                       r.origin = CGPointZero;
+                       
+                       if( CGRectContainsPoint( r, local ) )
+                               return item;
+               }
+       }
+       return nil;
+}
+
+-(BOOL) ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event
+{
+       if( state_ != kCCMenuStateWaiting || !visible_ )
+               return NO;
+       
+       selectedItem_ = [self itemForTouch:touch];
+       [selectedItem_ selected];
+       
+       if( selectedItem_ ) {
+               state_ = kCCMenuStateTrackingTouch;
+               return YES;
+       }
+       return NO;
+}
+
+-(void) ccTouchEnded:(UITouch *)touch withEvent:(UIEvent *)event
+{
+       NSAssert(state_ == kCCMenuStateTrackingTouch, @"[Menu ccTouchEnded] -- invalid state");
+       
+       [selectedItem_ unselected];
+       [selectedItem_ activate];
+       
+       state_ = kCCMenuStateWaiting;
+}
+
+-(void) ccTouchCancelled:(UITouch *)touch withEvent:(UIEvent *)event
+{
+       NSAssert(state_ == kCCMenuStateTrackingTouch, @"[Menu ccTouchCancelled] -- invalid state");
+       
+       [selectedItem_ unselected];
+       
+       state_ = kCCMenuStateWaiting;
+}
+
+-(void) ccTouchMoved:(UITouch *)touch withEvent:(UIEvent *)event
+{
+       NSAssert(state_ == kCCMenuStateTrackingTouch, @"[Menu ccTouchMoved] -- invalid state");
+       
+       CCMenuItem *currentItem = [self itemForTouch:touch];
+       
+       if (currentItem != selectedItem_) {
+               [selectedItem_ unselected];
+               selectedItem_ = currentItem;
+               [selectedItem_ selected];
+       }
+}
+
+#pragma mark Menu - Mouse
+
+#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED)
+
+-(NSInteger) mouseDelegatePriority
+{
+       return kCCMenuMousePriority+1;
+}
+
+-(CCMenuItem *) itemForMouseEvent: (NSEvent *) event
+{
+       CGPoint location = [(CCDirectorMac*)[CCDirector sharedDirector] convertEventToGL:event];
+       
+       CCMenuItem* item;
+       CCARRAY_FOREACH(children_, item){
+               // ignore invisible and disabled items: issue #779, #866
+               if ( [item visible] && [item isEnabled] ) {
+                       
+                       CGPoint local = [item convertToNodeSpace:location];
+                       
+                       CGRect r = [item rect];
+                       r.origin = CGPointZero;
+                       
+                       if( CGRectContainsPoint( r, local ) )
+                               return item;
+               }
+       }
+       return nil;
+}
+
+-(BOOL) ccMouseUp:(NSEvent *)event
+{
+       if( ! visible_ )
+               return NO;
+
+       if(state_ == kCCMenuStateTrackingTouch) {
+               if( selectedItem_ ) {
+                       [selectedItem_ unselected];
+                       [selectedItem_ activate];
+               }
+               state_ = kCCMenuStateWaiting;
+               
+               return YES;
+       }
+       return NO;
+}
+
+-(BOOL) ccMouseDown:(NSEvent *)event
+{
+       if( ! visible_ )
+               return NO;
+       
+       selectedItem_ = [self itemForMouseEvent:event];
+       [selectedItem_ selected];
+
+       if( selectedItem_ ) {
+               state_ = kCCMenuStateTrackingTouch;
+               return YES;
+       }
+
+       return NO;      
+}
+
+-(BOOL) ccMouseDragged:(NSEvent *)event
+{
+       if( ! visible_ )
+               return NO;
+
+       if(state_ == kCCMenuStateTrackingTouch) {
+               CCMenuItem *currentItem = [self itemForMouseEvent:event];
+               
+               if (currentItem != selectedItem_) {
+                       [selectedItem_ unselected];
+                       selectedItem_ = currentItem;
+                       [selectedItem_ selected];
+               }
+               
+               return YES;
+       }
+       return NO;
+}
+
+#endif // Mac Mouse support
+
+#pragma mark Menu - Alignment
+-(void) alignItemsVertically
+{
+       return [self alignItemsVerticallyWithPadding:kDefaultPadding];
+}
+-(void) alignItemsVerticallyWithPadding:(float)padding
+{
+       float height = -padding;
+       
+       CCMenuItem *item;
+       CCARRAY_FOREACH(children_, item)
+           height += item.contentSize.height * item.scaleY + padding;
+
+       float y = height / 2.0f;
+       
+       CCARRAY_FOREACH(children_, item) {
+               CGSize itemSize = item.contentSize;
+           [item setPosition:ccp(0, y - itemSize.height * item.scaleY / 2.0f)];
+           y -= itemSize.height * item.scaleY + padding;
+       }
+}
+
+-(void) alignItemsHorizontally
+{
+       return [self alignItemsHorizontallyWithPadding:kDefaultPadding];
+}
+
+-(void) alignItemsHorizontallyWithPadding:(float)padding
+{
+       
+       float width = -padding;
+       CCMenuItem *item;
+       CCARRAY_FOREACH(children_, item)
+           width += item.contentSize.width * item.scaleX + padding;
+
+       float x = -width / 2.0f;
+       
+       CCARRAY_FOREACH(children_, item){
+               CGSize itemSize = item.contentSize;
+               [item setPosition:ccp(x + itemSize.width * item.scaleX / 2.0f, 0)];
+               x += itemSize.width * item.scaleX + padding;
+       }
+}
+
+-(void) alignItemsInColumns: (NSNumber *) columns, ...
+{
+       va_list args;
+       va_start(args, columns);
+       
+       [self alignItemsInColumns:columns vaList:args];
+       
+       va_end(args);
+}
+
+-(void) alignItemsInColumns: (NSNumber *) columns vaList: (va_list) args
+{
+       NSMutableArray *rows = [[NSMutableArray alloc] initWithObjects:columns, nil];
+       columns = va_arg(args, NSNumber*);
+       while(columns) {
+        [rows addObject:columns];
+               columns = va_arg(args, NSNumber*);
+       }
+    
+       int height = -5;
+    NSUInteger row = 0, rowHeight = 0, columnsOccupied = 0, rowColumns;
+       CCMenuItem *item;
+       CCARRAY_FOREACH(children_, item){
+               NSAssert( row < [rows count], @"Too many menu items for the amount of rows/columns.");
+        
+               rowColumns = [(NSNumber *) [rows objectAtIndex:row] unsignedIntegerValue];
+               NSAssert( rowColumns, @"Can't have zero columns on a row");
+        
+               rowHeight = fmaxf(rowHeight, item.contentSize.height);
+               ++columnsOccupied;
+        
+               if(columnsOccupied >= rowColumns) {
+                       height += rowHeight + 5;
+
+                       columnsOccupied = 0;
+                       rowHeight = 0;
+                       ++row;
+               }
+       }
+       NSAssert( !columnsOccupied, @"Too many rows/columns for available menu items." );
+
+       CGSize winSize = [[CCDirector sharedDirector] winSize];
+    
+       row = 0; rowHeight = 0; rowColumns = 0;
+       float w, x, y = height / 2;
+       CCARRAY_FOREACH(children_, item) {
+               if(rowColumns == 0) {
+                       rowColumns = [(NSNumber *) [rows objectAtIndex:row] unsignedIntegerValue];
+                       w = winSize.width / (1 + rowColumns);
+                       x = w;
+               }
+
+               CGSize itemSize = item.contentSize;
+               rowHeight = fmaxf(rowHeight, itemSize.height);
+               [item setPosition:ccp(x - winSize.width / 2,
+                                                         y - itemSize.height / 2)];
+            
+               x += w + 10;
+               ++columnsOccupied;
+               
+               if(columnsOccupied >= rowColumns) {
+                       y -= rowHeight + 5;
+                       
+                       columnsOccupied = 0;
+                       rowColumns = 0;
+                       rowHeight = 0;
+                       ++row;
+               }
+       }
+
+       [rows release];
+}
+
+-(void) alignItemsInRows: (NSNumber *) rows, ...
+{
+       va_list args;
+       va_start(args, rows);
+       
+       [self alignItemsInRows:rows vaList:args];
+       
+       va_end(args);
+}
+
+-(void) alignItemsInRows: (NSNumber *) rows vaList: (va_list) args
+{
+       NSMutableArray *columns = [[NSMutableArray alloc] initWithObjects:rows, nil];
+       rows = va_arg(args, NSNumber*);
+       while(rows) {
+               [columns addObject:rows];
+               rows = va_arg(args, NSNumber*);
+       }
+
+       NSMutableArray *columnWidths = [[NSMutableArray alloc] init];
+       NSMutableArray *columnHeights = [[NSMutableArray alloc] init];
+       
+       int width = -10, columnHeight = -5;
+       NSUInteger column = 0, columnWidth = 0, rowsOccupied = 0, columnRows;
+       CCMenuItem *item;
+       CCARRAY_FOREACH(children_, item){
+               NSAssert( column < [columns count], @"Too many menu items for the amount of rows/columns.");
+               
+               columnRows = [(NSNumber *) [columns objectAtIndex:column] unsignedIntegerValue];
+               NSAssert( columnRows, @"Can't have zero rows on a column");
+               
+               CGSize itemSize = item.contentSize;
+               columnWidth = fmaxf(columnWidth, itemSize.width);
+               columnHeight += itemSize.height + 5;
+               ++rowsOccupied;
+               
+               if(rowsOccupied >= columnRows) {
+                       [columnWidths addObject:[NSNumber numberWithUnsignedInteger:columnWidth]];
+                       [columnHeights addObject:[NSNumber numberWithUnsignedInteger:columnHeight]];
+                       width += columnWidth + 10;
+                       
+                       rowsOccupied = 0;
+                       columnWidth = 0;
+                       columnHeight = -5;
+                       ++column;
+               }
+       }
+       NSAssert( !rowsOccupied, @"Too many rows/columns for available menu items.");
+       
+       CGSize winSize = [[CCDirector sharedDirector] winSize];
+       
+       column = 0; columnWidth = 0; columnRows = 0;
+       float x = -width / 2, y;
+       
+       CCARRAY_FOREACH(children_, item){
+               if(columnRows == 0) {
+                       columnRows = [(NSNumber *) [columns objectAtIndex:column] unsignedIntegerValue];
+                       y = ([(NSNumber *) [columnHeights objectAtIndex:column] intValue] + winSize.height) / 2;
+               }
+               
+               CGSize itemSize = item.contentSize;
+               columnWidth = fmaxf(columnWidth, itemSize.width);
+               [item setPosition:ccp(x + [(NSNumber *) [columnWidths objectAtIndex:column] unsignedIntegerValue] / 2,
+                                                         y - winSize.height / 2)];
+               
+               y -= itemSize.height + 10;
+               ++rowsOccupied;
+               
+               if(rowsOccupied >= columnRows) {
+                       x += columnWidth + 5;
+                       
+                       rowsOccupied = 0;
+                       columnRows = 0;
+                       columnWidth = 0;
+                       ++column;
+               }
+       }
+       
+       [columns release];
+       [columnWidths release];
+       [columnHeights release];
+}
+
+#pragma mark Menu - Opacity Protocol
+
+/** Override synthesized setOpacity to recurse items */
+- (void) setOpacity:(GLubyte)newOpacity
+{
+       opacity_ = newOpacity;
+       
+       id<CCRGBAProtocol> item;
+       CCARRAY_FOREACH(children_, item)
+               [item setOpacity:opacity_];
+}
+
+-(void) setColor:(ccColor3B)color
+{
+       color_ = color;
+       
+       id<CCRGBAProtocol> item;
+       CCARRAY_FOREACH(children_, item)
+               [item setColor:color_];
+}
+@end
diff --git a/libs/cocos2d/CCMenuItem.h b/libs/cocos2d/CCMenuItem.h
new file mode 100644 (file)
index 0000000..d78af7f
--- /dev/null
@@ -0,0 +1,363 @@
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2008-2011 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+#import "CCBlockSupport.h"
+
+#import "CCNode.h"
+#import "CCProtocols.h"
+
+@class CCSprite;
+
+#define kItemSize 32
+
+#pragma mark -
+#pragma mark CCMenuItem
+/** CCMenuItem base class
+ *
+ *  Subclass CCMenuItem (or any subclass) to create your custom CCMenuItem objects.
+ */
+@interface CCMenuItem : CCNode
+{
+       NSInvocation *invocation;
+#if NS_BLOCKS_AVAILABLE
+       // used for menu items using a block
+       void (^block_)(id sender);
+#endif
+       
+       BOOL isEnabled_;
+       BOOL isSelected_;
+}
+
+/** returns whether or not the item is selected
+@since v0.8.2
+*/
+@property (nonatomic,readonly) BOOL isSelected;
+
+/** Creates a CCMenuItem with a target/selector */
++(id) itemWithTarget:(id)target selector:(SEL)selector;
+
+/** Initializes a CCMenuItem with a target/selector */
+-(id) initWithTarget:(id)target selector:(SEL)selector;
+
+#if NS_BLOCKS_AVAILABLE
+/** Creates a CCMenuItem with the specified block.
+ The block will be "copied".
+ */
++(id) itemWithBlock:(void(^)(id sender))block;
+
+/** Initializes a CCMenuItem with the specified block.
+ The block will be "copied".
+*/
+-(id) initWithBlock:(void(^)(id sender))block;
+#endif
+
+/** Returns the outside box in points */
+-(CGRect) rect;
+
+/** Activate the item */
+-(void) activate;
+
+/** The item was selected (not activated), similar to "mouse-over" */
+-(void) selected;
+
+/** The item was unselected */
+-(void) unselected;
+
+/** Enable or disabled the CCMenuItem */
+-(void) setIsEnabled:(BOOL)enabled;
+/** Returns whether or not the CCMenuItem is enabled */
+-(BOOL) isEnabled;
+@end
+
+#pragma mark -
+#pragma mark CCMenuItemLabel
+
+/** An abstract class for "label" CCMenuItemLabel items 
+ Any CCNode that supports the CCLabelProtocol protocol can be added.
+ Supported nodes:
+   - CCLabelBMFont
+   - CCLabelAtlas
+   - CCLabelTTF
+ */
+@interface CCMenuItemLabel : CCMenuItem  <CCRGBAProtocol>
+{
+       CCNode<CCLabelProtocol, CCRGBAProtocol> *label_;
+       ccColor3B       colorBackup;
+       ccColor3B       disabledColor_;
+       float           originalScale_;
+}
+
+/** the color that will be used to disable the item */
+@property (nonatomic,readwrite) ccColor3B disabledColor;
+
+/** Label that is rendered. It can be any CCNode that implements the CCLabelProtocol */
+@property (nonatomic,readwrite,assign) CCNode<CCLabelProtocol, CCRGBAProtocol>* label;
+
+/** creates a CCMenuItemLabel with a Label. Target and selector will be nill */
++(id) itemWithLabel:(CCNode<CCLabelProtocol,CCRGBAProtocol>*)label;
+
+/** creates a CCMenuItemLabel with a Label, target and selector */
++(id) itemWithLabel:(CCNode<CCLabelProtocol,CCRGBAProtocol>*)label target:(id)target selector:(SEL)selector;
+
+/** initializes a CCMenuItemLabel with a Label, target and selector */
+-(id) initWithLabel:(CCNode<CCLabelProtocol,CCRGBAProtocol>*)label target:(id)target selector:(SEL)selector;
+
+#if NS_BLOCKS_AVAILABLE
+/** creates a CCMenuItemLabel with a Label and a block to execute.
+ The block will be "copied".
+ */
++(id) itemWithLabel:(CCNode<CCLabelProtocol,CCRGBAProtocol>*)label block:(void(^)(id sender))block;
+
+/** initializes a CCMenuItemLabel with a Label and a block to execute.
+ The block will be "copied".
+ */
+-(id) initWithLabel:(CCNode<CCLabelProtocol,CCRGBAProtocol>*)label block:(void(^)(id sender))block;
+#endif
+
+/** sets a new string to the inner label */
+-(void) setString:(NSString*)label;
+
+/** Enable or disabled the CCMenuItemFont
+ @warning setIsEnabled changes the RGB color of the font
+ */
+-(void) setIsEnabled: (BOOL)enabled;
+@end
+
+#pragma mark -
+#pragma mark CCMenuItemAtlasFont
+
+/** A CCMenuItemAtlasFont
+ Helper class that creates a MenuItemLabel class with a LabelAtlas
+ */
+@interface CCMenuItemAtlasFont : CCMenuItemLabel
+{
+}
+
+/** creates a menu item from a string and atlas with a target/selector */
++(id) itemFromString: (NSString*) value charMapFile:(NSString*) charMapFile itemWidth:(int)itemWidth itemHeight:(int)itemHeight startCharMap:(char)startCharMap;
+
+/** creates a menu item from a string and atlas. Use it with MenuItemToggle */
++(id) itemFromString: (NSString*) value charMapFile:(NSString*) charMapFile itemWidth:(int)itemWidth itemHeight:(int)itemHeight startCharMap:(char)startCharMap target:(id) rec selector:(SEL) cb;
+
+/** initializes a menu item from a string and atlas with a target/selector */
+-(id) initFromString: (NSString*) value charMapFile:(NSString*) charMapFile itemWidth:(int)itemWidth itemHeight:(int)itemHeight startCharMap:(char)startCharMap target:(id) rec selector:(SEL) cb;
+
+#if NS_BLOCKS_AVAILABLE
+/** creates a menu item from a string and atlas. Use it with MenuItemToggle.
+ The block will be "copied".
+ */
++(id) itemFromString: (NSString*) value charMapFile:(NSString*) charMapFile itemWidth:(int)itemWidth itemHeight:(int)itemHeight startCharMap:(char)startCharMap block:(void(^)(id sender))block;
+
+/** initializes a menu item from a string and atlas with a  block.
+ The block will be "copied".
+ */
+-(id) initFromString: (NSString*) value charMapFile:(NSString*) charMapFile itemWidth:(int)itemWidth itemHeight:(int)itemHeight startCharMap:(char)startCharMap block:(void(^)(id sender))block;
+#endif
+
+@end
+
+#pragma mark -
+#pragma mark CCMenuItemFont
+
+/** A CCMenuItemFont
+ Helper class that creates a CCMenuItemLabel class with a Label
+ */
+@interface CCMenuItemFont : CCMenuItemLabel
+{
+}
+/** set font size */
++(void) setFontSize: (int) s;
+
+/** get font size */
++(int) fontSize;
+
+/** set the font name */
++(void) setFontName: (NSString*) n;
+
+/** get the font name */
++(NSString*) fontName;
+
+/** creates a menu item from a string without target/selector. To be used with CCMenuItemToggle */
++(id) itemFromString: (NSString*) value;
+
+/** creates a menu item from a string with a target/selector */
++(id) itemFromString: (NSString*) value target:(id) r selector:(SEL) s;
+
+/** initializes a menu item from a string with a target/selector */
+-(id) initFromString: (NSString*) value target:(id) r selector:(SEL) s;
+
+#if NS_BLOCKS_AVAILABLE
+/** creates a menu item from a string with the specified block.
+ The block will be "copied".
+ */
++(id) itemFromString: (NSString*) value block:(void(^)(id sender))block;
+
+/** initializes a menu item from a string with the specified block.
+ The block will be "copied".
+ */
+-(id) initFromString: (NSString*) value block:(void(^)(id sender))block;
+#endif
+@end
+
+#pragma mark -
+#pragma mark CCMenuItemSprite
+
+/** CCMenuItemSprite accepts CCNode<CCRGBAProtocol> objects as items.
+ The images has 3 different states:
+ - unselected image
+ - selected image
+ - disabled image
+ @since v0.8.0
+ */
+@interface CCMenuItemSprite : CCMenuItem <CCRGBAProtocol>
+{
+       CCNode<CCRGBAProtocol> *normalImage_, *selectedImage_, *disabledImage_;
+}
+
+// weak references
+
+/** the image used when the item is not selected */
+@property (nonatomic,readwrite,assign) CCNode<CCRGBAProtocol> *normalImage;
+/** the image used when the item is selected */
+@property (nonatomic,readwrite,assign) CCNode<CCRGBAProtocol> *selectedImage;
+/** the image used when the item is disabled */
+@property (nonatomic,readwrite,assign) CCNode<CCRGBAProtocol> *disabledImage;
+
+/** creates a menu item with a normal and selected image*/
++(id) itemFromNormalSprite:(CCNode<CCRGBAProtocol>*)normalSprite selectedSprite:(CCNode<CCRGBAProtocol>*)selectedSprite;
+/** creates a menu item with a normal and selected image with target/selector */
++(id) itemFromNormalSprite:(CCNode<CCRGBAProtocol>*)normalSprite selectedSprite:(CCNode<CCRGBAProtocol>*)selectedSprite target:(id)target selector:(SEL)selector;
+/** creates a menu item with a normal,selected  and disabled image with target/selector */
++(id) itemFromNormalSprite:(CCNode<CCRGBAProtocol>*)normalSprite selectedSprite:(CCNode<CCRGBAProtocol>*)selectedSprite disabledSprite:(CCNode<CCRGBAProtocol>*)disabledSprite target:(id)target selector:(SEL)selector;
+/** initializes a menu item with a normal, selected  and disabled image with target/selector */
+-(id) initFromNormalSprite:(CCNode<CCRGBAProtocol>*)normalSprite selectedSprite:(CCNode<CCRGBAProtocol>*)selectedSprite disabledSprite:(CCNode<CCRGBAProtocol>*)disabledSprite target:(id)target selector:(SEL)selector;
+
+#if NS_BLOCKS_AVAILABLE
+/** creates a menu item with a normal and selected image with a block.
+ The block will be "copied".
+ */
++(id) itemFromNormalSprite:(CCNode<CCRGBAProtocol>*)normalSprite selectedSprite:(CCNode<CCRGBAProtocol>*)selectedSprite block:(void(^)(id sender))block;
+/** creates a menu item with a normal,selected  and disabled image with a block.
+ The block will be "copied".
+ */
++(id) itemFromNormalSprite:(CCNode<CCRGBAProtocol>*)normalSprite selectedSprite:(CCNode<CCRGBAProtocol>*)selectedSprite disabledSprite:(CCNode<CCRGBAProtocol>*)disabledSprite block:(void(^)(id sender))block;
+/** initializes a menu item with a normal, selected  and disabled image with a block.
+ The block will be "copied".
+ */
+-(id) initFromNormalSprite:(CCNode<CCRGBAProtocol>*)normalSprite selectedSprite:(CCNode<CCRGBAProtocol>*)selectedSprite disabledSprite:(CCNode<CCRGBAProtocol>*)disabledSprite block:(void(^)(id sender))block;
+#endif
+
+@end
+
+#pragma mark -
+#pragma mark CCMenuItemImage
+
+/** CCMenuItemImage accepts images as items.
+ The images has 3 different states:
+ - unselected image
+ - selected image
+ - disabled image
+ For best results try that all images are of the same size
+ */
+@interface CCMenuItemImage : CCMenuItemSprite
+{
+}
+
+/** creates a menu item with a normal and selected image*/
++(id) itemFromNormalImage: (NSString*)value selectedImage:(NSString*) value2;
+/** creates a menu item with a normal and selected image with target/selector */
++(id) itemFromNormalImage: (NSString*)value selectedImage:(NSString*) value2 target:(id) r selector:(SEL) s;
+/** creates a menu item with a normal,selected  and disabled image with target/selector */
++(id) itemFromNormalImage: (NSString*)value selectedImage:(NSString*) value2 disabledImage:(NSString*) value3 target:(id) r selector:(SEL) s;
+/** initializes a menu item with a normal, selected  and disabled image with target/selector */
+-(id) initFromNormalImage: (NSString*) value selectedImage:(NSString*)value2 disabledImage:(NSString*) value3 target:(id) r selector:(SEL) s;
+#if NS_BLOCKS_AVAILABLE
+/** creates a menu item with a normal and selected image with a block.
+ The block will be "copied".
+ */
++(id) itemFromNormalImage: (NSString*)value selectedImage:(NSString*) value2 block:(void(^)(id sender))block;
+/** creates a menu item with a normal,selected  and disabled image with a block.
+ The block will be "copied".
+*/
++(id) itemFromNormalImage: (NSString*)value selectedImage:(NSString*) value2 disabledImage:(NSString*) value3 block:(void(^)(id sender))block;
+/** initializes a menu item with a normal, selected  and disabled image with a block.
+ The block will be "copied".
+*/
+-(id) initFromNormalImage: (NSString*) value selectedImage:(NSString*)value2 disabledImage:(NSString*) value3 block:(void(^)(id sender))block;
+#endif
+@end
+
+#pragma mark -
+#pragma mark CCMenuItemToggle
+
+/** A CCMenuItemToggle
+ A simple container class that "toggles" it's inner items
+ The inner itmes can be any MenuItem
+ */
+@interface CCMenuItemToggle : CCMenuItem <CCRGBAProtocol>
+{
+       NSUInteger selectedIndex_;
+       NSMutableArray* subItems_;
+       GLubyte         opacity_;
+       ccColor3B       color_;
+}
+
+/** conforms with CCRGBAProtocol protocol */
+@property (nonatomic,readonly) GLubyte opacity;
+/** conforms with CCRGBAProtocol protocol */
+@property (nonatomic,readonly) ccColor3B color;
+
+/** returns the selected item */
+@property (nonatomic,readwrite) NSUInteger selectedIndex;
+/** NSMutableArray that contains the subitems. You can add/remove items in runtime, and you can replace the array with a new one.
+ @since v0.7.2
+ */
+@property (nonatomic,readwrite,retain) NSMutableArray *subItems;
+
+/** creates a menu item from a list of items with a target/selector */
++(id) itemWithTarget:(id)t selector:(SEL)s items:(CCMenuItem*) item, ... NS_REQUIRES_NIL_TERMINATION;
+
+/** initializes a menu item from a list of items with a target selector */
+-(id) initWithTarget:(id)t selector:(SEL)s items:(CCMenuItem*) item vaList:(va_list) args;
+
+#if NS_BLOCKS_AVAILABLE
+/** creates a menu item from a list of items and executes the given block when the item is selected.
+ The block will be "copied".
+ */
++(id) itemWithBlock:(void(^)(id sender))block items:(CCMenuItem*)item, ... NS_REQUIRES_NIL_TERMINATION;
+
+/** initializes a menu item from a list of items with a block.
+ The block will be "copied".
+ */
+-(id) initWithBlock:(void (^)(id))block items:(CCMenuItem*)item vaList:(va_list)args;
+#endif
+
+/** return the selected item */
+-(CCMenuItem*) selectedItem;
+@end
+
diff --git a/libs/cocos2d/CCMenuItem.m b/libs/cocos2d/CCMenuItem.m
new file mode 100644 (file)
index 0000000..3f8ab2e
--- /dev/null
@@ -0,0 +1,763 @@
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2008-2011 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+#import "CCMenuItem.h"
+#import "CCLabelTTF.h"
+#import "CCLabelAtlas.h"
+#import "CCActionInterval.h"
+#import "CCSprite.h"
+#import "Support/CGPointExtension.h"
+
+static int _fontSize = kItemSize;
+static NSString *_fontName = @"Marker Felt";
+static BOOL _fontNameRelease = NO;
+
+enum {
+       kCurrentItem = 0xc0c05001,
+};
+
+enum {
+       kZoomActionTag = 0xc0c05002,
+};
+
+
+
+#pragma mark -
+#pragma mark CCMenuItem
+
+@implementation CCMenuItem
+
+@synthesize isSelected=isSelected_;
+-(id) init
+{
+       NSAssert(NO, @"MenuItemInit: Init not supported.");
+       [self release];
+       return nil;
+}
+
++(id) itemWithTarget:(id) r selector:(SEL) s
+{
+       return [[[self alloc] initWithTarget:r selector:s] autorelease];
+}
+
+-(id) initWithTarget:(id) rec selector:(SEL) cb
+{
+       if((self=[super init]) ) {
+       
+               anchorPoint_ = ccp(0.5f, 0.5f);
+               NSMethodSignature * sig = nil;
+               
+               if( rec && cb ) {
+                       sig = [rec methodSignatureForSelector:cb];
+                       
+                       invocation = nil;
+                       invocation = [NSInvocation invocationWithMethodSignature:sig];
+                       [invocation setTarget:rec];
+                       [invocation setSelector:cb];
+#if NS_BLOCKS_AVAILABLE
+                       if ([sig numberOfArguments] == 3) 
+#endif
+                       [invocation setArgument:&self atIndex:2];
+                       
+                       [invocation retain];
+               }
+               
+               isEnabled_ = YES;
+               isSelected_ = NO;
+       }
+       
+       return self;
+}
+
+#if NS_BLOCKS_AVAILABLE
+
++(id) itemWithBlock:(void(^)(id sender))block {
+       return [[[self alloc] initWithBlock:block] autorelease];
+}
+
+-(id) initWithBlock:(void(^)(id sender))block {
+       block_ = [block copy];
+       return [self initWithTarget:block_ selector:@selector(ccCallbackBlockWithSender:)];
+}
+
+#endif // NS_BLOCKS_AVAILABLE
+
+-(void) dealloc
+{
+       [invocation release];
+
+#if NS_BLOCKS_AVAILABLE
+       [block_ release];
+#endif
+       
+       [super dealloc];
+}
+
+-(void) selected
+{
+       isSelected_ = YES;
+}
+
+-(void) unselected
+{
+       isSelected_ = NO;
+}
+
+-(void) activate
+{
+       if(isEnabled_)
+        [invocation invoke];
+}
+
+-(void) setIsEnabled: (BOOL)enabled
+{
+    isEnabled_ = enabled;
+}
+
+-(BOOL) isEnabled
+{
+    return isEnabled_;
+}
+
+-(CGRect) rect
+{
+       return CGRectMake( position_.x - contentSize_.width*anchorPoint_.x,
+                                         position_.y - contentSize_.height*anchorPoint_.y,
+                                         contentSize_.width, contentSize_.height);     
+}
+
+@end
+
+
+#pragma mark -
+#pragma mark CCMenuItemLabel
+
+@implementation CCMenuItemLabel
+
+@synthesize disabledColor = disabledColor_;
+
++(id) itemWithLabel:(CCNode<CCLabelProtocol,CCRGBAProtocol>*)label target:(id)target selector:(SEL)selector
+{
+       return [[[self alloc] initWithLabel:label target:target selector:selector] autorelease];
+}
+
++(id) itemWithLabel:(CCNode<CCLabelProtocol,CCRGBAProtocol>*)label
+{
+       return [[[self alloc] initWithLabel:label target:nil selector:NULL] autorelease];
+}
+
+-(id) initWithLabel:(CCNode<CCLabelProtocol,CCRGBAProtocol>*)label target:(id)target selector:(SEL)selector
+{
+       if( (self=[super initWithTarget:target selector:selector]) ) {
+               originalScale_ = 1;
+               colorBackup = ccWHITE;
+               disabledColor_ = ccc3( 126,126,126);
+               self.label = label;
+               
+       }
+       return self;
+}
+
+#if NS_BLOCKS_AVAILABLE
+
++(id) itemWithLabel:(CCNode<CCLabelProtocol,CCRGBAProtocol>*)label block:(void(^)(id sender))block {
+       return [[[self alloc] initWithLabel:label block:block] autorelease];
+}
+
+-(id) initWithLabel:(CCNode<CCLabelProtocol,CCRGBAProtocol>*)label block:(void(^)(id sender))block {
+       block_ = [block copy];
+       return [self initWithLabel:label target:block_ selector:@selector(ccCallbackBlockWithSender:)];
+}
+
+#endif // NS_BLOCKS_AVAILABLE
+
+-(CCNode<CCLabelProtocol, CCRGBAProtocol>*) label
+{
+       return label_;
+}
+-(void) setLabel:(CCNode<CCLabelProtocol, CCRGBAProtocol>*) label
+{
+       if( label != label_ ) {
+               [self removeChild:label_ cleanup:YES];
+               [self addChild:label];
+               
+               label_ = label;
+               label_.anchorPoint = ccp(0,0);
+
+               [self setContentSize:[label_ contentSize]];
+       }
+}
+
+-(void) setString:(NSString *)string
+{
+       [label_ setString:string];
+       [self setContentSize: [label_ contentSize]];
+}
+
+-(void) activate {
+       if(isEnabled_) {
+               [self stopAllActions];
+        
+               self.scale = originalScale_;
+        
+               [super activate];
+       }
+}
+
+-(void) selected
+{
+       // subclass to change the default action
+       if(isEnabled_) {        
+               [super selected];
+
+               CCAction *action = [self getActionByTag:kZoomActionTag];
+               if( action )
+                       [self stopAction:action];
+               else
+                       originalScale_ = self.scale;
+
+               CCAction *zoomAction = [CCScaleTo actionWithDuration:0.1f scale:originalScale_ * 1.2f];
+               zoomAction.tag = kZoomActionTag;
+               [self runAction:zoomAction];
+       }
+}
+
+-(void) unselected
+{
+       // subclass to change the default action
+       if(isEnabled_) {
+               [super unselected];
+               [self stopActionByTag:kZoomActionTag];
+               CCAction *zoomAction = [CCScaleTo actionWithDuration:0.1f scale:originalScale_];
+               zoomAction.tag = kZoomActionTag;
+               [self runAction:zoomAction];
+       }
+}
+
+-(void) setIsEnabled: (BOOL)enabled
+{
+       if( isEnabled_ != enabled ) {
+               if(enabled == NO) {
+                       colorBackup = [label_ color];
+                       [label_ setColor: disabledColor_];
+               }
+               else
+                       [label_ setColor:colorBackup];
+       }
+    
+       [super setIsEnabled:enabled];
+}
+
+- (void) setOpacity: (GLubyte)opacity
+{
+    [label_ setOpacity:opacity];
+}
+-(GLubyte) opacity
+{
+       return [label_ opacity];
+}
+-(void) setColor:(ccColor3B)color
+{
+       [label_ setColor:color];
+}
+-(ccColor3B) color
+{
+       return [label_ color];
+}
+@end
+
+#pragma mark  -
+#pragma mark CCMenuItemAtlasFont
+
+@implementation CCMenuItemAtlasFont
+
++(id) itemFromString: (NSString*) value charMapFile:(NSString*) charMapFile itemWidth:(int)itemWidth itemHeight:(int)itemHeight startCharMap:(char)startCharMap
+{
+       return [CCMenuItemAtlasFont itemFromString:value charMapFile:charMapFile itemWidth:itemWidth itemHeight:itemHeight startCharMap:startCharMap target:nil selector:nil];
+}
+
++(id) itemFromString: (NSString*) value charMapFile:(NSString*) charMapFile itemWidth:(int)itemWidth itemHeight:(int)itemHeight startCharMap:(char)startCharMap target:(id) rec selector:(SEL) cb
+{
+       return [[[self alloc] initFromString:value charMapFile:charMapFile itemWidth:itemWidth itemHeight:itemHeight startCharMap:startCharMap target:rec selector:cb] autorelease];
+}
+
+-(id) initFromString: (NSString*) value charMapFile:(NSString*) charMapFile itemWidth:(int)itemWidth itemHeight:(int)itemHeight startCharMap:(char)startCharMap target:(id) rec selector:(SEL) cb
+{
+       NSAssert( [value length] != 0, @"value length must be greater than 0");
+       
+       CCLabelAtlas *label = [[CCLabelAtlas alloc] initWithString:value charMapFile:charMapFile itemWidth:itemWidth itemHeight:itemHeight startCharMap:startCharMap];
+       [label autorelease];
+
+       if((self=[super initWithLabel:label target:rec selector:cb]) ) {
+               // do something ?
+       }
+       
+       return self;
+}
+
+#if NS_BLOCKS_AVAILABLE
++(id) itemFromString:(NSString*)value charMapFile:(NSString*)charMapFile itemWidth:(int)itemWidth itemHeight:(int)itemHeight startCharMap:(char)startCharMap block:(void(^)(id sender))block {
+       return [[[self alloc] initFromString:value charMapFile:charMapFile itemWidth:itemWidth itemHeight:itemHeight startCharMap:startCharMap block:block] autorelease];
+}
+
+-(id) initFromString:(NSString*)value charMapFile:(NSString*)charMapFile itemWidth:(int)itemWidth itemHeight:(int)itemHeight startCharMap:(char)startCharMap block:(void(^)(id sender))block {
+       block_ = [block copy];
+       return [self initFromString:value charMapFile:charMapFile itemWidth:itemWidth itemHeight:itemHeight startCharMap:startCharMap target:block_ selector:@selector(ccCallbackBlockWithSender:)];
+}
+#endif // NS_BLOCKS_AVAILABLE
+
+-(void) dealloc
+{
+       [super dealloc];
+}
+@end
+
+
+#pragma mark -
+#pragma mark CCMenuItemFont
+
+@implementation CCMenuItemFont
+
++(void) setFontSize: (int) s
+{
+       _fontSize = s;
+}
+
++(int) fontSize
+{
+       return _fontSize;
+}
+
++(void) setFontName: (NSString*) n
+{
+       if( _fontNameRelease )
+               [_fontName release];
+       
+       _fontName = [n retain];
+       _fontNameRelease = YES;
+}
+
++(NSString*) fontName
+{
+       return _fontName;
+}
+
++(id) itemFromString: (NSString*) value target:(id) r selector:(SEL) s
+{
+       return [[[self alloc] initFromString: value target:r selector:s] autorelease];
+}
+
++(id) itemFromString: (NSString*) value
+{
+       return [[[self alloc] initFromString: value target:nil selector:nil] autorelease];
+}
+
+-(id) initFromString: (NSString*) value target:(id) rec selector:(SEL) cb
+{
+       NSAssert( [value length] != 0, @"Value length must be greater than 0");
+       
+       CCLabelTTF *label = [CCLabelTTF labelWithString:value fontName:_fontName fontSize:_fontSize];
+
+       if((self=[super initWithLabel:label target:rec selector:cb]) ) {
+               // do something ?
+       }
+       
+       return self;
+}
+
+#if NS_BLOCKS_AVAILABLE
++(id) itemFromString: (NSString*) value block:(void(^)(id sender))block {
+       return [[[self alloc] initFromString:value block:block] autorelease];
+}
+
+-(id) initFromString: (NSString*) value block:(void(^)(id sender))block {
+       block_ = [block copy];
+       return [self initFromString:value target:block_ selector:@selector(ccCallbackBlockWithSender:)];
+}
+#endif // NS_BLOCKS_AVAILABLE
+
+@end
+
+#pragma mark -
+#pragma mark CCMenuItemSprite
+@implementation CCMenuItemSprite
+
+@synthesize normalImage=normalImage_, selectedImage=selectedImage_, disabledImage=disabledImage_;
+
++(id) itemFromNormalSprite:(CCNode<CCRGBAProtocol>*)normalSprite selectedSprite:(CCNode<CCRGBAProtocol>*)selectedSprite
+{
+       return [self itemFromNormalSprite:normalSprite selectedSprite:selectedSprite disabledSprite:nil target:nil selector:nil];
+}
++(id) itemFromNormalSprite:(CCNode<CCRGBAProtocol>*)normalSprite selectedSprite:(CCNode<CCRGBAProtocol>*)selectedSprite target:(id)target selector:(SEL)selector
+{
+       return [self itemFromNormalSprite:normalSprite selectedSprite:selectedSprite disabledSprite:nil target:target selector:selector];
+}
++(id) itemFromNormalSprite:(CCNode<CCRGBAProtocol>*)normalSprite selectedSprite:(CCNode<CCRGBAProtocol>*)selectedSprite disabledSprite:(CCNode<CCRGBAProtocol>*)disabledSprite target:(id)target selector:(SEL)selector
+{
+       return [[[self alloc] initFromNormalSprite:normalSprite selectedSprite:selectedSprite disabledSprite:disabledSprite target:target selector:selector] autorelease];
+}
+-(id) initFromNormalSprite:(CCNode<CCRGBAProtocol>*)normalSprite selectedSprite:(CCNode<CCRGBAProtocol>*)selectedSprite disabledSprite:(CCNode<CCRGBAProtocol>*)disabledSprite target:(id)target selector:(SEL)selector
+{
+       if( (self=[super initWithTarget:target selector:selector]) ) {
+               
+               self.normalImage = normalSprite;
+               self.selectedImage = selectedSprite;
+               self.disabledImage = disabledSprite;
+               
+               [self setContentSize: [normalImage_ contentSize]];
+       }
+       return self;    
+}
+
+#if NS_BLOCKS_AVAILABLE
++(id) itemFromNormalSprite:(CCNode<CCRGBAProtocol>*)normalSprite selectedSprite:(CCNode<CCRGBAProtocol>*)selectedSprite block:(void(^)(id sender))block {
+       return [self itemFromNormalSprite:normalSprite selectedSprite:selectedSprite disabledSprite:nil block:block];
+}
+
++(id) itemFromNormalSprite:(CCNode<CCRGBAProtocol>*)normalSprite selectedSprite:(CCNode<CCRGBAProtocol>*)selectedSprite disabledSprite:(CCNode<CCRGBAProtocol>*)disabledSprite block:(void(^)(id sender))block {
+       return [[[self alloc] initFromNormalSprite:normalSprite selectedSprite:selectedSprite disabledSprite:disabledSprite block:block] autorelease];
+}
+
+-(id) initFromNormalSprite:(CCNode<CCRGBAProtocol>*)normalSprite selectedSprite:(CCNode<CCRGBAProtocol>*)selectedSprite disabledSprite:(CCNode<CCRGBAProtocol>*)disabledSprite block:(void(^)(id sender))block {
+       block_ = [block copy];
+       return [self initFromNormalSprite:normalSprite selectedSprite:selectedSprite disabledSprite:disabledSprite target:block_ selector:@selector(ccCallbackBlockWithSender:)];
+}
+#endif // NS_BLOCKS_AVAILABLE
+
+
+-(void) setNormalImage:(CCNode <CCRGBAProtocol>*)image
+{
+       if( image != normalImage_ ) {
+               image.anchorPoint = ccp(0,0);
+               image.visible = YES;
+               
+               [self removeChild:normalImage_ cleanup:YES];
+               [self addChild:image];
+               
+               normalImage_ = image;
+       }
+}
+
+-(void) setSelectedImage:(CCNode <CCRGBAProtocol>*)image
+{
+       if( image != selectedImage_ ) {
+               image.anchorPoint = ccp(0,0);
+               image.visible = NO;
+               
+               [self removeChild:selectedImage_ cleanup:YES];
+               [self addChild:image];
+               
+               selectedImage_ = image;
+       }
+}
+
+-(void) setDisabledImage:(CCNode <CCRGBAProtocol>*)image
+{
+       if( image != disabledImage_ ) {
+               image.anchorPoint = ccp(0,0);
+               image.visible = NO;
+               
+               [self removeChild:disabledImage_ cleanup:YES];
+               [self addChild:image];
+               
+               disabledImage_ = image;
+       }
+}
+
+#pragma mark CCMenuItemImage - CCRGBAProtocol protocol
+- (void) setOpacity: (GLubyte)opacity
+{
+       [normalImage_ setOpacity:opacity];
+       [selectedImage_ setOpacity:opacity];
+       [disabledImage_ setOpacity:opacity];
+}
+
+-(void) setColor:(ccColor3B)color
+{
+       [normalImage_ setColor:color];
+       [selectedImage_ setColor:color];
+       [disabledImage_ setColor:color];        
+}
+
+-(GLubyte) opacity
+{
+       return [normalImage_ opacity];
+}
+
+-(ccColor3B) color
+{
+       return [normalImage_ color];
+}
+
+-(void) selected
+{
+       [super selected];
+
+       if( selectedImage_ ) {
+               [normalImage_ setVisible:NO];
+               [selectedImage_ setVisible:YES];
+               [disabledImage_ setVisible:NO];
+               
+       } else { // there is not selected image
+       
+               [normalImage_ setVisible:YES];
+               [selectedImage_ setVisible:NO];
+               [disabledImage_ setVisible:NO];         
+       }
+}
+
+-(void) unselected
+{
+       [super unselected];
+       [normalImage_ setVisible:YES];
+       [selectedImage_ setVisible:NO];
+       [disabledImage_ setVisible:NO];
+}
+
+-(void) setIsEnabled:(BOOL)enabled
+{
+       [super setIsEnabled:enabled];
+
+       if( enabled ) {
+               [normalImage_ setVisible:YES];
+               [selectedImage_ setVisible:NO];
+               [disabledImage_ setVisible:NO];
+
+       } else {
+               if( disabledImage_ ) {
+                       [normalImage_ setVisible:NO];
+                       [selectedImage_ setVisible:NO];
+                       [disabledImage_ setVisible:YES];                
+               } else {
+                       [normalImage_ setVisible:YES];
+                       [selectedImage_ setVisible:NO];
+                       [disabledImage_ setVisible:NO];
+               }
+       }
+}
+
+@end
+
+#pragma mark -
+#pragma mark CCMenuItemImage
+
+@implementation CCMenuItemImage
+
++(id) itemFromNormalImage: (NSString*)value selectedImage:(NSString*) value2
+{
+       return [self itemFromNormalImage:value selectedImage:value2 disabledImage: nil target:nil selector:nil];
+}
+
++(id) itemFromNormalImage: (NSString*)value selectedImage:(NSString*) value2 target:(id) t selector:(SEL) s
+{
+       return [self itemFromNormalImage:value selectedImage:value2 disabledImage: nil target:t selector:s];
+}
+
++(id) itemFromNormalImage: (NSString*)value selectedImage:(NSString*) value2 disabledImage: (NSString*) value3
+{
+       return [[[self alloc] initFromNormalImage:value selectedImage:value2 disabledImage:value3 target:nil selector:nil] autorelease];
+}
+
++(id) itemFromNormalImage: (NSString*)value selectedImage:(NSString*) value2 disabledImage: (NSString*) value3 target:(id) t selector:(SEL) s
+{
+       return [[[self alloc] initFromNormalImage:value selectedImage:value2 disabledImage:value3 target:t selector:s] autorelease];
+}
+
+-(id) initFromNormalImage: (NSString*) normalI selectedImage:(NSString*)selectedI disabledImage: (NSString*) disabledI target:(id)t selector:(SEL)sel
+{
+       CCNode<CCRGBAProtocol> *normalImage = [CCSprite spriteWithFile:normalI];
+       CCNode<CCRGBAProtocol> *selectedImage = nil;
+       CCNode<CCRGBAProtocol> *disabledImage = nil;
+
+       if( selectedI )
+               selectedImage = [CCSprite spriteWithFile:selectedI]; 
+       if(disabledI)
+               disabledImage = [CCSprite spriteWithFile:disabledI];
+
+       return [self initFromNormalSprite:normalImage selectedSprite:selectedImage disabledSprite:disabledImage target:t selector:sel];
+}
+
+#if NS_BLOCKS_AVAILABLE
+
++(id) itemFromNormalImage: (NSString*)value selectedImage:(NSString*) value2 block:(void(^)(id sender))block {
+       return [self itemFromNormalImage:value selectedImage:value2 disabledImage:nil block:block];
+}
+
++(id) itemFromNormalImage: (NSString*)value selectedImage:(NSString*) value2 disabledImage:(NSString*) value3 block:(void(^)(id sender))block {
+       return [[[self alloc] initFromNormalImage:value selectedImage:value2 disabledImage:value3 block:block] autorelease];
+}
+
+-(id) initFromNormalImage: (NSString*) value selectedImage:(NSString*)value2 disabledImage:(NSString*) value3 block:(void(^)(id sender))block {
+       block_ = [block copy];
+       return [self initFromNormalImage:value selectedImage:value2 disabledImage:value3 target:block_ selector:@selector(ccCallbackBlockWithSender:)];
+}
+
+#endif // NS_BLOCKS_AVAILABLE
+
+@end
+
+#pragma mark -
+#pragma mark CCMenuItemToggle
+
+//
+// MenuItemToggle
+//
+@implementation CCMenuItemToggle
+
+@synthesize subItems = subItems_;
+@synthesize opacity = opacity_, color = color_;
+
++(id) itemWithTarget: (id)t selector: (SEL)sel items: (CCMenuItem*) item, ...
+{
+       va_list args;
+       va_start(args, item);
+       
+       id s = [[[self alloc] initWithTarget: t selector:sel items: item vaList:args] autorelease];
+       
+       va_end(args);
+       return s;
+}
+
+-(id) initWithTarget: (id)t selector: (SEL)sel items:(CCMenuItem*) item vaList: (va_list) args
+{
+       if( (self=[super initWithTarget:t selector:sel]) ) {
+       
+               self.subItems = [NSMutableArray arrayWithCapacity:2];
+               
+               int z = 0;
+               CCMenuItem *i = item;
+               while(i) {
+                       z++;
+                       [subItems_ addObject:i];
+                       i = va_arg(args, CCMenuItem*);
+               }
+
+               selectedIndex_ = NSUIntegerMax;
+               [self setSelectedIndex:0];
+       }
+       
+       return self;
+}
+
+#if NS_BLOCKS_AVAILABLE
+                                                                 
++(id) itemWithBlock:(void(^)(id sender))block items:(CCMenuItem*)item, ... {
+       va_list args;
+       va_start(args, item);
+       
+       id s = [[[self alloc] initWithBlock:block items:item vaList:args] autorelease];
+       
+       va_end(args);
+       return s;
+}
+
+-(id) initWithBlock:(void (^)(id))block items:(CCMenuItem*)item vaList:(va_list)args {
+       block_ = [block copy];
+       return [self initWithTarget:block_ selector:@selector(ccCallbackBlockWithSender:) items:item vaList:args];
+}
+
+#endif // NS_BLOCKS_AVAILABLE
+
+-(void) dealloc
+{
+       [subItems_ release];
+       [super dealloc];
+}
+
+-(void)setSelectedIndex:(NSUInteger)index
+{
+       if( index != selectedIndex_ ) {
+               selectedIndex_=index;
+               [self removeChildByTag:kCurrentItem cleanup:NO];
+               
+               CCMenuItem *item = [subItems_ objectAtIndex:selectedIndex_];
+               [self addChild:item z:0 tag:kCurrentItem];
+               
+               CGSize s = [item contentSize];
+               [self setContentSize: s];
+               item.position = ccp( s.width/2, s.height/2 );
+       }
+}
+
+-(NSUInteger) selectedIndex
+{
+       return selectedIndex_;
+}
+
+
+-(void) selected
+{
+       [super selected];
+       [[subItems_ objectAtIndex:selectedIndex_] selected];
+}
+
+-(void) unselected
+{
+       [super unselected];
+       [[subItems_ objectAtIndex:selectedIndex_] unselected];
+}
+
+-(void) activate
+{
+       // update index
+       if( isEnabled_ ) {
+               NSUInteger newIndex = (selectedIndex_ + 1) % [subItems_ count];
+               [self setSelectedIndex:newIndex];
+
+       }
+
+       [super activate];
+}
+
+-(void) setIsEnabled: (BOOL)enabled
+{
+       [super setIsEnabled:enabled];
+       for(CCMenuItem* item in subItems_)
+               [item setIsEnabled:enabled];
+}
+
+-(CCMenuItem*) selectedItem
+{
+       return [subItems_ objectAtIndex:selectedIndex_];
+}
+
+#pragma mark CCMenuItemToggle - CCRGBAProtocol protocol
+
+- (void) setOpacity: (GLubyte)opacity
+{
+       opacity_ = opacity;
+       for(CCMenuItem<CCRGBAProtocol>* item in subItems_)
+               [item setOpacity:opacity];
+}
+
+- (void) setColor:(ccColor3B)color
+{
+       color_ = color;
+       for(CCMenuItem<CCRGBAProtocol>* item in subItems_)
+               [item setColor:color];
+}
+
+@end
diff --git a/libs/cocos2d/CCMotionStreak.h b/libs/cocos2d/CCMotionStreak.h
new file mode 100644 (file)
index 0000000..e017124
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2008, 2009 Jason Booth
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+#import <Foundation/Foundation.h>
+#import "CCNode.h"
+#import "CCRibbon.h"
+
+/**
+ * CCMotionStreak manages a Ribbon based on it's motion in absolute space.
+ * You construct it with a fadeTime, minimum segment size, texture path, texture
+ * length and color. The fadeTime controls how long it takes each vertex in
+ * the streak to fade out, the minimum segment size it how many pixels the
+ * streak will move before adding a new ribbon segement, and the texture
+ * length is the how many pixels the texture is stretched across. The texture
+ * is vertically aligned along the streak segemnts. 
+ *
+ * Limitations:
+ *   CCMotionStreak, by default, will use the GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA blending function.
+ *   This blending function might not be the correct one for certain textures.
+ *   But you can change it by using:
+ *     [obj setBlendFunc: (ccBlendfunc) {new_src_blend_func, new_dst_blend_func}];
+ *
+ * @since v0.8.1
+ */
+@interface CCMotionStreak : CCNode <CCTextureProtocol>
+{
+       CCRibbon*       ribbon_;
+       float           segThreshold_;
+       float           width_;
+       CGPoint lastLocation_;
+}
+
+/** Ribbon used by MotionStreak (weak reference) */
+@property (nonatomic,readonly) CCRibbon *ribbon;
+
+/** creates the a MotionStreak. The image will be loaded using the TextureMgr. */
++(id)streakWithFade:(float)fade minSeg:(float)seg image:(NSString*)path width:(float)width length:(float)length color:(ccColor4B)color;
+
+/** initializes a MotionStreak. The file will be loaded using the TextureMgr. */
+-(id)initWithFade:(float)fade minSeg:(float)seg image:(NSString*)path width:(float)width length:(float)length color:(ccColor4B)color;
+
+/** polling function */
+-(void)update:(ccTime)delta;
+
+@end
diff --git a/libs/cocos2d/CCMotionStreak.m b/libs/cocos2d/CCMotionStreak.m
new file mode 100644 (file)
index 0000000..42737b9
--- /dev/null
@@ -0,0 +1,104 @@
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2008, 2009 Jason Booth
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ *
+ *********************************************************
+ *
+ * Motion Streak manages a Ribbon based on it's motion in absolute space.
+ * You construct it with a fadeTime, minimum segment size, texture path, texture
+ * length and color. The fadeTime controls how long it takes each vertex in
+ * the streak to fade out, the minimum segment size it how many pixels the
+ * streak will move before adding a new ribbon segement, and the texture
+ * length is the how many pixels the texture is stretched across. The texture
+ * is vertically aligned along the streak segemnts. 
+ */
+
+#import "CCMotionStreak.h"
+#import "Support/CGPointExtension.h"
+
+@implementation CCMotionStreak
+
+@synthesize ribbon = ribbon_;
+
++(id)streakWithFade:(float)fade minSeg:(float)seg image:(NSString*)path width:(float)width length:(float)length color:(ccColor4B)color
+{
+       return [[[self alloc] initWithFade:(float)fade minSeg:seg image:path width:width length:length color:color] autorelease];
+}
+
+-(id)initWithFade:(float)fade minSeg:(float)seg image:(NSString*)path width:(float)width length:(float)length color:(ccColor4B)color
+{
+       if( (self=[super init])) {
+               segThreshold_ = seg;
+               width_ = width;
+               lastLocation_ = CGPointZero;
+               ribbon_ = [CCRibbon ribbonWithWidth:width_ image:path length:length color:color fade:fade];
+               [self addChild:ribbon_];
+
+               // update ribbon position. Use schedule:interval and not scheduleUpdated. issue #1075
+               [self schedule:@selector(update:) interval:0];
+       }
+       return self;
+}
+
+-(void)update:(ccTime)delta
+{
+       CGPoint location = [self convertToWorldSpace:CGPointZero];
+       [ribbon_ setPosition:ccp(-1*location.x, -1*location.y)];
+       float len = ccpLength(ccpSub(lastLocation_, location));
+       if (len > segThreshold_)
+       {
+               [ribbon_ addPointAt:location width:width_];
+               lastLocation_ = location;
+       }
+       [ribbon_ update:delta];
+}
+
+
+-(void)dealloc
+{
+       [super dealloc];
+}
+
+#pragma mark MotionStreak - CocosNodeTexture protocol
+
+-(void) setTexture:(CCTexture2D*) texture
+{
+       [ribbon_ setTexture: texture];
+}
+
+-(CCTexture2D*) texture
+{
+       return [ribbon_ texture];
+}
+
+-(ccBlendFunc) blendFunc
+{
+       return [ribbon_ blendFunc];
+}
+
+-(void) setBlendFunc:(ccBlendFunc)blendFunc
+{
+       [ribbon_ setBlendFunc:blendFunc];
+}
+
+@end
diff --git a/libs/cocos2d/CCNode.h b/libs/cocos2d/CCNode.h
new file mode 100644 (file)
index 0000000..64acdc5
--- /dev/null
@@ -0,0 +1,529 @@
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2009 Valentin Milea
+ *
+ * Copyright (c) 2008-2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#import <Availability.h>
+
+#import "Platforms/CCGL.h"
+#import "CCAction.h"
+#import "ccTypes.h"
+#import "CCTexture2D.h"
+#import "CCProtocols.h"
+#import "ccConfig.h"
+#import "Support/CCArray.h"
+
+enum {
+       kCCNodeTagInvalid = -1,
+};
+
+@class CCCamera;
+@class CCGridBase;
+
+/** CCNode is the main element. Anything thats gets drawn or contains things that get drawn is a CCNode.
+ The most popular CCNodes are: CCScene, CCLayer, CCSprite, CCMenu.
+ The main features of a CCNode are:
+ - They can contain other CCNode nodes (addChild, getChildByTag, removeChild, etc)
+ - They can schedule periodic callback (schedule, unschedule, etc)
+ - They can execute actions (runAction, stopAction, etc)
+ Some CCNode nodes provide extra functionality for them or their children.
+ Subclassing a CCNode usually means (one/all) of:
+ - overriding init to initialize resources and schedule callbacks
+ - create callbacks to handle the advancement of time
+ - overriding draw to render the node
+ Features of CCNode:
+ - position
+ - scale (x, y)
+ - rotation (in degrees, clockwise)
+ - CCCamera (an interface to gluLookAt )
+ - CCGridBase (to do mesh transformations)
+ - anchor point
+ - size
+ - visible
+ - z-order
+ - openGL z position
+ Default values:
+  - rotation: 0
+  - position: (x=0,y=0)
+  - scale: (x=1,y=1)
+  - contentSize: (x=0,y=0)
+  - anchorPoint: (x=0,y=0)
+ Limitations:
+ - A CCNode is a "void" object. It doesn't have a texture
+ Order in transformations with grid disabled
+ -# The node will be translated (position)
+ -# The node will be rotated (rotation)
+ -# The node will be scaled (scale)
+ -# The node will be moved according to the camera values (camera)
+ Order in transformations with grid enabled
+ -# The node will be translated (position)
+ -# The node will be rotated (rotation)
+ -# The node will be scaled (scale)
+ -# The grid will capture the screen
+ -# The node will be moved according to the camera values (camera)
+ -# The grid will render the captured screen
+ Camera:
+ - Each node has a camera. By default it points to the center of the CCNode.
+ */ 
+@interface CCNode : NSObject
+{      
+       // rotation angle
+       float rotation_;        
+       
+       // scaling factors
+       float scaleX_, scaleY_;
+       
+       // position of the node
+       CGPoint position_;
+       CGPoint positionInPixels_;
+       
+       // skew angles
+       float skewX_, skewY_;
+
+       // is visible
+       BOOL visible_;
+       
+       // anchor point in pixels
+       CGPoint anchorPointInPixels_;   
+       // anchor point normalized
+       CGPoint anchorPoint_;   
+       // If YES the transformtions will be relative to (-transform.x, -transform.y).
+       // Sprites, Labels and any other "small" object uses it.
+       // Scenes, Layers and other "whole screen" object don't use it.
+       BOOL isRelativeAnchorPoint_;
+       
+       // untransformed size of the node
+       CGSize  contentSize_;
+       CGSize  contentSizeInPixels_;
+       
+       // transform
+       CGAffineTransform transform_, inverse_;
+#if    CC_NODE_TRANSFORM_USING_AFFINE_MATRIX
+       GLfloat transformGL_[16];
+#endif
+
+       // openGL real Z vertex
+       float vertexZ_;
+       
+       // a Camera
+       CCCamera *camera_;
+       
+       // a Grid
+       CCGridBase *grid_;
+       
+       // z-order value
+       NSInteger zOrder_;
+       
+       // array of children
+       CCArray *children_;
+       
+       // weakref to parent
+       CCNode *parent_;
+       
+       // a tag. any number you want to assign to the node
+       NSInteger tag_;
+    
+       // user data field
+       void *userData_;
+
+       // Is running
+       BOOL isRunning_;
+
+       // To reduce memory, place BOOLs that are not properties here:
+       BOOL isTransformDirty_:1;
+       BOOL isInverseDirty_:1;
+#if    CC_NODE_TRANSFORM_USING_AFFINE_MATRIX
+       BOOL isTransformGLDirty_:1;
+#endif
+}
+
+/** The z order of the node relative to it's "brothers": children of the same parent */
+@property(nonatomic,readonly) NSInteger zOrder;
+/** The real openGL Z vertex.
+ Differences between openGL Z vertex and cocos2d Z order:
+   - OpenGL Z modifies the Z vertex, and not the Z order in the relation between parent-children
+   - OpenGL Z might require to set 2D projection
+   - cocos2d Z order works OK if all the nodes uses the same openGL Z vertex. eg: vertexZ = 0
+ @warning: Use it at your own risk since it might break the cocos2d parent-children z order
+ @since v0.8
+ */
+@property (nonatomic,readwrite) float vertexZ;
+
+/** The X skew angle of the node in degrees.
+ This angle describes the shear distortion in the X direction.
+ Thus, it is the angle between the Y axis and the left edge of the shape
+ The default skewX angle is 0. Positive values distort the node in a CW direction.
+ */
+@property(nonatomic,readwrite,assign) float skewX;
+
+/** The Y skew angle of the node in degrees.
+ This angle describes the shear distortion in the Y direction.
+ Thus, it is the angle between the X axis and the bottom edge of the shape
+ The default skewY angle is 0. Positive values distort the node in a CCW direction.
+ */
+@property(nonatomic,readwrite,assign) float skewY;
+/** The rotation (angle) of the node in degrees. 0 is the default rotation angle. Positive values rotate node CW. */
+@property(nonatomic,readwrite,assign) float rotation;
+/** The scale factor of the node. 1.0 is the default scale factor. It modifies the X and Y scale at the same time. */
+@property(nonatomic,readwrite,assign) float scale;
+/** The scale factor of the node. 1.0 is the default scale factor. It only modifies the X scale factor. */
+@property(nonatomic,readwrite,assign) float scaleX;
+/** The scale factor of the node. 1.0 is the default scale factor. It only modifies the Y scale factor. */
+@property(nonatomic,readwrite,assign) float scaleY;
+/** Position (x,y) of the node in points. (0,0) is the left-bottom corner. */
+@property(nonatomic,readwrite,assign) CGPoint position;
+/** Position (x,y) of the node in points. (0,0) is the left-bottom corner. */
+@property(nonatomic,readwrite,assign) CGPoint positionInPixels;
+/** A CCCamera object that lets you move the node using a gluLookAt
+*/
+@property(nonatomic,readonly) CCCamera* camera;
+/** Array of children */
+@property(nonatomic,readonly) CCArray *children;
+/** A CCGrid object that is used when applying effects */
+@property(nonatomic,readwrite,retain) CCGridBase* grid;
+/** Whether of not the node is visible. Default is YES */
+@property(nonatomic,readwrite,assign) BOOL visible;
+/** anchorPoint is the point around which all transformations and positioning manipulations take place.
+ It's like a pin in the node where it is "attached" to its parent.
+ The anchorPoint is normalized, like a percentage. (0,0) means the bottom-left corner and (1,1) means the top-right corner.
+ But you can use values higher than (1,1) and lower than (0,0) too.
+ The default anchorPoint is (0,0). It starts in the bottom-left corner. CCSprite and other subclasses have a different default anchorPoint.
+ @since v0.8
+ */
+@property(nonatomic,readwrite) CGPoint anchorPoint;
+/** The anchorPoint in absolute pixels.
+ Since v0.8 you can only read it. If you wish to modify it, use anchorPoint instead
+ */
+@property(nonatomic,readonly) CGPoint anchorPointInPixels;
+
+/** The untransformed size of the node in Points
+ The contentSize remains the same no matter the node is scaled or rotated.
+ All nodes has a size. Layer and Scene has the same size of the screen.
+ @since v0.8
+ */
+@property (nonatomic,readwrite) CGSize contentSize;
+
+/** The untransformed size of the node in Pixels
+ The contentSize remains the same no matter the node is scaled or rotated.
+ All nodes has a size. Layer and Scene has the same size of the screen.
+ @since v0.8
+ */
+@property (nonatomic,readwrite) CGSize contentSizeInPixels;
+
+/** whether or not the node is running */
+@property(nonatomic,readonly) BOOL isRunning;
+/** A weak reference to the parent */
+@property(nonatomic,readwrite,assign) CCNode* parent;
+/** If YES the transformtions will be relative to it's anchor point.
+ * Sprites, Labels and any other sizeble object use it have it enabled by default.
+ * Scenes, Layers and other "whole screen" object don't use it, have it disabled by default.
+ */
+@property(nonatomic,readwrite,assign) BOOL isRelativeAnchorPoint;
+/** A tag used to identify the node easily */
+@property(nonatomic,readwrite,assign) NSInteger tag;
+/** A custom user data pointer */
+@property(nonatomic,readwrite,assign) void *userData;
+
+// initializators
+/** allocates and initializes a node.
+ The node will be created as "autorelease".
+ */
++(id) node;
+/** initializes the node */
+-(id) init;
+
+
+// scene managment
+
+/** callback that is called every time the CCNode enters the 'stage'.
+ If the CCNode enters the 'stage' with a transition, this callback is called when the transition starts.
+ During onEnter you can't a "sister/brother" node.
+ */
+-(void) onEnter;
+/** callback that is called when the CCNode enters in the 'stage'.
+ If the CCNode enters the 'stage' with a transition, this callback is called when the transition finishes.
+ @since v0.8
+ */
+-(void) onEnterTransitionDidFinish;
+/** callback that is called every time the CCNode leaves the 'stage'.
+ If the CCNode leaves the 'stage' with a transition, this callback is called when the transition finishes.
+ During onExit you can't access a sibling node.
+ */
+-(void) onExit;
+
+
+// composition: ADD
+
+/** Adds a child to the container with z-order as 0.
+ If the child is added to a 'running' node, then 'onEnter' and 'onEnterTransitionDidFinish' will be called immediately.
+ @since v0.7.1
+ */
+-(void) addChild: (CCNode*)node;
+
+/** Adds a child to the container with a z-order.
+ If the child is added to a 'running' node, then 'onEnter' and 'onEnterTransitionDidFinish' will be called immediately.
+ @since v0.7.1
+ */
+-(void) addChild: (CCNode*)node z:(NSInteger)z;
+
+/** Adds a child to the container with z order and tag.
+ If the child is added to a 'running' node, then 'onEnter' and 'onEnterTransitionDidFinish' will be called immediately.
+ @since v0.7.1
+ */
+-(void) addChild: (CCNode*)node z:(NSInteger)z tag:(NSInteger)tag;
+
+// composition: REMOVE
+
+/** Remove itself from its parent node. If cleanup is YES, then also remove all actions and callbacks.
+ If the node orphan, then nothing happens.
+ @since v0.99.3
+ */
+-(void) removeFromParentAndCleanup:(BOOL)cleanup;
+
+/** Removes a child from the container. It will also cleanup all running actions depending on the cleanup parameter.
+ @since v0.7.1
+ */
+-(void) removeChild: (CCNode*)node cleanup:(BOOL)cleanup;
+
+/** Removes a child from the container by tag value. It will also cleanup all running actions depending on the cleanup parameter
+ @since v0.7.1
+ */
+-(void) removeChildByTag:(NSInteger) tag cleanup:(BOOL)cleanup;
+
+/** Removes all children from the container and do a cleanup all running actions depending on the cleanup parameter.
+ @since v0.7.1
+ */
+-(void) removeAllChildrenWithCleanup:(BOOL)cleanup;
+
+// composition: GET
+/** Gets a child from the container given its tag
+ @return returns a CCNode object
+ @since v0.7.1
+ */
+-(CCNode*) getChildByTag:(NSInteger) tag;
+
+/** Reorders a child according to a new z value.
+ * The child MUST be already added.
+ */
+-(void) reorderChild:(CCNode*)child z:(NSInteger)zOrder;
+
+/** Stops all running actions and schedulers
+ @since v0.8
+ */
+-(void) cleanup;
+
+// draw
+
+/** Override this method to draw your own node.
+ The following GL states will be enabled by default:
+       - glEnableClientState(GL_VERTEX_ARRAY);
+       - glEnableClientState(GL_COLOR_ARRAY);
+       - glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+       - glEnable(GL_TEXTURE_2D);
+   AND YOU SHOULD NOT DISABLE THEM AFTER DRAWING YOUR NODE
+ But if you enable any other GL state, you should disable it after drawing your node.
+ */
+-(void) draw;
+/** recursive method that visit its children and draw them */
+-(void) visit;
+
+// transformations
+
+/** performs OpenGL view-matrix transformation based on position, scale, rotation and other attributes. */
+-(void) transform;
+
+/** performs OpenGL view-matrix transformation of it's ancestors.
+ Generally the ancestors are already transformed, but in certain cases (eg: attaching a FBO)
+ it's necessary to transform the ancestors again.
+ @since v0.7.2
+ */
+-(void) transformAncestors;
+
+/** returns a "local" axis aligned bounding box of the node in points.
+ The returned box is relative only to its parent.
+ The returned box is in Points.
+ @since v0.8.2
+ */
+- (CGRect) boundingBox;
+
+/** returns a "local" axis aligned bounding box of the node in pixels.
+ The returned box is relative only to its parent.
+ The returned box is in Points.
+ @since v0.99.5
+ */
+- (CGRect) boundingBoxInPixels;
+
+
+// actions
+
+/** Executes an action, and returns the action that is executed.
+ The node becomes the action's target.
+ @warning Starting from v0.8 actions don't retain their target anymore.
+ @since v0.7.1
+ @return An Action pointer
+ */
+-(CCAction*) runAction: (CCAction*) action;
+/** Removes all actions from the running action list */
+-(void) stopAllActions;
+/** Removes an action from the running action list */
+-(void) stopAction: (CCAction*) action;
+/** Removes an action from the running action list given its tag
+ @since v0.7.1
+*/
+-(void) stopActionByTag:(NSInteger) tag;
+/** Gets an action from the running action list given its tag
+ @since v0.7.1
+ @return the Action the with the given tag
+ */
+-(CCAction*) getActionByTag:(NSInteger) tag;
+/** Returns the numbers of actions that are running plus the ones that are schedule to run (actions in actionsToAdd and actions arrays). 
+ * Composable actions are counted as 1 action. Example:
+ *    If you are running 1 Sequence of 7 actions, it will return 1.
+ *    If you are running 7 Sequences of 2 actions, it will return 7.
+ */
+-(NSUInteger) numberOfRunningActions;
+
+// timers
+
+/** check whether a selector is scheduled. */
+//-(BOOL) isScheduled: (SEL) selector;
+
+/** schedules the "update" method. It will use the order number 0. This method will be called every frame.
+ Scheduled methods with a lower order value will be called before the ones that have a higher order value.
+ Only one "udpate" method could be scheduled per node.
+ @since v0.99.3
+ */
+-(void) scheduleUpdate;
+
+/** schedules the "update" selector with a custom priority. This selector will be called every frame.
+ Scheduled selectors with a lower priority will be called before the ones that have a higher value.
+ Only one "udpate" selector could be scheduled per node (You can't have 2 'update' selectors).
+
+ @since v0.99.3
+ */
+-(void) scheduleUpdateWithPriority:(NSInteger)priority;
+
+/* unschedules the "update" method.
+ @since v0.99.3
+ */
+-(void) unscheduleUpdate;
+
+
+/** schedules a selector.
+ The scheduled selector will be ticked every frame
+ */
+-(void) schedule: (SEL) s;
+/** schedules a custom selector with an interval time in seconds.
+ If time is 0 it will be ticked every frame.
+ If time is 0, it is recommended to use 'scheduleUpdate' instead.
+ If the selector is already scheduled, then the interval parameter will be updated without scheduling it again.
+ */
+-(void) schedule: (SEL) s interval:(ccTime)seconds;
+/** unschedules a custom selector.*/
+-(void) unschedule: (SEL) s;
+
+/** unschedule all scheduled selectors: custom selectors, and the 'update' selector.
+ Actions are not affected by this method.
+@since v0.99.3
+ */
+-(void) unscheduleAllSelectors;
+
+/** resumes all scheduled selectors and actions.
+ Called internally by onEnter
+ */
+-(void) resumeSchedulerAndActions;
+/** pauses all scheduled selectors and actions.
+ Called internally by onExit
+ */
+-(void) pauseSchedulerAndActions;
+
+
+// transformation methods
+
+/** Returns the matrix that transform the node's (local) space coordinates into the parent's space coordinates.
+ The matrix is in Pixels.
+ @since v0.7.1
+ */
+- (CGAffineTransform)nodeToParentTransform;
+/** Returns the matrix that transform parent's space coordinates to the node's (local) space coordinates.
+ The matrix is in Pixels.
+ @since v0.7.1
+ */
+- (CGAffineTransform)parentToNodeTransform;
+/** Retrusn the world affine transform matrix. The matrix is in Pixels.
+ @since v0.7.1
+ */
+- (CGAffineTransform)nodeToWorldTransform;
+/** Returns the inverse world affine transform matrix. The matrix is in Pixels.
+ @since v0.7.1
+ */
+- (CGAffineTransform)worldToNodeTransform;
+/** Converts a Point to node (local) space coordinates. The result is in Points.
+ @since v0.7.1
+ */
+- (CGPoint)convertToNodeSpace:(CGPoint)worldPoint;
+/** Converts a Point to world space coordinates. The result is in Points.
+ @since v0.7.1
+ */
+- (CGPoint)convertToWorldSpace:(CGPoint)nodePoint;
+/** Converts a Point to node (local) space coordinates. The result is in Points.
+ treating the returned/received node point as anchor relative.
+ @since v0.7.1
+ */
+- (CGPoint)convertToNodeSpaceAR:(CGPoint)worldPoint;
+/** Converts a local Point to world space coordinates.The result is in Points.
+ treating the returned/received node point as anchor relative.
+ @since v0.7.1
+ */
+- (CGPoint)convertToWorldSpaceAR:(CGPoint)nodePoint;
+
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+/** Converts a UITouch to node (local) space coordinates. The result is in Points.
+ @since v0.7.1
+ */
+- (CGPoint)convertTouchToNodeSpace:(UITouch *)touch;
+/** Converts a UITouch to node (local) space coordinates. The result is in Points.
+ This method is AR (Anchor Relative)..
+ @since v0.7.1
+ */
+- (CGPoint)convertTouchToNodeSpaceAR:(UITouch *)touch;
+#endif // __IPHONE_OS_VERSION_MAX_ALLOWED
+@end
diff --git a/libs/cocos2d/CCNode.m b/libs/cocos2d/CCNode.m
new file mode 100644 (file)
index 0000000..569cb22
--- /dev/null
@@ -0,0 +1,921 @@
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2009 Valentin Milea
+ *
+ * Copyright (c) 2008-2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#import "CCNode.h"
+#import "CCGrid.h"
+#import "CCDirector.h"
+#import "CCActionManager.h"
+#import "CCCamera.h"
+#import "CCScheduler.h"
+#import "ccConfig.h"
+#import "ccMacros.h"
+#import "Support/CGPointExtension.h"
+#import "Support/ccCArray.h"
+#import "Support/TransformUtils.h"
+#import "ccMacros.h"
+
+#import <Availability.h>
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+#import "Platforms/iOS/CCDirectorIOS.h"
+#endif
+
+
+#if CC_COCOSNODE_RENDER_SUBPIXEL
+#define RENDER_IN_SUBPIXEL
+#else
+#define RENDER_IN_SUBPIXEL (NSInteger)
+#endif
+
+@interface CCNode ()
+// lazy allocs
+-(void) childrenAlloc;
+// helper that reorder a child
+-(void) insertChild:(CCNode*)child z:(NSInteger)z;
+// used internally to alter the zOrder variable. DON'T call this method manually
+-(void) _setZOrder:(NSInteger) z;
+-(void) detachChild:(CCNode *)child cleanup:(BOOL)doCleanup;
+@end
+
+@implementation CCNode
+
+@synthesize children = children_;
+@synthesize visible = visible_;
+@synthesize parent = parent_;
+@synthesize grid = grid_;
+@synthesize zOrder = zOrder_;
+@synthesize tag = tag_;
+@synthesize vertexZ = vertexZ_;
+@synthesize isRunning = isRunning_;
+@synthesize userData = userData_;
+
+#pragma mark CCNode - Transform related properties
+
+@synthesize rotation = rotation_, scaleX = scaleX_, scaleY = scaleY_;
+@synthesize skewX = skewX_, skewY = skewY_;
+@synthesize position = position_, positionInPixels = positionInPixels_;
+@synthesize anchorPoint = anchorPoint_, anchorPointInPixels = anchorPointInPixels_;
+@synthesize contentSize = contentSize_, contentSizeInPixels = contentSizeInPixels_;
+@synthesize isRelativeAnchorPoint = isRelativeAnchorPoint_;
+
+// getters synthesized, setters explicit
+
+-(void) setSkewX:(float)newSkewX
+{
+       skewX_ = newSkewX;
+       isTransformDirty_ = isInverseDirty_ = YES;
+#if CC_NODE_TRANSFORM_USING_AFFINE_MATRIX
+       isTransformGLDirty_ = YES;
+#endif
+}
+
+-(void) setSkewY:(float)newSkewY
+{
+       skewY_ = newSkewY;
+       isTransformDirty_ = isInverseDirty_ = YES;
+#if CC_NODE_TRANSFORM_USING_AFFINE_MATRIX
+       isTransformGLDirty_ = YES;
+#endif
+}
+
+-(void) setRotation: (float)newRotation
+{
+       rotation_ = newRotation;
+       isTransformDirty_ = isInverseDirty_ = YES;
+#if CC_NODE_TRANSFORM_USING_AFFINE_MATRIX
+       isTransformGLDirty_ = YES;
+#endif
+}
+
+-(void) setScaleX: (float)newScaleX
+{
+       scaleX_ = newScaleX;
+       isTransformDirty_ = isInverseDirty_ = YES;
+#if CC_NODE_TRANSFORM_USING_AFFINE_MATRIX
+       isTransformGLDirty_ = YES;
+#endif 
+}
+
+-(void) setScaleY: (float)newScaleY
+{
+       scaleY_ = newScaleY;
+       isTransformDirty_ = isInverseDirty_ = YES;
+#if CC_NODE_TRANSFORM_USING_AFFINE_MATRIX
+       isTransformGLDirty_ = YES;
+#endif 
+}
+
+-(void) setPosition: (CGPoint)newPosition
+{
+       position_ = newPosition;
+       if( CC_CONTENT_SCALE_FACTOR() == 1 )
+               positionInPixels_ = position_;
+       else
+               positionInPixels_ = ccpMult( newPosition,  CC_CONTENT_SCALE_FACTOR() );
+       
+       isTransformDirty_ = isInverseDirty_ = YES;
+#if CC_NODE_TRANSFORM_USING_AFFINE_MATRIX
+       isTransformGLDirty_ = YES;
+#endif 
+}
+
+-(void) setPositionInPixels:(CGPoint)newPosition
+{
+       positionInPixels_ = newPosition;
+
+       if( CC_CONTENT_SCALE_FACTOR() == 1 )
+               position_ = positionInPixels_;
+       else
+               position_ = ccpMult( newPosition, 1/CC_CONTENT_SCALE_FACTOR() );
+       
+       isTransformDirty_ = isInverseDirty_ = YES;
+#if CC_NODE_TRANSFORM_USING_AFFINE_MATRIX
+       isTransformGLDirty_ = YES;
+#endif 
+}
+
+-(void) setIsRelativeAnchorPoint: (BOOL)newValue
+{
+       isRelativeAnchorPoint_ = newValue;
+       isTransformDirty_ = isInverseDirty_ = YES;
+#if CC_NODE_TRANSFORM_USING_AFFINE_MATRIX
+       isTransformGLDirty_ = YES;
+#endif 
+}
+
+-(void) setAnchorPoint:(CGPoint)point
+{
+       if( ! CGPointEqualToPoint(point, anchorPoint_) ) {
+               anchorPoint_ = point;
+               anchorPointInPixels_ = ccp( contentSizeInPixels_.width * anchorPoint_.x, contentSizeInPixels_.height * anchorPoint_.y );
+               isTransformDirty_ = isInverseDirty_ = YES;
+#if CC_NODE_TRANSFORM_USING_AFFINE_MATRIX
+               isTransformGLDirty_ = YES;
+#endif         
+       }
+}
+
+-(void) setContentSize:(CGSize)size
+{
+       if( ! CGSizeEqualToSize(size, contentSize_) ) {
+               contentSize_ = size;
+               
+               if( CC_CONTENT_SCALE_FACTOR() == 1 )
+                       contentSizeInPixels_ = contentSize_;
+               else
+                       contentSizeInPixels_ = CGSizeMake( size.width * CC_CONTENT_SCALE_FACTOR(), size.height * CC_CONTENT_SCALE_FACTOR() );
+               
+               anchorPointInPixels_ = ccp( contentSizeInPixels_.width * anchorPoint_.x, contentSizeInPixels_.height * anchorPoint_.y );
+               isTransformDirty_ = isInverseDirty_ = YES;
+#if CC_NODE_TRANSFORM_USING_AFFINE_MATRIX
+               isTransformGLDirty_ = YES;
+#endif         
+       }
+}
+
+-(void) setContentSizeInPixels:(CGSize)size
+{
+       if( ! CGSizeEqualToSize(size, contentSizeInPixels_) ) {
+               contentSizeInPixels_ = size;
+
+               if( CC_CONTENT_SCALE_FACTOR() == 1 )
+                       contentSize_ = contentSizeInPixels_;
+               else
+                       contentSize_ = CGSizeMake( size.width / CC_CONTENT_SCALE_FACTOR(), size.height / CC_CONTENT_SCALE_FACTOR() );
+               
+               anchorPointInPixels_ = ccp( contentSizeInPixels_.width * anchorPoint_.x, contentSizeInPixels_.height * anchorPoint_.y );
+               isTransformDirty_ = isInverseDirty_ = YES;
+#if CC_NODE_TRANSFORM_USING_AFFINE_MATRIX
+               isTransformGLDirty_ = YES;
+#endif         
+       }
+}
+
+- (CGRect) boundingBox
+{
+       CGRect ret = [self boundingBoxInPixels];
+       return CC_RECT_PIXELS_TO_POINTS( ret );
+}
+
+- (CGRect) boundingBoxInPixels
+{
+       CGRect rect = CGRectMake(0, 0, contentSizeInPixels_.width, contentSizeInPixels_.height);
+       return CGRectApplyAffineTransform(rect, [self nodeToParentTransform]);
+}
+
+-(void) setVertexZ:(float)vertexZ
+{
+       vertexZ_ = vertexZ * CC_CONTENT_SCALE_FACTOR();
+}
+
+-(float) scale
+{
+       NSAssert( scaleX_ == scaleY_, @"CCNode#scale. ScaleX != ScaleY. Don't know which one to return");
+       return scaleX_;
+}
+
+-(void) setScale:(float) s
+{
+       scaleX_ = scaleY_ = s;
+       isTransformDirty_ = isInverseDirty_ = YES;
+#if CC_NODE_TRANSFORM_USING_AFFINE_MATRIX
+       isTransformGLDirty_ = YES;
+#endif 
+}
+
+#pragma mark CCNode - Init & cleanup
+
++(id) node
+{
+       return [[[self alloc] init] autorelease];
+}
+
+-(id) init
+{
+       if ((self=[super init]) ) {
+               
+               isRunning_ = NO;
+               
+               skewX_ = skewY_ = 0.0f;
+               rotation_ = 0.0f;
+               scaleX_ = scaleY_ = 1.0f;
+               positionInPixels_ = position_ = CGPointZero;
+               anchorPointInPixels_ = anchorPoint_ = CGPointZero;
+               contentSizeInPixels_ = contentSize_ = CGSizeZero;
+               
+               
+               // "whole screen" objects. like Scenes and Layers, should set isRelativeAnchorPoint to NO
+               isRelativeAnchorPoint_ = YES; 
+               
+               isTransformDirty_ = isInverseDirty_ = YES;
+#if CC_NODE_TRANSFORM_USING_AFFINE_MATRIX
+               isTransformGLDirty_ = YES;
+#endif
+               
+               vertexZ_ = 0;
+               
+               grid_ = nil;
+               
+               visible_ = YES;
+               
+               tag_ = kCCNodeTagInvalid;
+               
+               zOrder_ = 0;
+               
+               // lazy alloc
+               camera_ = nil;
+               
+               // children (lazy allocs)
+               children_ = nil;
+               
+               // userData is always inited as nil
+               userData_ = nil;
+
+               //initialize parent to nil
+               parent_ = nil;
+       }
+       
+       return self;
+}
+
+- (void)cleanup
+{
+       // actions
+       [self stopAllActions];
+       [self unscheduleAllSelectors];
+       
+       // timers
+       [children_ makeObjectsPerformSelector:@selector(cleanup)];
+}
+
+- (NSString*) description
+{
+       return [NSString stringWithFormat:@"<%@ = %08X | Tag = %i>", [self class], self, tag_];
+}
+
+- (void) dealloc
+{
+       CCLOGINFO( @"cocos2d: deallocing %@", self);
+       
+       // attributes
+       [camera_ release];
+       
+       [grid_ release];
+       
+       // children
+       CCNode *child;
+       CCARRAY_FOREACH(children_, child)
+               child.parent = nil;
+       
+       [children_ release];
+       
+       [super dealloc];
+}
+
+#pragma mark CCNode Composition
+
+-(void) childrenAlloc
+{
+       children_ = [[CCArray alloc] initWithCapacity:4];
+}
+
+// camera: lazy alloc
+-(CCCamera*) camera
+{
+       if( ! camera_ ) {
+               camera_ = [[CCCamera alloc] init];
+               
+               // by default, center camera at the Sprite's anchor point
+               //              [camera_ setCenterX:anchorPointInPixels_.x centerY:anchorPointInPixels_.y centerZ:0];
+               //              [camera_ setEyeX:anchorPointInPixels_.x eyeY:anchorPointInPixels_.y eyeZ:1];
+               
+               //              [camera_ setCenterX:0 centerY:0 centerZ:0];
+               //              [camera_ setEyeX:0 eyeY:0 eyeZ:1];
+               
+       }
+       
+       return camera_;
+}
+
+-(CCNode*) getChildByTag:(NSInteger) aTag
+{
+       NSAssert( aTag != kCCNodeTagInvalid, @"Invalid tag");
+       
+       CCNode *node;
+       CCARRAY_FOREACH(children_, node){
+               if( node.tag == aTag )
+                       return node;
+       }
+       // not found
+       return nil;
+}
+
+/* "add" logic MUST only be on this method
+ * If a class want's to extend the 'addChild' behaviour it only needs
+ * to override this method
+ */
+-(void) addChild: (CCNode*) child z:(NSInteger)z tag:(NSInteger) aTag
+{      
+       NSAssert( child != nil, @"Argument must be non-nil");
+       NSAssert( child.parent == nil, @"child already added. It can't be added again");
+       
+       if( ! children_ )
+               [self childrenAlloc];
+       
+       [self insertChild:child z:z];
+       
+       child.tag = aTag;
+       
+       [child setParent: self];
+       
+       if( isRunning_ ) {
+               [child onEnter];
+               [child onEnterTransitionDidFinish];
+       }
+}
+
+-(void) addChild: (CCNode*) child z:(NSInteger)z
+{
+       NSAssert( child != nil, @"Argument must be non-nil");
+       [self addChild:child z:z tag:child.tag];
+}
+
+-(void) addChild: (CCNode*) child
+{
+       NSAssert( child != nil, @"Argument must be non-nil");
+       [self addChild:child z:child.zOrder tag:child.tag];
+}
+
+-(void) removeFromParentAndCleanup:(BOOL)cleanup
+{
+       [parent_ removeChild:self cleanup:cleanup];
+}
+
+/* "remove" logic MUST only be on this method
+ * If a class want's to extend the 'removeChild' behavior it only needs
+ * to override this method
+ */
+-(void) removeChild: (CCNode*)child cleanup:(BOOL)cleanup
+{
+       // explicit nil handling
+       if (child == nil)
+               return;
+       
+       if ( [children_ containsObject:child] )
+               [self detachChild:child cleanup:cleanup];
+}
+
+-(void) removeChildByTag:(NSInteger)aTag cleanup:(BOOL)cleanup
+{
+       NSAssert( aTag != kCCNodeTagInvalid, @"Invalid tag");
+       
+       CCNode *child = [self getChildByTag:aTag];
+       
+       if (child == nil)
+               CCLOG(@"cocos2d: removeChildByTag: child not found!");
+       else
+               [self removeChild:child cleanup:cleanup];
+}
+
+-(void) removeAllChildrenWithCleanup:(BOOL)cleanup
+{
+       // not using detachChild improves speed here
+       CCNode *c;
+       CCARRAY_FOREACH(children_, c)
+       {
+               // IMPORTANT:
+               //  -1st do onExit
+               //  -2nd cleanup
+               if (isRunning_)
+                       [c onExit];
+               
+               if (cleanup)
+                       [c cleanup];
+               
+               // set parent nil at the end (issue #476)
+               [c setParent:nil];
+       }
+       
+       [children_ removeAllObjects];
+}
+
+-(void) detachChild:(CCNode *)child cleanup:(BOOL)doCleanup
+{
+       // IMPORTANT:
+       //  -1st do onExit
+       //  -2nd cleanup
+       if (isRunning_)
+               [child onExit];
+       
+       // If you don't do cleanup, the child's actions will not get removed and the
+       // its scheduledSelectors_ dict will not get released!
+       if (doCleanup)
+               [child cleanup];
+       
+       // set parent nil at the end (issue #476)
+       [child setParent:nil];
+       
+       [children_ removeObject:child];
+}
+
+// used internally to alter the zOrder variable. DON'T call this method manually
+-(void) _setZOrder:(NSInteger) z
+{
+       zOrder_ = z;
+}
+
+// helper used by reorderChild & add
+-(void) insertChild:(CCNode*)child z:(NSInteger)z
+{
+       NSUInteger index=0;
+       CCNode *a = [children_ lastObject];
+       
+       // quick comparison to improve performance
+       if (!a || a.zOrder <= z)
+               [children_ addObject:child];
+       
+       else
+       {
+               CCARRAY_FOREACH(children_, a) {
+                       if ( a.zOrder > z ) {
+                               [children_ insertObject:child atIndex:index];
+                               break;
+                       }
+                       index++;
+               }
+       }
+       
+       [child _setZOrder:z];
+}
+
+-(void) reorderChild:(CCNode*) child z:(NSInteger)z
+{
+       NSAssert( child != nil, @"Child must be non-nil");
+       
+       [child retain];
+       [children_ removeObject:child];
+       
+       [self insertChild:child z:z];
+       
+       [child release];
+}
+
+#pragma mark CCNode Draw
+
+-(void) draw
+{
+       // override me
+       // Only use this function to draw your staff.
+       // DON'T draw your stuff outside this method
+}
+
+-(void) visit
+{
+       // quick return if not visible
+       if (!visible_)
+               return;
+       
+       glPushMatrix();
+       
+       if ( grid_ && grid_.active) {
+               [grid_ beforeDraw];
+               [self transformAncestors];
+       }
+
+       [self transform];
+       
+       if(children_) {
+               ccArray *arrayData = children_->data;
+               NSUInteger i = 0;
+               
+               // draw children zOrder < 0
+               for( ; i < arrayData->num; i++ ) {
+                       CCNode *child = arrayData->arr[i];
+                       if ( [child zOrder] < 0 )
+                               [child visit];
+                       else
+                               break;
+               }
+               
+               // self draw
+               [self draw];
+               
+               // draw children zOrder >= 0
+               for( ; i < arrayData->num; i++ ) {
+                       CCNode *child =  arrayData->arr[i];
+                       [child visit];
+               }
+
+       } else
+               [self draw];
+       
+       if ( grid_ && grid_.active)
+               [grid_ afterDraw:self];
+       
+       glPopMatrix();
+}
+
+#pragma mark CCNode - Transformations
+
+-(void) transformAncestors
+{
+       if( parent_ ) {
+               [parent_ transformAncestors];
+               [parent_ transform];
+       }
+}
+
+-(void) transform
+{      
+       // transformations
+       
+#if CC_NODE_TRANSFORM_USING_AFFINE_MATRIX
+       // BEGIN alternative -- using cached transform
+       //
+       if( isTransformGLDirty_ ) {
+               CGAffineTransform t = [self nodeToParentTransform];
+               CGAffineToGL(&t, transformGL_);
+               isTransformGLDirty_ = NO;
+       }
+       
+       glMultMatrixf(transformGL_);
+       if( vertexZ_ )
+               glTranslatef(0, 0, vertexZ_);
+       
+       // XXX: Expensive calls. Camera should be integrated into the cached affine matrix
+       if ( camera_ && !(grid_ && grid_.active) )
+       {
+               BOOL translate = (anchorPointInPixels_.x != 0.0f || anchorPointInPixels_.y != 0.0f);
+               
+               if( translate )
+                       ccglTranslate(RENDER_IN_SUBPIXEL(anchorPointInPixels_.x), RENDER_IN_SUBPIXEL(anchorPointInPixels_.y), 0);
+               
+               [camera_ locate];
+               
+               if( translate )
+                       ccglTranslate(RENDER_IN_SUBPIXEL(-anchorPointInPixels_.x), RENDER_IN_SUBPIXEL(-anchorPointInPixels_.y), 0);
+       }
+       
+       
+       // END alternative
+       
+#else
+       // BEGIN original implementation
+       // 
+       // translate
+       if ( isRelativeAnchorPoint_ && (anchorPointInPixels_.x != 0 || anchorPointInPixels_.y != 0 ) )
+               glTranslatef( RENDER_IN_SUBPIXEL(-anchorPointInPixels_.x), RENDER_IN_SUBPIXEL(-anchorPointInPixels_.y), 0);
+       
+       if (anchorPointInPixels_.x != 0 || anchorPointInPixels_.y != 0)
+               glTranslatef( RENDER_IN_SUBPIXEL(positionInPixels_.x + anchorPointInPixels_.x), RENDER_IN_SUBPIXEL(positionInPixels_.y + anchorPointInPixels_.y), vertexZ_);
+       else if ( positionInPixels_.x !=0 || positionInPixels_.y !=0 || vertexZ_ != 0)
+               glTranslatef( RENDER_IN_SUBPIXEL(positionInPixels_.x), RENDER_IN_SUBPIXEL(positionInPixels_.y), vertexZ_ );
+       
+       // rotate
+       if (rotation_ != 0.0f )
+               glRotatef( -rotation_, 0.0f, 0.0f, 1.0f );
+       
+       // skew
+       if ( (skewX_ != 0.0f) || (skewY_ != 0.0f) ) {
+               CGAffineTransform skewMatrix = CGAffineTransformMake( 1.0f, tanf(CC_DEGREES_TO_RADIANS(skewY_)), tanf(CC_DEGREES_TO_RADIANS(skewX_)), 1.0f, 0.0f, 0.0f );
+               GLfloat glMatrix[16];
+               CGAffineToGL(&skewMatrix, glMatrix);                                                                                                                     
+               glMultMatrixf(glMatrix);
+       }
+       
+       // scale
+       if (scaleX_ != 1.0f || scaleY_ != 1.0f)
+               glScalef( scaleX_, scaleY_, 1.0f );
+       
+       if ( camera_ && !(grid_ && grid_.active) )
+               [camera_ locate];
+       
+       // restore and re-position point
+       if (anchorPointInPixels_.x != 0.0f || anchorPointInPixels_.y != 0.0f)
+               glTranslatef(RENDER_IN_SUBPIXEL(-anchorPointInPixels_.x), RENDER_IN_SUBPIXEL(-anchorPointInPixels_.y), 0);
+       
+       //
+       // END original implementation
+#endif
+       
+}
+
+#pragma mark CCNode SceneManagement
+
+-(void) onEnter
+{
+       [children_ makeObjectsPerformSelector:@selector(onEnter)];      
+       [self resumeSchedulerAndActions];
+       
+       isRunning_ = YES;
+}
+
+-(void) onEnterTransitionDidFinish
+{
+       [children_ makeObjectsPerformSelector:@selector(onEnterTransitionDidFinish)];
+}
+
+-(void) onExit
+{
+       [self pauseSchedulerAndActions];
+       isRunning_ = NO;        
+       
+       [children_ makeObjectsPerformSelector:@selector(onExit)];
+}
+
+#pragma mark CCNode Actions
+
+-(CCAction*) runAction:(CCAction*) action
+{
+       NSAssert( action != nil, @"Argument must be non-nil");
+       
+       [[CCActionManager sharedManager] addAction:action target:self paused:!isRunning_];
+       return action;
+}
+
+-(void) stopAllActions
+{
+       [[CCActionManager sharedManager] removeAllActionsFromTarget:self];
+}
+
+-(void) stopAction: (CCAction*) action
+{
+       [[CCActionManager sharedManager] removeAction:action];
+}
+
+-(void) stopActionByTag:(NSInteger)aTag
+{
+       NSAssert( aTag != kCCActionTagInvalid, @"Invalid tag");
+       [[CCActionManager sharedManager] removeActionByTag:aTag target:self];
+}
+
+-(CCAction*) getActionByTag:(NSInteger) aTag
+{
+       NSAssert( aTag != kCCActionTagInvalid, @"Invalid tag");
+       return [[CCActionManager sharedManager] getActionByTag:aTag target:self];
+}
+
+-(NSUInteger) numberOfRunningActions
+{
+       return [[CCActionManager sharedManager] numberOfRunningActionsInTarget:self];
+}
+
+#pragma mark CCNode - Scheduler
+
+-(void) scheduleUpdate
+{
+       [self scheduleUpdateWithPriority:0];
+}
+
+-(void) scheduleUpdateWithPriority:(NSInteger)priority
+{
+       [[CCScheduler sharedScheduler] scheduleUpdateForTarget:self priority:priority paused:!isRunning_];
+}
+
+-(void) unscheduleUpdate
+{
+       [[CCScheduler sharedScheduler] unscheduleUpdateForTarget:self];
+}
+
+-(void) schedule:(SEL)selector
+{
+       [self schedule:selector interval:0];
+}
+
+-(void) schedule:(SEL)selector interval:(ccTime)interval
+{
+       NSAssert( selector != nil, @"Argument must be non-nil");
+       NSAssert( interval >=0, @"Arguemnt must be positive");
+       
+       [[CCScheduler sharedScheduler] scheduleSelector:selector forTarget:self interval:interval paused:!isRunning_];
+}
+
+-(void) unschedule:(SEL)selector
+{
+       // explicit nil handling
+       if (selector == nil)
+               return;
+       
+       [[CCScheduler sharedScheduler] unscheduleSelector:selector forTarget:self];
+}
+
+-(void) unscheduleAllSelectors
+{
+       [[CCScheduler sharedScheduler] unscheduleAllSelectorsForTarget:self];
+}
+- (void) resumeSchedulerAndActions
+{
+       [[CCScheduler sharedScheduler] resumeTarget:self];
+       [[CCActionManager sharedManager] resumeTarget:self];
+}
+
+- (void) pauseSchedulerAndActions
+{
+       [[CCScheduler sharedScheduler] pauseTarget:self];
+       [[CCActionManager sharedManager] pauseTarget:self];
+}
+
+#pragma mark CCNode Transform
+
+- (CGAffineTransform)nodeToParentTransform
+{
+       if ( isTransformDirty_ ) {
+               
+               transform_ = CGAffineTransformIdentity;
+               
+               if ( !isRelativeAnchorPoint_ && !CGPointEqualToPoint(anchorPointInPixels_, CGPointZero) )
+                       transform_ = CGAffineTransformTranslate(transform_, anchorPointInPixels_.x, anchorPointInPixels_.y);
+
+               if( ! CGPointEqualToPoint(positionInPixels_, CGPointZero) )
+                       transform_ = CGAffineTransformTranslate(transform_, positionInPixels_.x, positionInPixels_.y);
+               
+               if( rotation_ != 0 )
+                       transform_ = CGAffineTransformRotate(transform_, -CC_DEGREES_TO_RADIANS(rotation_));
+               
+               if( skewX_ != 0 || skewY_ != 0 ) {
+                       // create a skewed coordinate system
+                       CGAffineTransform skew = CGAffineTransformMake(1.0f, tanf(CC_DEGREES_TO_RADIANS(skewY_)), tanf(CC_DEGREES_TO_RADIANS(skewX_)), 1.0f, 0.0f, 0.0f);
+                       // apply the skew to the transform
+                       transform_ = CGAffineTransformConcat(skew, transform_);
+               }
+               
+               if( ! (scaleX_ == 1 && scaleY_ == 1) ) 
+                       transform_ = CGAffineTransformScale(transform_, scaleX_, scaleY_);
+               
+               if( ! CGPointEqualToPoint(anchorPointInPixels_, CGPointZero) )
+                       transform_ = CGAffineTransformTranslate(transform_, -anchorPointInPixels_.x, -anchorPointInPixels_.y);
+                               
+               isTransformDirty_ = NO;
+       }
+       
+       return transform_;
+}
+
+- (CGAffineTransform)parentToNodeTransform
+{
+       if ( isInverseDirty_ ) {
+               inverse_ = CGAffineTransformInvert([self nodeToParentTransform]);
+               isInverseDirty_ = NO;
+       }
+       
+       return inverse_;
+}
+
+- (CGAffineTransform)nodeToWorldTransform
+{
+       CGAffineTransform t = [self nodeToParentTransform];
+       
+       for (CCNode *p = parent_; p != nil; p = p.parent)
+               t = CGAffineTransformConcat(t, [p nodeToParentTransform]);
+       
+       return t;
+}
+
+- (CGAffineTransform)worldToNodeTransform
+{
+       return CGAffineTransformInvert([self nodeToWorldTransform]);
+}
+
+- (CGPoint)convertToNodeSpace:(CGPoint)worldPoint
+{
+       CGPoint ret;
+       if( CC_CONTENT_SCALE_FACTOR() == 1 )
+               ret = CGPointApplyAffineTransform(worldPoint, [self worldToNodeTransform]);
+       else {
+               ret = ccpMult( worldPoint, CC_CONTENT_SCALE_FACTOR() );
+               ret = CGPointApplyAffineTransform(ret, [self worldToNodeTransform]);
+               ret = ccpMult( ret, 1/CC_CONTENT_SCALE_FACTOR() );
+       }
+       
+       return ret;
+}
+
+- (CGPoint)convertToWorldSpace:(CGPoint)nodePoint
+{
+       CGPoint ret;
+       if( CC_CONTENT_SCALE_FACTOR() == 1 )
+               ret = CGPointApplyAffineTransform(nodePoint, [self nodeToWorldTransform]);
+       else {
+               ret = ccpMult( nodePoint, CC_CONTENT_SCALE_FACTOR() );
+               ret = CGPointApplyAffineTransform(ret, [self nodeToWorldTransform]);
+               ret = ccpMult( ret, 1/CC_CONTENT_SCALE_FACTOR() );
+       }
+       
+       return ret;
+}
+
+- (CGPoint)convertToNodeSpaceAR:(CGPoint)worldPoint
+{
+       CGPoint nodePoint = [self convertToNodeSpace:worldPoint];
+       CGPoint anchorInPoints;
+       if( CC_CONTENT_SCALE_FACTOR() == 1 )
+               anchorInPoints = anchorPointInPixels_;
+       else
+               anchorInPoints = ccpMult( anchorPointInPixels_, 1/CC_CONTENT_SCALE_FACTOR() );
+          
+       return ccpSub(nodePoint, anchorInPoints);
+}
+
+- (CGPoint)convertToWorldSpaceAR:(CGPoint)nodePoint
+{
+       CGPoint anchorInPoints;
+       if( CC_CONTENT_SCALE_FACTOR() == 1 )
+               anchorInPoints = anchorPointInPixels_;
+       else
+               anchorInPoints = ccpMult( anchorPointInPixels_, 1/CC_CONTENT_SCALE_FACTOR() );
+       
+       nodePoint = ccpAdd(nodePoint, anchorInPoints);
+       return [self convertToWorldSpace:nodePoint];
+}
+
+- (CGPoint)convertToWindowSpace:(CGPoint)nodePoint
+{
+    CGPoint worldPoint = [self convertToWorldSpace:nodePoint];
+       return [[CCDirector sharedDirector] convertToUI:worldPoint];
+}
+
+// convenience methods which take a UITouch instead of CGPoint
+
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+
+- (CGPoint)convertTouchToNodeSpace:(UITouch *)touch
+{
+       CGPoint point = [touch locationInView: [touch view]];
+       point = [[CCDirector sharedDirector] convertToGL: point];
+       return [self convertToNodeSpace:point];
+}
+
+- (CGPoint)convertTouchToNodeSpaceAR:(UITouch *)touch
+{
+       CGPoint point = [touch locationInView: [touch view]];
+       point = [[CCDirector sharedDirector] convertToGL: point];
+       return [self convertToNodeSpaceAR:point];
+}
+
+#endif // __IPHONE_OS_VERSION_MAX_ALLOWED
+
+
+@end
diff --git a/libs/cocos2d/CCParallaxNode.h b/libs/cocos2d/CCParallaxNode.h
new file mode 100644 (file)
index 0000000..5728eb1
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2009-2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+#import "CCNode.h"
+#import "Support/ccCArray.h"
+
+/** CCParallaxNode: A node that simulates a parallax scroller
+ The children will be moved faster / slower than the parent according the the parallax ratio.
+ */
+@interface CCParallaxNode : CCNode
+{
+       ccArray                         *parallaxArray_;
+       CGPoint                         lastPosition;
+}
+
+/** array that holds the offset / ratio of the children */
+@property (nonatomic,readwrite) ccArray * parallaxArray;
+
+/** Adds a child to the container with a z-order, a parallax ratio and a position offset
+ It returns self, so you can chain several addChilds.
+ @since v0.8
+ */
+-(void) addChild: (CCNode*)node z:(NSInteger)z parallaxRatio:(CGPoint)c positionOffset:(CGPoint)positionOffset;
+
+@end
diff --git a/libs/cocos2d/CCParallaxNode.m b/libs/cocos2d/CCParallaxNode.m
new file mode 100644 (file)
index 0000000..9d39cc8
--- /dev/null
@@ -0,0 +1,161 @@
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2009-2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+#import "CCParallaxNode.h"
+#import "Support/CGPointExtension.h"
+#import "Support/ccCArray.h"
+
+@interface CGPointObject : NSObject
+{
+       CGPoint ratio_;
+       CGPoint offset_;
+       CCNode *child_; // weak ref
+}
+@property (readwrite) CGPoint ratio;
+@property (readwrite) CGPoint offset;
+@property (readwrite,assign) CCNode *child;
++(id) pointWithCGPoint:(CGPoint)point offset:(CGPoint)offset;
+-(id) initWithCGPoint:(CGPoint)point offset:(CGPoint)offset;
+@end
+@implementation CGPointObject
+@synthesize ratio = ratio_;
+@synthesize offset = offset_;
+@synthesize child=child_;
+
++(id) pointWithCGPoint:(CGPoint)ratio offset:(CGPoint)offset
+{
+       return [[[self alloc] initWithCGPoint:ratio offset:offset] autorelease];
+}
+-(id) initWithCGPoint:(CGPoint)ratio offset:(CGPoint)offset
+{
+       if( (self=[super init])) {
+               ratio_ = ratio;
+               offset_ = offset;
+       }
+       return self;
+}
+@end
+
+@implementation CCParallaxNode
+
+@synthesize parallaxArray = parallaxArray_;
+
+-(id) init
+{
+       if( (self=[super init]) ) {
+               parallaxArray_ = ccArrayNew(5);         
+               lastPosition = CGPointMake(-100,-100);
+       }
+       return self;
+}
+
+- (void) dealloc
+{
+       if( parallaxArray_ ) {
+               ccArrayFree(parallaxArray_);
+               parallaxArray_ = nil;
+       }
+       [super dealloc];
+}
+
+-(void) addChild:(CCNode*)child z:(NSInteger)z tag:(NSInteger)tag
+{
+       NSAssert(NO,@"ParallaxNode: use addChild:z:parallaxRatio:positionOffset instead");
+}
+
+-(void) addChild: (CCNode*) child z:(NSInteger)z parallaxRatio:(CGPoint)ratio positionOffset:(CGPoint)offset
+{
+       NSAssert( child != nil, @"Argument must be non-nil");
+       CGPointObject *obj = [CGPointObject pointWithCGPoint:ratio offset:offset];
+       obj.child = child;
+       ccArrayAppendObjectWithResize(parallaxArray_, obj);
+       
+       CGPoint pos = self.position;
+       pos.x = pos.x * ratio.x + offset.x;
+       pos.y = pos.y * ratio.y + offset.y;
+       child.position = pos;
+       
+       [super addChild: child z:z tag:child.tag];
+}
+
+-(void) removeChild:(CCNode*)node cleanup:(BOOL)cleanup
+{
+       for( unsigned int i=0;i < parallaxArray_->num;i++) {
+               CGPointObject *point = parallaxArray_->arr[i];
+               if( [point.child isEqual:node] ) {
+                       ccArrayRemoveObjectAtIndex(parallaxArray_, i);
+                       break;
+               }
+       }
+       [super removeChild:node cleanup:cleanup];
+}
+
+-(void) removeAllChildrenWithCleanup:(BOOL)cleanup
+{
+       ccArrayRemoveAllObjects(parallaxArray_);
+       [super removeAllChildrenWithCleanup:cleanup];
+}
+
+-(CGPoint) absolutePosition_
+{
+       CGPoint ret = position_;
+       
+       CCNode *cn = self;
+       
+       while (cn.parent != nil) {
+               cn = cn.parent;
+               ret = ccpAdd( ret,  cn.position );
+       }
+       
+       return ret;
+}
+
+/*
+ The positions are updated at visit because:
+   - using a timer is not guaranteed that it will called after all the positions were updated
+   - overriding "draw" will only precise if the children have a z > 0
+*/
+-(void) visit
+{
+//     CGPoint pos = position_;
+//     CGPoint pos = [self convertToWorldSpace:CGPointZero];
+       CGPoint pos = [self absolutePosition_];
+       if( ! CGPointEqualToPoint(pos, lastPosition) ) {
+               
+               for(unsigned int i=0; i < parallaxArray_->num; i++ ) {
+
+                       CGPointObject *point = parallaxArray_->arr[i];
+                       float x = -pos.x + pos.x * point.ratio.x + point.offset.x;
+                       float y = -pos.y + pos.y * point.ratio.y + point.offset.y;                      
+                       point.child.position = ccp(x,y);
+               }
+               
+               lastPosition = pos;
+       }
+       
+       [super visit];
+}
+@end
diff --git a/libs/cocos2d/CCParticleExamples.h b/libs/cocos2d/CCParticleExamples.h
new file mode 100644 (file)
index 0000000..cd382c4
--- /dev/null
@@ -0,0 +1,111 @@
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2008-2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+
+#import <Availability.h>
+
+#import "CCParticleSystemPoint.h"
+#import "CCParticleSystemQuad.h"
+
+// build each architecture with the optimal particle system
+
+// ARMv7, Mac or Simulator use "Quad" particle
+#if defined(__ARM_NEON__) || defined(__MAC_OS_X_VERSION_MAX_ALLOWED) || TARGET_IPHONE_SIMULATOR
+       #define ARCH_OPTIMAL_PARTICLE_SYSTEM CCParticleSystemQuad
+
+// ARMv6 use "Point" particle
+#elif __arm__
+       #define ARCH_OPTIMAL_PARTICLE_SYSTEM CCParticleSystemPoint
+#else
+       #error(unknown architecture)
+#endif
+
+
+//! A fire particle system
+@interface CCParticleFire: ARCH_OPTIMAL_PARTICLE_SYSTEM
+{
+}
+@end
+
+//! A fireworks particle system
+@interface CCParticleFireworks : ARCH_OPTIMAL_PARTICLE_SYSTEM
+{
+}
+@end
+
+//! A sun particle system
+@interface CCParticleSun : ARCH_OPTIMAL_PARTICLE_SYSTEM
+{
+}
+@end
+
+//! A galaxy particle system
+@interface CCParticleGalaxy : ARCH_OPTIMAL_PARTICLE_SYSTEM
+{
+}
+@end
+
+//! A flower particle system
+@interface CCParticleFlower : ARCH_OPTIMAL_PARTICLE_SYSTEM
+{
+}
+@end
+
+//! A meteor particle system
+@interface CCParticleMeteor : ARCH_OPTIMAL_PARTICLE_SYSTEM
+{
+}
+@end
+
+//! An spiral particle system
+@interface CCParticleSpiral : ARCH_OPTIMAL_PARTICLE_SYSTEM
+{
+}
+@end
+
+//! An explosion particle system
+@interface CCParticleExplosion : ARCH_OPTIMAL_PARTICLE_SYSTEM
+{
+}
+@end
+
+//! An smoke particle system
+@interface CCParticleSmoke : ARCH_OPTIMAL_PARTICLE_SYSTEM
+{
+}
+@end
+
+//! An snow particle system
+@interface CCParticleSnow : ARCH_OPTIMAL_PARTICLE_SYSTEM
+{
+}
+@end
+
+//! A rain particle system
+@interface CCParticleRain : ARCH_OPTIMAL_PARTICLE_SYSTEM
+{
+}
+@end
diff --git a/libs/cocos2d/CCParticleExamples.m b/libs/cocos2d/CCParticleExamples.m
new file mode 100644 (file)
index 0000000..38c8b46
--- /dev/null
@@ -0,0 +1,926 @@
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2008-2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+
+// cocos2d
+#import "CCParticleExamples.h"
+#import "CCTextureCache.h"
+#import "CCDirector.h"
+#import "Support/CGPointExtension.h"
+
+//
+// ParticleFireworks
+//
+@implementation CCParticleFireworks
+-(id) init
+{
+       return [self initWithTotalParticles:1500];
+}
+
+-(id) initWithTotalParticles:(NSUInteger)p
+{
+       if( (self=[super initWithTotalParticles:p]) ) {
+               // duration
+               duration = kCCParticleDurationInfinity;
+
+               // Gravity Mode
+               self.emitterMode = kCCParticleModeGravity;
+
+               // Gravity Mode: gravity
+               self.gravity = ccp(0,-90);
+               
+               // Gravity Mode:  radial
+               self.radialAccel = 0;
+               self.radialAccelVar = 0;
+
+               //  Gravity Mode: speed of particles
+               self.speed = 180;
+               self.speedVar = 50;
+               
+               // emitter position
+               CGSize winSize = [[CCDirector sharedDirector] winSize];
+               self.position = ccp(winSize.width/2, winSize.height/2);
+               
+               // angle
+               angle = 90;
+               angleVar = 20;
+                               
+               // life of particles
+               life = 3.5f;
+               lifeVar = 1;
+                       
+               // emits per frame
+               emissionRate = totalParticles/life;
+               
+               // color of particles
+               startColor.r = 0.5f;
+               startColor.g = 0.5f;
+               startColor.b = 0.5f;
+               startColor.a = 1.0f;
+               startColorVar.r = 0.5f;
+               startColorVar.g = 0.5f;
+               startColorVar.b = 0.5f;
+               startColorVar.a = 0.1f;
+               endColor.r = 0.1f;
+               endColor.g = 0.1f;
+               endColor.b = 0.1f;
+               endColor.a = 0.2f;
+               endColorVar.r = 0.1f;
+               endColorVar.g = 0.1f;
+               endColorVar.b = 0.1f;
+               endColorVar.a = 0.2f;
+               
+               // size, in pixels
+               startSize = 8.0f;
+               startSizeVar = 2.0f;
+               endSize = kCCParticleStartSizeEqualToEndSize;
+
+               self.texture = [[CCTextureCache sharedTextureCache] addImage: @"fire.png"];
+
+               // additive
+               self.blendAdditive = NO;
+       }
+       
+       return self;
+}
+@end
+
+//
+// ParticleFire
+//
+@implementation CCParticleFire
+-(id) init
+{
+       return [self initWithTotalParticles:250];
+}
+
+-(id) initWithTotalParticles:(NSUInteger) p
+{
+       if( (self=[super initWithTotalParticles:p]) ) {
+
+               // duration
+               duration = kCCParticleDurationInfinity;
+
+               // Gravity Mode
+               self.emitterMode = kCCParticleModeGravity;
+
+               // Gravity Mode: gravity
+               self.gravity = ccp(0,0);
+               
+               // Gravity Mode: radial acceleration
+               self.radialAccel = 0;
+               self.radialAccelVar = 0;
+               
+               // Gravity Mode: speed of particles
+               self.speed = 60;
+               self.speedVar = 20;             
+               
+               // starting angle
+               angle = 90;
+               angleVar = 10;
+               
+               // emitter position
+               CGSize winSize = [[CCDirector sharedDirector] winSize];
+               self.position = ccp(winSize.width/2, 60);
+               posVar = ccp(40, 20);
+               
+               // life of particles
+               life = 3;
+               lifeVar = 0.25f;
+               
+                       
+               // size, in pixels
+               startSize = 54.0f;
+               startSizeVar = 10.0f;
+               endSize = kCCParticleStartSizeEqualToEndSize;
+
+               // emits per frame
+               emissionRate = totalParticles/life;
+               
+               // color of particles
+               startColor.r = 0.76f;
+               startColor.g = 0.25f;
+               startColor.b = 0.12f;
+               startColor.a = 1.0f;
+               startColorVar.r = 0.0f;
+               startColorVar.g = 0.0f;
+               startColorVar.b = 0.0f;
+               startColorVar.a = 0.0f;
+               endColor.r = 0.0f;
+               endColor.g = 0.0f;
+               endColor.b = 0.0f;
+               endColor.a = 1.0f;
+               endColorVar.r = 0.0f;
+               endColorVar.g = 0.0f;
+               endColorVar.b = 0.0f;
+               endColorVar.a = 0.0f;
+               
+               self.texture = [[CCTextureCache sharedTextureCache] addImage: @"fire.png"];
+               
+               // additive
+               self.blendAdditive = YES;
+       }
+               
+       return self;
+}
+@end
+
+//
+// ParticleSun
+//
+@implementation CCParticleSun
+-(id) init
+{
+       return [self initWithTotalParticles:350];
+}
+
+-(id) initWithTotalParticles:(NSUInteger) p
+{
+       if( (self=[super initWithTotalParticles:p]) ) {
+
+               // additive
+               self.blendAdditive = YES;
+                       
+               // duration
+               duration = kCCParticleDurationInfinity;
+               
+               // Gravity Mode
+               self.emitterMode = kCCParticleModeGravity;
+               
+               // Gravity Mode: gravity
+               self.gravity = ccp(0,0);
+               
+               // Gravity mode: radial acceleration
+               self.radialAccel = 0;
+               self.radialAccelVar = 0;
+               
+               // Gravity mode: speed of particles
+               self.speed = 20;
+               self.speedVar = 5;
+                               
+               
+               // angle
+               angle = 90;
+               angleVar = 360;
+               
+               // emitter position
+               CGSize winSize = [[CCDirector sharedDirector] winSize];
+               self.position = ccp(winSize.width/2, winSize.height/2);
+               posVar = CGPointZero;
+               
+               // life of particles
+               life = 1;
+               lifeVar = 0.5f;
+               
+               // size, in pixels
+               startSize = 30.0f;
+               startSizeVar = 10.0f;
+               endSize = kCCParticleStartSizeEqualToEndSize;
+
+               // emits per seconds
+               emissionRate = totalParticles/life;
+               
+               // color of particles
+               startColor.r = 0.76f;
+               startColor.g = 0.25f;
+               startColor.b = 0.12f;
+               startColor.a = 1.0f;
+               startColorVar.r = 0.0f;
+               startColorVar.g = 0.0f;
+               startColorVar.b = 0.0f;
+               startColorVar.a = 0.0f;
+               endColor.r = 0.0f;
+               endColor.g = 0.0f;
+               endColor.b = 0.0f;
+               endColor.a = 1.0f;
+               endColorVar.r = 0.0f;
+               endColorVar.g = 0.0f;
+               endColorVar.b = 0.0f;
+               endColorVar.a = 0.0f;
+               
+               self.texture = [[CCTextureCache sharedTextureCache] addImage: @"fire.png"];
+       }
+               
+       return self;
+}
+@end
+
+//
+// ParticleGalaxy
+//
+@implementation CCParticleGalaxy
+-(id) init
+{
+       return [self initWithTotalParticles:200];
+}
+
+-(id) initWithTotalParticles:(NSUInteger)p
+{
+       if( (self=[super initWithTotalParticles:p]) ) {
+
+               // duration
+               duration = kCCParticleDurationInfinity;
+
+               // Gravity Mode
+               self.emitterMode = kCCParticleModeGravity;
+
+               // Gravity Mode: gravity
+               self.gravity = ccp(0,0);
+               
+               // Gravity Mode: speed of particles
+               self.speed = 60;
+               self.speedVar = 10;
+                       
+               // Gravity Mode: radial
+               self.radialAccel = -80;
+               self.radialAccelVar = 0;
+               
+               // Gravity Mode: tagential
+               self.tangentialAccel = 80;
+               self.tangentialAccelVar = 0;
+               
+               // angle
+               angle = 90;
+               angleVar = 360;
+               
+               // emitter position
+               CGSize winSize = [[CCDirector sharedDirector] winSize];
+               self.position = ccp(winSize.width/2, winSize.height/2);
+               posVar = CGPointZero;
+               
+               // life of particles
+               life = 4;
+               lifeVar = 1;
+               
+               // size, in pixels
+               startSize = 37.0f;
+               startSizeVar = 10.0f;
+               endSize = kCCParticleStartSizeEqualToEndSize;
+               
+               // emits per second
+               emissionRate = totalParticles/life;
+               
+               // color of particles
+               startColor.r = 0.12f;
+               startColor.g = 0.25f;
+               startColor.b = 0.76f;
+               startColor.a = 1.0f;
+               startColorVar.r = 0.0f;
+               startColorVar.g = 0.0f;
+               startColorVar.b = 0.0f;
+               startColorVar.a = 0.0f;
+               endColor.r = 0.0f;
+               endColor.g = 0.0f;
+               endColor.b = 0.0f;
+               endColor.a = 1.0f;
+               endColorVar.r = 0.0f;
+               endColorVar.g = 0.0f;
+               endColorVar.b = 0.0f;
+               endColorVar.a = 0.0f;
+               
+               self.texture = [[CCTextureCache sharedTextureCache] addImage: @"fire.png"];
+
+               // additive
+               self.blendAdditive = YES;
+       }
+       
+       return self;
+}
+@end
+
+//
+// ParticleFlower
+//
+@implementation CCParticleFlower
+-(id) init
+{
+       return [self initWithTotalParticles:250];
+}
+
+-(id) initWithTotalParticles:(NSUInteger) p
+{
+       if( (self=[super initWithTotalParticles:p]) ) {
+       
+               // duration
+               duration = kCCParticleDurationInfinity;
+
+               // Gravity Mode
+               self.emitterMode = kCCParticleModeGravity;
+
+               // Gravity Mode: gravity
+               self.gravity = ccp(0,0);
+               
+               // Gravity Mode: speed of particles
+               self.speed = 80;
+               self.speedVar = 10;
+               
+               // Gravity Mode: radial
+               self.radialAccel = -60;
+               self.radialAccelVar = 0;
+               
+               // Gravity Mode: tagential
+               self.tangentialAccel = 15;
+               self.tangentialAccelVar = 0;
+
+               // angle
+               angle = 90;
+               angleVar = 360;
+               
+               // emitter position
+               CGSize winSize = [[CCDirector sharedDirector] winSize];
+               self.position = ccp(winSize.width/2, winSize.height/2);
+               posVar = CGPointZero;
+               
+               // life of particles
+               life = 4;
+               lifeVar = 1;
+               
+               // size, in pixels
+               startSize = 30.0f;
+               startSizeVar = 10.0f;
+               endSize = kCCParticleStartSizeEqualToEndSize;
+
+               // emits per second
+               emissionRate = totalParticles/life;
+               
+               // color of particles
+               startColor.r = 0.50f;
+               startColor.g = 0.50f;
+               startColor.b = 0.50f;
+               startColor.a = 1.0f;
+               startColorVar.r = 0.5f;
+               startColorVar.g = 0.5f;
+               startColorVar.b = 0.5f;
+               startColorVar.a = 0.5f;
+               endColor.r = 0.0f;
+               endColor.g = 0.0f;
+               endColor.b = 0.0f;
+               endColor.a = 1.0f;
+               endColorVar.r = 0.0f;
+               endColorVar.g = 0.0f;
+               endColorVar.b = 0.0f;
+               endColorVar.a = 0.0f;
+               
+               self.texture = [[CCTextureCache sharedTextureCache] addImage: @"fire.png"];
+
+               // additive
+               self.blendAdditive = YES;
+       }
+               
+       return self;
+}
+@end
+
+//
+// ParticleMeteor
+//
+@implementation CCParticleMeteor
+-(id) init
+{
+       return [self initWithTotalParticles:150];
+}
+
+-(id) initWithTotalParticles:(NSUInteger) p
+{
+       if( (self=[super initWithTotalParticles:p]) ) {
+
+               // duration
+               duration = kCCParticleDurationInfinity;
+               
+               // Gravity Mode
+               self.emitterMode = kCCParticleModeGravity;
+
+               // Gravity Mode: gravity
+               self.gravity = ccp(-200,200);
+
+               // Gravity Mode: speed of particles
+               self.speed = 15;
+               self.speedVar = 5;
+               
+               // Gravity Mode: radial
+               self.radialAccel = 0;
+               self.radialAccelVar = 0;
+               
+               // Gravity Mode: tagential
+               self.tangentialAccel = 0;
+               self.tangentialAccelVar = 0;
+               
+               // angle
+               angle = 90;
+               angleVar = 360;
+               
+               // emitter position
+               CGSize winSize = [[CCDirector sharedDirector] winSize];
+               self.position = ccp(winSize.width/2, winSize.height/2);
+               posVar = CGPointZero;
+               
+               // life of particles
+               life = 2;
+               lifeVar = 1;
+               
+               // size, in pixels
+               startSize = 60.0f;
+               startSizeVar = 10.0f;
+               endSize = kCCParticleStartSizeEqualToEndSize;
+
+               // emits per second
+               emissionRate = totalParticles/life;
+               
+               // color of particles
+               startColor.r = 0.2f;
+               startColor.g = 0.4f;
+               startColor.b = 0.7f;
+               startColor.a = 1.0f;
+               startColorVar.r = 0.0f;
+               startColorVar.g = 0.0f;
+               startColorVar.b = 0.2f;
+               startColorVar.a = 0.1f;
+               endColor.r = 0.0f;
+               endColor.g = 0.0f;
+               endColor.b = 0.0f;
+               endColor.a = 1.0f;
+               endColorVar.r = 0.0f;
+               endColorVar.g = 0.0f;
+               endColorVar.b = 0.0f;
+               endColorVar.a = 0.0f;
+               
+               self.texture = [[CCTextureCache sharedTextureCache] addImage: @"fire.png"];
+               
+               // additive
+               self.blendAdditive = YES;
+       }
+       
+       return self;
+}
+@end
+
+//
+// ParticleSpiral
+//
+@implementation CCParticleSpiral
+-(id) init
+{
+       return [self initWithTotalParticles:500];
+}
+
+-(id) initWithTotalParticles:(NSUInteger) p
+{
+       if( (self=[super initWithTotalParticles:p]) ) {
+       
+               // duration
+               duration = kCCParticleDurationInfinity;
+
+               // Gravity Mode
+               self.emitterMode = kCCParticleModeGravity;
+               
+               // Gravity Mode: gravity
+               self.gravity = ccp(0,0);
+               
+               // Gravity Mode: speed of particles
+               self.speed = 150;
+               self.speedVar = 0;
+               
+               // Gravity Mode: radial
+               self.radialAccel = -380;
+               self.radialAccelVar = 0;
+               
+               // Gravity Mode: tagential
+               self.tangentialAccel = 45;
+               self.tangentialAccelVar = 0;
+               
+               // angle
+               angle = 90;
+               angleVar = 0;
+               
+               // emitter position
+               CGSize winSize = [[CCDirector sharedDirector] winSize];
+               self.position = ccp(winSize.width/2, winSize.height/2);
+               posVar = CGPointZero;
+               
+               // life of particles
+               life = 12;
+               lifeVar = 0;
+               
+               // size, in pixels
+               startSize = 20.0f;
+               startSizeVar = 0.0f;
+               endSize = kCCParticleStartSizeEqualToEndSize;
+
+               // emits per second
+               emissionRate = totalParticles/life;
+               
+               // color of particles
+               startColor.r = 0.5f;
+               startColor.g = 0.5f;
+               startColor.b = 0.5f;
+               startColor.a = 1.0f;
+               startColorVar.r = 0.5f;
+               startColorVar.g = 0.5f;
+               startColorVar.b = 0.5f;
+               startColorVar.a = 0.0f;
+               endColor.r = 0.5f;
+               endColor.g = 0.5f;
+               endColor.b = 0.5f;
+               endColor.a = 1.0f;
+               endColorVar.r = 0.5f;
+               endColorVar.g = 0.5f;
+               endColorVar.b = 0.5f;
+               endColorVar.a = 0.0f;
+               
+               self.texture = [[CCTextureCache sharedTextureCache] addImage: @"fire.png"];
+
+               // additive
+               self.blendAdditive = NO;
+       }
+       
+       return self;
+}
+@end
+
+//
+// ParticleExplosion
+//
+@implementation CCParticleExplosion
+-(id) init
+{
+       return [self initWithTotalParticles:700];
+}
+
+-(id) initWithTotalParticles:(NSUInteger)p
+{
+       if( (self=[super initWithTotalParticles:p]) ) {
+       
+               // duration
+               duration = 0.1f;
+               
+               self.emitterMode = kCCParticleModeGravity;
+
+               // Gravity Mode: gravity
+               self.gravity = ccp(0,0);
+               
+               // Gravity Mode: speed of particles
+               self.speed = 70;
+               self.speedVar = 40;
+               
+               // Gravity Mode: radial
+               self.radialAccel = 0;
+               self.radialAccelVar = 0;
+               
+               // Gravity Mode: tagential
+               self.tangentialAccel = 0;
+               self.tangentialAccelVar = 0;
+               
+               // angle
+               angle = 90;
+               angleVar = 360;
+                               
+               // emitter position
+               CGSize winSize = [[CCDirector sharedDirector] winSize];
+               self.position = ccp(winSize.width/2, winSize.height/2);
+               posVar = CGPointZero;
+               
+               // life of particles
+               life = 5.0f;
+               lifeVar = 2;
+               
+               // size, in pixels
+               startSize = 15.0f;
+               startSizeVar = 10.0f;
+               endSize = kCCParticleStartSizeEqualToEndSize;
+
+               // emits per second
+               emissionRate = totalParticles/duration;
+               
+               // color of particles
+               startColor.r = 0.7f;
+               startColor.g = 0.1f;
+               startColor.b = 0.2f;
+               startColor.a = 1.0f;
+               startColorVar.r = 0.5f;
+               startColorVar.g = 0.5f;
+               startColorVar.b = 0.5f;
+               startColorVar.a = 0.0f;
+               endColor.r = 0.5f;
+               endColor.g = 0.5f;
+               endColor.b = 0.5f;
+               endColor.a = 0.0f;
+               endColorVar.r = 0.5f;
+               endColorVar.g = 0.5f;
+               endColorVar.b = 0.5f;
+               endColorVar.a = 0.0f;
+               
+               self.texture = [[CCTextureCache sharedTextureCache] addImage: @"fire.png"];
+
+               // additive
+               self.blendAdditive = NO;
+       }
+       
+       return self;
+}
+@end
+
+//
+// ParticleSmoke
+//
+@implementation CCParticleSmoke
+-(id) init
+{
+       return [self initWithTotalParticles:200];
+}
+
+-(id) initWithTotalParticles:(NSUInteger) p
+{
+       if( (self=[super initWithTotalParticles:p]) ) {
+       
+               // duration
+               duration = kCCParticleDurationInfinity;
+               
+               // Emitter mode: Gravity Mode
+               self.emitterMode = kCCParticleModeGravity;
+               
+               // Gravity Mode: gravity
+               self.gravity = ccp(0,0);
+
+               // Gravity Mode: radial acceleration
+               self.radialAccel = 0;
+               self.radialAccelVar = 0;
+               
+               // Gravity Mode: speed of particles
+               self.speed = 25;
+               self.speedVar = 10;
+               
+               // angle
+               angle = 90;
+               angleVar = 5;
+               
+               // emitter position
+               CGSize winSize = [[CCDirector sharedDirector] winSize];
+               self.position = ccp(winSize.width/2, 0);
+               posVar = ccp(20, 0);
+               
+               // life of particles
+               life = 4;
+               lifeVar = 1;
+               
+               // size, in pixels
+               startSize = 60.0f;
+               startSizeVar = 10.0f;
+               endSize = kCCParticleStartSizeEqualToEndSize;
+
+               // emits per frame
+               emissionRate = totalParticles/life;
+               
+               // color of particles
+               startColor.r = 0.8f;
+               startColor.g = 0.8f;
+               startColor.b = 0.8f;
+               startColor.a = 1.0f;
+               startColorVar.r = 0.02f;
+               startColorVar.g = 0.02f;
+               startColorVar.b = 0.02f;
+               startColorVar.a = 0.0f;
+               endColor.r = 0.0f;
+               endColor.g = 0.0f;
+               endColor.b = 0.0f;
+               endColor.a = 1.0f;
+               endColorVar.r = 0.0f;
+               endColorVar.g = 0.0f;
+               endColorVar.b = 0.0f;
+               endColorVar.a = 0.0f;
+               
+               self.texture = [[CCTextureCache sharedTextureCache] addImage: @"fire.png"];
+               
+               // additive
+               self.blendAdditive = NO;
+       }
+       
+       return self;
+}
+@end
+
+@implementation CCParticleSnow
+-(id) init
+{
+       return [self initWithTotalParticles:700];
+}
+
+-(id) initWithTotalParticles:(NSUInteger)p
+{
+       if( (self=[super initWithTotalParticles:p]) ) {
+       
+               // duration
+               duration = kCCParticleDurationInfinity;
+               
+               // set gravity mode.
+               self.emitterMode = kCCParticleModeGravity;
+
+               // Gravity Mode: gravity
+               self.gravity = ccp(0,-1);
+               
+               // Gravity Mode: speed of particles
+               self.speed = 5;
+               self.speedVar = 1;
+               
+               // Gravity Mode: radial
+               self.radialAccel = 0;
+               self.radialAccelVar = 1;
+               
+               // Gravity mode: tagential
+               self.tangentialAccel = 0;
+               self.tangentialAccelVar = 1;
+               
+               // emitter position
+               self.position = (CGPoint) {
+                       [[CCDirector sharedDirector] winSize].width / 2,
+                       [[CCDirector sharedDirector] winSize].height + 10
+               };
+               posVar = ccp( [[CCDirector sharedDirector] winSize].width / 2, 0 );
+               
+               // angle
+               angle = -90;
+               angleVar = 5;
+
+               // life of particles
+               life = 45;
+               lifeVar = 15;
+               
+               // size, in pixels
+               startSize = 10.0f;
+               startSizeVar = 5.0f;
+               endSize = kCCParticleStartSizeEqualToEndSize;
+
+               // emits per second
+               emissionRate = 10;
+               
+               // color of particles
+               startColor.r = 1.0f;
+               startColor.g = 1.0f;
+               startColor.b = 1.0f;
+               startColor.a = 1.0f;
+               startColorVar.r = 0.0f;
+               startColorVar.g = 0.0f;
+               startColorVar.b = 0.0f;
+               startColorVar.a = 0.0f;
+               endColor.r = 1.0f;
+               endColor.g = 1.0f;
+               endColor.b = 1.0f;
+               endColor.a = 0.0f;
+               endColorVar.r = 0.0f;
+               endColorVar.g = 0.0f;
+               endColorVar.b = 0.0f;
+               endColorVar.a = 0.0f;
+               
+               self.texture = [[CCTextureCache sharedTextureCache] addImage: @"fire.png"];
+               
+               // additive
+               self.blendAdditive = NO;
+       }
+               
+       return self;
+}
+@end
+
+@implementation CCParticleRain
+-(id) init
+{
+       return [self initWithTotalParticles:1000];
+}
+
+-(id) initWithTotalParticles:(NSUInteger)p
+{
+       if( (self=[super initWithTotalParticles:p]) ) {
+       
+               // duration
+               duration = kCCParticleDurationInfinity;
+               
+               self.emitterMode = kCCParticleModeGravity;
+
+               // Gravity Mode: gravity
+               self.gravity = ccp(10,-10);
+               
+               // Gravity Mode: radial
+               self.radialAccel = 0;
+               self.radialAccelVar = 1;
+               
+               // Gravity Mode: tagential
+               self.tangentialAccel = 0;
+               self.tangentialAccelVar = 1;
+
+               // Gravity Mode: speed of particles
+               self.speed = 130;
+               self.speedVar = 30;
+               
+               // angle
+               angle = -90;
+               angleVar = 5;
+               
+               
+               // emitter position
+               self.position = (CGPoint) {
+                       [[CCDirector sharedDirector] winSize].width / 2,
+                       [[CCDirector sharedDirector] winSize].height
+               };
+               posVar = ccp( [[CCDirector sharedDirector] winSize].width / 2, 0 );
+               
+               // life of particles
+               life = 4.5f;
+               lifeVar = 0;
+               
+               // size, in pixels
+               startSize = 4.0f;
+               startSizeVar = 2.0f;
+               endSize = kCCParticleStartSizeEqualToEndSize;
+
+               // emits per second
+               emissionRate = 20;
+               
+               // color of particles
+               startColor.r = 0.7f;
+               startColor.g = 0.8f;
+               startColor.b = 1.0f;
+               startColor.a = 1.0f;
+               startColorVar.r = 0.0f;
+               startColorVar.g = 0.0f;
+               startColorVar.b = 0.0f;
+               startColorVar.a = 0.0f;
+               endColor.r = 0.7f;
+               endColor.g = 0.8f;
+               endColor.b = 1.0f;
+               endColor.a = 0.5f;
+               endColorVar.r = 0.0f;
+               endColorVar.g = 0.0f;
+               endColorVar.b = 0.0f;
+               endColorVar.a = 0.0f;
+               
+               self.texture = [[CCTextureCache sharedTextureCache] addImage: @"fire.png"];
+               
+               // additive
+               self.blendAdditive = NO;
+       }
+       
+       return self;
+}
+@end
diff --git a/libs/cocos2d/CCParticleSystem.h b/libs/cocos2d/CCParticleSystem.h
new file mode 100644 (file)
index 0000000..429e814
--- /dev/null
@@ -0,0 +1,445 @@
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2008-2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+
+#import "CCProtocols.h"
+#import "CCNode.h"
+#import "ccTypes.h"
+#import "ccConfig.h"
+
+#if CC_ENABLE_PROFILERS
+@class CCProfilingTimer;
+#endif
+
+//* @enum
+enum {
+       /** The Particle emitter lives forever */
+       kCCParticleDurationInfinity = -1,
+
+       /** The starting size of the particle is equal to the ending size */
+       kCCParticleStartSizeEqualToEndSize = -1,
+       
+       /** The starting radius of the particle is equal to the ending radius */
+       kCCParticleStartRadiusEqualToEndRadius = -1,
+
+       // backward compatible
+       kParticleStartSizeEqualToEndSize = kCCParticleStartSizeEqualToEndSize,
+       kParticleDurationInfinity = kCCParticleDurationInfinity,
+};
+
+//* @enum
+enum {
+       /** Gravity mode (A mode) */
+       kCCParticleModeGravity,
+       
+       /** Radius mode (B mode) */
+       kCCParticleModeRadius,  
+};
+
+
+/** @typedef tCCPositionType
+ possible types of particle positions
+ */
+typedef enum {
+       /** Living particles are attached to the world and are unaffected by emitter repositioning. */
+       kCCPositionTypeFree,
+
+       /** Living particles are attached to the world but will follow the emitter repositioning.
+        Use case: Attach an emitter to an sprite, and you want that the emitter follows the sprite.
+        */
+       kCCPositionTypeRelative,
+       
+       /** Living particles are attached to the emitter and are translated along with it. */
+       kCCPositionTypeGrouped,
+}tCCPositionType;
+
+// backward compatible
+enum {
+       kPositionTypeFree = kCCPositionTypeFree,
+       kPositionTypeGrouped = kCCPositionTypeGrouped,
+}; 
+
+/** @struct tCCParticle
+ Structure that contains the values of each particle
+ */
+typedef struct sCCParticle {
+       CGPoint         pos;
+       CGPoint         startPos;
+
+       ccColor4F       color;
+       ccColor4F       deltaColor;
+
+       float           size;
+       float           deltaSize;
+
+       float           rotation;
+       float           deltaRotation;
+
+       ccTime          timeToLive;
+
+       union {
+               // Mode A: gravity, direction, radial accel, tangential accel
+               struct {
+                       CGPoint         dir;
+                       float           radialAccel;
+                       float           tangentialAccel;
+               } A;
+       
+               // Mode B: radius mode
+               struct {
+                       float           angle;
+                       float           degreesPerSecond;
+                       float           radius;
+                       float           deltaRadius;
+               } B;
+       } mode;
+
+}tCCParticle;
+
+typedef void (*CC_UPDATE_PARTICLE_IMP)(id, SEL, tCCParticle*, CGPoint);
+
+@class CCTexture2D;
+
+/** Particle System base class
+ Attributes of a Particle System:
+       - emmision rate of the particles
+       - Gravity Mode (Mode A):
+               - gravity
+               - direction
+               - speed +-  variance
+               - tangential acceleration +- variance
+               - radial acceleration +- variance
+       - Radius Mode (Mode B):
+               - startRadius +- variance
+               - endRadius +- variance
+               - rotate +- variance
+       - Properties common to all modes:
+               - life +- life variance
+               - start spin +- variance
+               - end spin +- variance
+               - start size +- variance
+               - end size +- variance
+               - start color +- variance
+               - end color +- variance
+               - life +- variance
+               - blending function
+       - texture
+
+ cocos2d also supports particles generated by Particle Designer (http://particledesigner.71squared.com/).
+ 'Radius Mode' in Particle Designer uses a fixed emit rate of 30 hz. Since that can't be guarateed in cocos2d,
+ cocos2d uses a another approach, but the results are almost identical. 
+ cocos2d supports all the variables used by Particle Designer plus a bit more:
+       - spinning particles (supported when using CCParticleSystemQuad)
+       - tangential acceleration (Gravity mode)
+       - radial acceleration (Gravity mode)
+       - radius direction (Radius mode) (Particle Designer supports outwards to inwards direction only)
+ It is possible to customize any of the above mentioned properties in runtime. Example:
+ @code
+       emitter.radialAccel = 15;
+       emitter.startSpin = 0;
+ @endcode
+ */
+@interface CCParticleSystem : CCNode <CCTextureProtocol>
+{      
+       // is the particle system active ?
+       BOOL active;
+       // duration in seconds of the system. -1 is infinity
+       float duration;
+       // time elapsed since the start of the system (in seconds)
+       float elapsed;
+       
+       // position is from "superclass" CocosNode
+       CGPoint sourcePosition;
+       // Position variance
+       CGPoint posVar;
+       
+       // The angle (direction) of the particles measured in degrees
+       float angle;
+       // Angle variance measured in degrees;
+       float angleVar;
+       
+       // Different modes
+       
+       NSInteger emitterMode_;
+       union {
+               // Mode A:Gravity + Tangential Accel + Radial Accel
+               struct {
+                       // gravity of the particles
+                       CGPoint gravity;
+
+                       // The speed the particles will have.
+                       float speed;
+                       // The speed variance
+                       float speedVar;
+
+                       // Tangential acceleration
+                       float tangentialAccel;
+                       // Tangential acceleration variance
+                       float tangentialAccelVar;
+
+                       // Radial acceleration
+                       float radialAccel;
+                       // Radial acceleration variance
+                       float radialAccelVar;
+                       } A;
+
+               // Mode B: circular movement (gravity, radial accel and tangential accel don't are not used in this mode)
+               struct {
+       
+                       // The starting radius of the particles
+                       float startRadius;
+                       // The starting radius variance of the particles
+                       float startRadiusVar;
+                       // The ending radius of the particles
+                       float endRadius;
+                       // The ending radius variance of the particles
+                       float endRadiusVar;                     
+                       // Number of degress to rotate a particle around the source pos per second
+                       float rotatePerSecond;
+                       // Variance in degrees for rotatePerSecond
+                       float rotatePerSecondVar;
+               } B;
+       } mode;
+       
+       // start ize of the particles
+       float startSize;
+       // start Size variance
+       float startSizeVar;
+       // End size of the particle
+       float endSize;
+       // end size of variance
+       float endSizeVar;
+       
+       // How many seconds will the particle live
+       float life;
+       // Life variance
+       float lifeVar;
+       
+       // Start color of the particles
+       ccColor4F startColor;
+       // Start color variance
+       ccColor4F startColorVar;
+       // End color of the particles
+       ccColor4F endColor;
+       // End color variance
+       ccColor4F endColorVar;
+       
+       // start angle of the particles
+       float startSpin;
+       // start angle variance
+       float startSpinVar;
+       // End angle of the particle
+       float endSpin;
+       // end angle ariance
+       float endSpinVar;
+       
+       
+       // Array of particles
+       tCCParticle *particles;
+       // Maximum particles
+       NSUInteger totalParticles;
+       // Count of active particles
+       NSUInteger particleCount;
+       
+       // color modulate
+//     BOOL colorModulate;
+       
+       // How many particles can be emitted per second
+       float emissionRate;
+       float emitCounter;
+       
+       // Texture of the particles
+       CCTexture2D *texture_;
+       // blend function
+       ccBlendFunc     blendFunc_;
+
+       // movment type: free or grouped
+       tCCPositionType positionType_;
+
+       // Whether or not the node will be auto-removed when there are not particles
+       BOOL    autoRemoveOnFinish_;
+
+       //  particle idx
+       NSUInteger particleIdx;
+       
+       // Optimization
+       CC_UPDATE_PARTICLE_IMP  updateParticleImp;
+       SEL                                             updateParticleSel;
+       
+// profiling
+#if CC_ENABLE_PROFILERS
+       CCProfilingTimer* _profilingTimer;
+#endif
+}
+
+/** Is the emitter active */
+@property (nonatomic,readonly) BOOL active;
+/** Quantity of particles that are being simulated at the moment */
+@property (nonatomic,readonly) NSUInteger      particleCount;
+/** How many seconds the emitter wil run. -1 means 'forever' */
+@property (nonatomic,readwrite,assign) float duration;
+/** sourcePosition of the emitter */
+@property (nonatomic,readwrite,assign) CGPoint sourcePosition;
+/** Position variance of the emitter */
+@property (nonatomic,readwrite,assign) CGPoint posVar;
+/** life, and life variation of each particle */
+@property (nonatomic,readwrite,assign) float life;
+/** life variance of each particle */
+@property (nonatomic,readwrite,assign) float lifeVar;
+/** angle and angle variation of each particle */
+@property (nonatomic,readwrite,assign) float angle;
+/** angle variance of each particle */
+@property (nonatomic,readwrite,assign) float angleVar;
+
+/** Gravity value. Only available in 'Gravity' mode. */
+@property (nonatomic,readwrite,assign) CGPoint gravity;
+/** speed of each particle. Only available in 'Gravity' mode.  */
+@property (nonatomic,readwrite,assign) float speed;
+/** speed variance of each particle. Only available in 'Gravity' mode. */
+@property (nonatomic,readwrite,assign) float speedVar;
+/** tangential acceleration of each particle. Only available in 'Gravity' mode. */
+@property (nonatomic,readwrite,assign) float tangentialAccel;
+/** tangential acceleration variance of each particle. Only available in 'Gravity' mode. */
+@property (nonatomic,readwrite,assign) float tangentialAccelVar;
+/** radial acceleration of each particle. Only available in 'Gravity' mode. */
+@property (nonatomic,readwrite,assign) float radialAccel;
+/** radial acceleration variance of each particle. Only available in 'Gravity' mode. */
+@property (nonatomic,readwrite,assign) float radialAccelVar;
+
+/** The starting radius of the particles. Only available in 'Radius' mode. */
+@property (nonatomic,readwrite,assign) float startRadius;
+/** The starting radius variance of the particles. Only available in 'Radius' mode. */
+@property (nonatomic,readwrite,assign) float startRadiusVar;
+/** The ending radius of the particles. Only available in 'Radius' mode. */
+@property (nonatomic,readwrite,assign) float endRadius;
+/** The ending radius variance of the particles. Only available in 'Radius' mode. */
+@property (nonatomic,readwrite,assign) float endRadiusVar;                     
+/** Number of degress to rotate a particle around the source pos per second. Only available in 'Radius' mode. */
+@property (nonatomic,readwrite,assign) float rotatePerSecond;
+/** Variance in degrees for rotatePerSecond. Only available in 'Radius' mode. */
+@property (nonatomic,readwrite,assign) float rotatePerSecondVar;
+
+/** start size in pixels of each particle */
+@property (nonatomic,readwrite,assign) float startSize;
+/** size variance in pixels of each particle */
+@property (nonatomic,readwrite,assign) float startSizeVar;
+/** end size in pixels of each particle */
+@property (nonatomic,readwrite,assign) float endSize;
+/** end size variance in pixels of each particle */
+@property (nonatomic,readwrite,assign) float endSizeVar;
+/** start color of each particle */
+@property (nonatomic,readwrite,assign) ccColor4F startColor;
+/** start color variance of each particle */
+@property (nonatomic,readwrite,assign) ccColor4F startColorVar;
+/** end color and end color variation of each particle */
+@property (nonatomic,readwrite,assign) ccColor4F endColor;
+/** end color variance of each particle */
+@property (nonatomic,readwrite,assign) ccColor4F endColorVar;
+//* initial angle of each particle
+@property (nonatomic,readwrite,assign) float startSpin;
+//* initial angle of each particle
+@property (nonatomic,readwrite,assign) float startSpinVar;
+//* initial angle of each particle
+@property (nonatomic,readwrite,assign) float endSpin;
+//* initial angle of each particle
+@property (nonatomic,readwrite,assign) float endSpinVar;
+/** emission rate of the particles */
+@property (nonatomic,readwrite,assign) float emissionRate;
+/** maximum particles of the system */
+@property (nonatomic,readwrite,assign) NSUInteger totalParticles;
+/** conforms to CocosNodeTexture protocol */
+@property (nonatomic,readwrite, retain) CCTexture2D * texture;
+/** conforms to CocosNodeTexture protocol */
+@property (nonatomic,readwrite) ccBlendFunc blendFunc;
+/** whether or not the particles are using blend additive.
+ If enabled, the following blending function will be used.
+ @code
+       source blend function = GL_SRC_ALPHA;
+       dest blend function = GL_ONE;
+ @endcode
+ */
+@property (nonatomic,readwrite) BOOL blendAdditive;
+/** particles movement type: Free or Grouped
+ @since v0.8
+ */
+@property (nonatomic,readwrite) tCCPositionType positionType;
+/** whether or not the node will be auto-removed when it has no particles left.
+ By default it is NO.
+ @since v0.8
+ */
+@property (nonatomic,readwrite) BOOL autoRemoveOnFinish;
+/** Switch between different kind of emitter modes:
+   - kCCParticleModeGravity: uses gravity, speed, radial and tangential acceleration
+   - kCCParticleModeRadius: uses radius movement + rotation
+ */
+@property (nonatomic,readwrite) NSInteger emitterMode;
+
+/** creates an initializes a CCParticleSystem from a plist file.
+ This plist files can be creted manually or with Particle Designer:
+       http://particledesigner.71squared.com/
+ @since v0.99.3
+ */
++(id) particleWithFile:(NSString*)plistFile;
+
+/** initializes a CCParticleSystem from a plist file.
+ This plist files can be creted manually or with Particle Designer:
+       http://particledesigner.71squared.com/
+ @since v0.99.3
+ */
+-(id) initWithFile:(NSString*) plistFile;
+
+/** initializes a CCQuadParticleSystem from a NSDictionary.
+ @since v0.99.3
+ */
+-(id) initWithDictionary:(NSDictionary*)dictionary;
+
+//! Initializes a system with a fixed number of particles
+-(id) initWithTotalParticles:(NSUInteger) numberOfParticles;
+//! Add a particle to the emitter
+-(BOOL) addParticle;
+//! Initializes a particle
+-(void) initParticle: (tCCParticle*) particle;
+//! stop emitting particles. Running particles will continue to run until they die
+-(void) stopSystem;
+//! Kill all living particles.
+-(void) resetSystem;
+//! whether or not the system is full
+-(BOOL) isFull;
+
+//! should be overriden by subclasses
+-(void) updateQuadWithParticle:(tCCParticle*)particle newPosition:(CGPoint)pos;
+//! should be overriden by subclasses
+-(void) postStep;
+
+//! called in every loop.
+-(void) update: (ccTime) dt;
+
+@end
+
diff --git a/libs/cocos2d/CCParticleSystem.m b/libs/cocos2d/CCParticleSystem.m
new file mode 100644 (file)
index 0000000..742676e
--- /dev/null
@@ -0,0 +1,808 @@
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2008-2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+
+// ideas taken from:
+//      . The ocean spray in your face [Jeff Lander]
+//             http://www.double.co.nz/dust/col0798.pdf
+//      . Building an Advanced Particle System [John van der Burg]
+//             http://www.gamasutra.com/features/20000623/vanderburg_01.htm
+//   . LOVE game engine
+//             http://love2d.org/
+//
+//
+// Radius mode support, from 71 squared
+//             http://particledesigner.71squared.com/
+//
+// IMPORTANT: Particle Designer is supported by cocos2d, but
+// 'Radius Mode' in Particle Designer uses a fixed emit rate of 30 hz. Since that can't be guarateed in cocos2d,
+//  cocos2d uses a another approach, but the results are almost identical. 
+//
+
+// opengl
+#import "Platforms/CCGL.h"
+
+// cocos2d
+#import "ccConfig.h"
+#if CC_ENABLE_PROFILERS
+#import "Support/CCProfiling.h"
+#endif
+#import "CCParticleSystem.h"
+#import "CCTextureCache.h"
+#import "ccMacros.h"
+
+// support
+#import "Support/OpenGL_Internal.h"
+#import "Support/CGPointExtension.h"
+#import "Support/base64.h"
+#import "Support/ZipUtils.h"
+#import "Support/CCFileUtils.h"
+
+@implementation CCParticleSystem
+@synthesize active, duration;
+@synthesize sourcePosition, posVar;
+@synthesize particleCount;
+@synthesize life, lifeVar;
+@synthesize angle, angleVar;
+@synthesize startColor, startColorVar, endColor, endColorVar;
+@synthesize startSpin, startSpinVar, endSpin, endSpinVar;
+@synthesize emissionRate;
+@synthesize totalParticles;
+@synthesize startSize, startSizeVar;
+@synthesize endSize, endSizeVar;
+@synthesize blendFunc = blendFunc_;
+@synthesize positionType = positionType_;
+@synthesize autoRemoveOnFinish = autoRemoveOnFinish_;
+@synthesize emitterMode = emitterMode_;
+
+
++(id) particleWithFile:(NSString*) plistFile
+{
+       return [[[self alloc] initWithFile:plistFile] autorelease];
+}
+
+-(id) init {
+       NSAssert(NO, @"CCParticleSystem: Init not supported.");
+       [self release];
+       return nil;     
+}
+
+-(id) initWithFile:(NSString *)plistFile
+{
+       NSString *path = [CCFileUtils fullPathFromRelativePath:plistFile];
+       NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:path];
+       
+       NSAssert( dict != nil, @"Particles: file not found");
+       return [self initWithDictionary:dict];
+}
+
+-(id) initWithDictionary:(NSDictionary *)dictionary
+{
+       NSUInteger maxParticles = [[dictionary valueForKey:@"maxParticles"] intValue];
+       // self, not super
+       if ((self=[self initWithTotalParticles:maxParticles] ) ) {
+               
+               // angle
+               angle = [[dictionary valueForKey:@"angle"] floatValue];
+               angleVar = [[dictionary valueForKey:@"angleVariance"] floatValue];
+               
+               // duration
+               duration = [[dictionary valueForKey:@"duration"] floatValue];
+               
+               // blend function 
+               blendFunc_.src = [[dictionary valueForKey:@"blendFuncSource"] intValue];
+               blendFunc_.dst = [[dictionary valueForKey:@"blendFuncDestination"] intValue];
+               
+               // color
+               float r,g,b,a;
+               
+               r = [[dictionary valueForKey:@"startColorRed"] floatValue];
+               g = [[dictionary valueForKey:@"startColorGreen"] floatValue];
+               b = [[dictionary valueForKey:@"startColorBlue"] floatValue];
+               a = [[dictionary valueForKey:@"startColorAlpha"] floatValue];
+               startColor = (ccColor4F) {r,g,b,a};
+               
+               r = [[dictionary valueForKey:@"startColorVarianceRed"] floatValue];
+               g = [[dictionary valueForKey:@"startColorVarianceGreen"] floatValue];
+               b = [[dictionary valueForKey:@"startColorVarianceBlue"] floatValue];
+               a = [[dictionary valueForKey:@"startColorVarianceAlpha"] floatValue];
+               startColorVar = (ccColor4F) {r,g,b,a};
+               
+               r = [[dictionary valueForKey:@"finishColorRed"] floatValue];
+               g = [[dictionary valueForKey:@"finishColorGreen"] floatValue];
+               b = [[dictionary valueForKey:@"finishColorBlue"] floatValue];
+               a = [[dictionary valueForKey:@"finishColorAlpha"] floatValue];
+               endColor = (ccColor4F) {r,g,b,a};
+               
+               r = [[dictionary valueForKey:@"finishColorVarianceRed"] floatValue];
+               g = [[dictionary valueForKey:@"finishColorVarianceGreen"] floatValue];
+               b = [[dictionary valueForKey:@"finishColorVarianceBlue"] floatValue];
+               a = [[dictionary valueForKey:@"finishColorVarianceAlpha"] floatValue];
+               endColorVar = (ccColor4F) {r,g,b,a};
+               
+               // particle size
+               startSize = [[dictionary valueForKey:@"startParticleSize"] floatValue];
+               startSizeVar = [[dictionary valueForKey:@"startParticleSizeVariance"] floatValue];
+               endSize = [[dictionary valueForKey:@"finishParticleSize"] floatValue];
+               endSizeVar = [[dictionary valueForKey:@"finishParticleSizeVariance"] floatValue];
+               
+               
+               // position
+               float x = [[dictionary valueForKey:@"sourcePositionx"] floatValue];
+               float y = [[dictionary valueForKey:@"sourcePositiony"] floatValue];
+               self.position = ccp(x,y);
+               posVar.x = [[dictionary valueForKey:@"sourcePositionVariancex"] floatValue];
+               posVar.y = [[dictionary valueForKey:@"sourcePositionVariancey"] floatValue];
+                               
+               
+               // Spinning
+               startSpin = [[dictionary valueForKey:@"rotationStart"] floatValue];
+               startSpinVar = [[dictionary valueForKey:@"rotationStartVariance"] floatValue];
+               endSpin = [[dictionary valueForKey:@"rotationEnd"] floatValue];
+               endSpinVar = [[dictionary valueForKey:@"rotationEndVariance"] floatValue];
+               
+               emitterMode_ = [[dictionary valueForKey:@"emitterType"] intValue];
+
+               // Mode A: Gravity + tangential accel + radial accel
+               if( emitterMode_ == kCCParticleModeGravity ) {
+                       // gravity
+                       mode.A.gravity.x = [[dictionary valueForKey:@"gravityx"] floatValue];
+                       mode.A.gravity.y = [[dictionary valueForKey:@"gravityy"] floatValue];
+                       
+                       //
+                       // speed
+                       mode.A.speed = [[dictionary valueForKey:@"speed"] floatValue];
+                       mode.A.speedVar = [[dictionary valueForKey:@"speedVariance"] floatValue];
+                       
+                       // radial acceleration                  
+                       NSString *tmp = [dictionary valueForKey:@"radialAcceleration"];
+                       mode.A.radialAccel = tmp ? [tmp floatValue] : 0;
+                       
+                       tmp = [dictionary valueForKey:@"radialAccelVariance"];
+                       mode.A.radialAccelVar = tmp ? [tmp floatValue] : 0;
+                                               
+                       // tangential acceleration
+                       tmp = [dictionary valueForKey:@"tangentialAcceleration"];
+                       mode.A.tangentialAccel = tmp ? [tmp floatValue] : 0;
+                       
+                       tmp = [dictionary valueForKey:@"tangentialAccelVariance"];
+                       mode.A.tangentialAccelVar = tmp ? [tmp floatValue] : 0;
+               }
+               
+               
+               // or Mode B: radius movement
+               else if( emitterMode_ == kCCParticleModeRadius ) {
+                       float maxRadius = [[dictionary valueForKey:@"maxRadius"] floatValue];
+                       float maxRadiusVar = [[dictionary valueForKey:@"maxRadiusVariance"] floatValue];
+                       float minRadius = [[dictionary valueForKey:@"minRadius"] floatValue];
+                       
+                       mode.B.startRadius = maxRadius;
+                       mode.B.startRadiusVar = maxRadiusVar;
+                       mode.B.endRadius = minRadius;
+                       mode.B.endRadiusVar = 0;
+                       mode.B.rotatePerSecond = [[dictionary valueForKey:@"rotatePerSecond"] floatValue];
+                       mode.B.rotatePerSecondVar = [[dictionary valueForKey:@"rotatePerSecondVariance"] floatValue];
+
+               } else {
+                       NSAssert( NO, @"Invalid emitterType in config file");
+               }
+               
+               // life span
+               life = [[dictionary valueForKey:@"particleLifespan"] floatValue];
+               lifeVar = [[dictionary valueForKey:@"particleLifespanVariance"] floatValue];                            
+               
+               // emission Rate
+               emissionRate = totalParticles/life;
+
+               // texture              
+               // Try to get the texture from the cache
+               NSString *textureName = [dictionary valueForKey:@"textureFileName"];
+
+               CCTexture2D *tex = [[CCTextureCache sharedTextureCache] addImage:textureName];
+
+               if( tex )
+                       self.texture = tex;
+
+               else {
+
+                       NSString *textureData = [dictionary valueForKey:@"textureImageData"];
+                       NSAssert( textureData, @"CCParticleSystem: Couldn't load texture");
+                       
+                       // if it fails, try to get it from the base64-gzipped data                      
+                       unsigned char *buffer = NULL;
+                       int len = base64Decode((unsigned char*)[textureData UTF8String], (unsigned int)[textureData length], &buffer);
+                       NSAssert( buffer != NULL, @"CCParticleSystem: error decoding textureImageData");
+                               
+                       unsigned char *deflated = NULL;
+                       NSUInteger deflatedLen = ccInflateMemory(buffer, len, &deflated);
+                       free( buffer );
+                               
+                       NSAssert( deflated != NULL, @"CCParticleSystem: error ungzipping textureImageData");
+                       NSData *data = [[NSData alloc] initWithBytes:deflated length:deflatedLen];
+                       
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+                       UIImage *image = [[UIImage alloc] initWithData:data];
+#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED)
+                       NSBitmapImageRep *image = [[NSBitmapImageRep alloc] initWithData:data];
+#endif
+                       
+                       free(deflated); deflated = NULL;
+
+                       self.texture = [[CCTextureCache sharedTextureCache] addCGImage:[image CGImage] forKey:textureName];
+                       [data release];
+                       [image release];
+               }
+               
+               NSAssert( [self texture] != NULL, @"CCParticleSystem: error loading the texture");
+               
+       }
+       
+       return self;
+}
+
+-(id) initWithTotalParticles:(NSUInteger) numberOfParticles
+{
+       if( (self=[super init]) ) {
+
+               totalParticles = numberOfParticles;
+               
+               particles = calloc( totalParticles, sizeof(tCCParticle) );
+
+               if( ! particles ) {
+                       NSLog(@"Particle system: not enough memory");
+                       [self release];
+                       return nil;
+               }
+               
+               // default, active
+               active = YES;
+               
+               // default blend function
+               blendFunc_ = (ccBlendFunc) { CC_BLEND_SRC, CC_BLEND_DST };
+               
+               // default movement type;
+               positionType_ = kCCPositionTypeFree;
+               
+               // by default be in mode A:
+               emitterMode_ = kCCParticleModeGravity;
+                               
+               // default: modulate
+               // XXX: not used
+       //      colorModulate = YES;
+               
+               autoRemoveOnFinish_ = NO;
+
+               // profiling
+#if CC_ENABLE_PROFILERS
+               _profilingTimer = [[CCProfiler timerWithName:@"particle system" andInstance:self] retain];
+#endif
+               
+               // Optimization: compile udpateParticle method
+               updateParticleSel = @selector(updateQuadWithParticle:newPosition:);
+               updateParticleImp = (CC_UPDATE_PARTICLE_IMP) [self methodForSelector:updateParticleSel];
+
+               // udpate after action in run!
+               [self scheduleUpdateWithPriority:1];
+               
+       }
+
+       return self;
+}
+
+-(void) dealloc
+{
+       free( particles );
+
+       [texture_ release];
+       // profiling
+#if CC_ENABLE_PROFILERS
+       [CCProfiler releaseTimer:_profilingTimer];
+#endif
+       
+       [super dealloc];
+}
+
+-(BOOL) addParticle
+{
+       if( [self isFull] )
+               return NO;
+       
+       tCCParticle * particle = &particles[ particleCount ];
+               
+       [self initParticle: particle];          
+       particleCount++;
+                               
+       return YES;
+}
+
+-(void) initParticle: (tCCParticle*) particle
+{
+
+       // timeToLive
+       // no negative life. prevent division by 0
+       particle->timeToLive = life + lifeVar * CCRANDOM_MINUS1_1();
+       particle->timeToLive = MAX(0, particle->timeToLive);
+
+       // position
+       particle->pos.x = sourcePosition.x + posVar.x * CCRANDOM_MINUS1_1();
+       particle->pos.x *= CC_CONTENT_SCALE_FACTOR();
+       particle->pos.y = sourcePosition.y + posVar.y * CCRANDOM_MINUS1_1();
+       particle->pos.y *= CC_CONTENT_SCALE_FACTOR();
+       
+       // Color
+       ccColor4F start;
+       start.r = clampf( startColor.r + startColorVar.r * CCRANDOM_MINUS1_1(), 0, 1);
+       start.g = clampf( startColor.g + startColorVar.g * CCRANDOM_MINUS1_1(), 0, 1);
+       start.b = clampf( startColor.b + startColorVar.b * CCRANDOM_MINUS1_1(), 0, 1);
+       start.a = clampf( startColor.a + startColorVar.a * CCRANDOM_MINUS1_1(), 0, 1);
+       
+       ccColor4F end;
+       end.r = clampf( endColor.r + endColorVar.r * CCRANDOM_MINUS1_1(), 0, 1);
+       end.g = clampf( endColor.g + endColorVar.g * CCRANDOM_MINUS1_1(), 0, 1);
+       end.b = clampf( endColor.b + endColorVar.b * CCRANDOM_MINUS1_1(), 0, 1);
+       end.a = clampf( endColor.a + endColorVar.a * CCRANDOM_MINUS1_1(), 0, 1);
+       
+       particle->color = start;
+       particle->deltaColor.r = (end.r - start.r) / particle->timeToLive;
+       particle->deltaColor.g = (end.g - start.g) / particle->timeToLive;
+       particle->deltaColor.b = (end.b - start.b) / particle->timeToLive;
+       particle->deltaColor.a = (end.a - start.a) / particle->timeToLive;
+       
+       // size
+       float startS = startSize + startSizeVar * CCRANDOM_MINUS1_1();
+       startS = MAX(0, startS); // No negative value
+       startS *= CC_CONTENT_SCALE_FACTOR();
+       
+       particle->size = startS;
+       if( endSize == kCCParticleStartSizeEqualToEndSize )
+               particle->deltaSize = 0;
+       else {
+               float endS = endSize + endSizeVar * CCRANDOM_MINUS1_1();
+               endS = MAX(0, endS);    // No negative values
+               endS *= CC_CONTENT_SCALE_FACTOR();
+               particle->deltaSize = (endS - startS) / particle->timeToLive;
+       }
+       
+       // rotation
+       float startA = startSpin + startSpinVar * CCRANDOM_MINUS1_1();
+       float endA = endSpin + endSpinVar * CCRANDOM_MINUS1_1();
+       particle->rotation = startA;
+       particle->deltaRotation = (endA - startA) / particle->timeToLive;
+       
+       // position
+       if( positionType_ == kCCPositionTypeFree ) {
+               CGPoint p = [self convertToWorldSpace:CGPointZero];
+               particle->startPos = ccpMult( p, CC_CONTENT_SCALE_FACTOR() );
+       }
+       else if( positionType_ == kCCPositionTypeRelative ) {
+               particle->startPos = ccpMult( position_, CC_CONTENT_SCALE_FACTOR() );
+       }
+       
+       // direction
+       float a = CC_DEGREES_TO_RADIANS( angle + angleVar * CCRANDOM_MINUS1_1() );      
+       
+       // Mode Gravity: A
+       if( emitterMode_ == kCCParticleModeGravity ) {
+
+               CGPoint v = {cosf( a ), sinf( a )};
+               float s = mode.A.speed + mode.A.speedVar * CCRANDOM_MINUS1_1();
+               s *= CC_CONTENT_SCALE_FACTOR();
+               
+               // direction
+               particle->mode.A.dir = ccpMult( v, s );
+               
+               // radial accel
+               particle->mode.A.radialAccel = mode.A.radialAccel + mode.A.radialAccelVar * CCRANDOM_MINUS1_1();
+               particle->mode.A.radialAccel *= CC_CONTENT_SCALE_FACTOR();
+               
+               // tangential accel
+               particle->mode.A.tangentialAccel = mode.A.tangentialAccel + mode.A.tangentialAccelVar * CCRANDOM_MINUS1_1();
+               particle->mode.A.tangentialAccel *= CC_CONTENT_SCALE_FACTOR();
+
+       }
+       
+       // Mode Radius: B
+       else {
+               // Set the default diameter of the particle from the source position
+               float startRadius = mode.B.startRadius + mode.B.startRadiusVar * CCRANDOM_MINUS1_1();
+               float endRadius = mode.B.endRadius + mode.B.endRadiusVar * CCRANDOM_MINUS1_1();
+
+               startRadius *= CC_CONTENT_SCALE_FACTOR();
+               endRadius *= CC_CONTENT_SCALE_FACTOR();
+               
+               particle->mode.B.radius = startRadius;
+
+               if( mode.B.endRadius == kCCParticleStartRadiusEqualToEndRadius )
+                       particle->mode.B.deltaRadius = 0;
+               else
+                       particle->mode.B.deltaRadius = (endRadius - startRadius) / particle->timeToLive;
+       
+               particle->mode.B.angle = a;
+               particle->mode.B.degreesPerSecond = CC_DEGREES_TO_RADIANS(mode.B.rotatePerSecond + mode.B.rotatePerSecondVar * CCRANDOM_MINUS1_1());
+       }       
+}
+
+-(void) stopSystem
+{
+       active = NO;
+       elapsed = duration;
+       emitCounter = 0;
+}
+
+-(void) resetSystem
+{
+       active = YES;
+       elapsed = 0;
+       for(particleIdx = 0; particleIdx < particleCount; ++particleIdx) {
+               tCCParticle *p = &particles[particleIdx];
+               p->timeToLive = 0;
+       }
+}
+
+-(BOOL) isFull
+{
+       return (particleCount == totalParticles);
+}
+
+#pragma mark ParticleSystem - MainLoop
+-(void) update: (ccTime) dt
+{
+       if( active && emissionRate ) {
+               float rate = 1.0f / emissionRate;
+               emitCounter += dt;
+               while( particleCount < totalParticles && emitCounter > rate ) {
+                       [self addParticle];
+                       emitCounter -= rate;
+               }
+               
+               elapsed += dt;
+               if(duration != -1 && duration < elapsed)
+                       [self stopSystem];
+       }
+       
+       particleIdx = 0;
+       
+       
+#if CC_ENABLE_PROFILERS
+       CCProfilingBeginTimingBlock(_profilingTimer);
+#endif
+       
+       
+       CGPoint currentPosition = CGPointZero;
+       if( positionType_ == kCCPositionTypeFree ) {
+               currentPosition = [self convertToWorldSpace:CGPointZero];
+               currentPosition.x *= CC_CONTENT_SCALE_FACTOR();
+               currentPosition.y *= CC_CONTENT_SCALE_FACTOR();
+       }
+       else if( positionType_ == kCCPositionTypeRelative ) {
+               currentPosition = position_;
+               currentPosition.x *= CC_CONTENT_SCALE_FACTOR();
+               currentPosition.y *= CC_CONTENT_SCALE_FACTOR();
+       }
+       
+       while( particleIdx < particleCount )
+       {
+               tCCParticle *p = &particles[particleIdx];
+               
+               // life
+               p->timeToLive -= dt;
+
+               if( p->timeToLive > 0 ) {
+                       
+                       // Mode A: gravity, direction, tangential accel & radial accel
+                       if( emitterMode_ == kCCParticleModeGravity ) {
+                               CGPoint tmp, radial, tangential;
+                               
+                               radial = CGPointZero;
+                               // radial acceleration
+                               if(p->pos.x || p->pos.y)
+                                       radial = ccpNormalize(p->pos);
+                               
+                               tangential = radial;
+                               radial = ccpMult(radial, p->mode.A.radialAccel);
+                               
+                               // tangential acceleration
+                               float newy = tangential.x;
+                               tangential.x = -tangential.y;
+                               tangential.y = newy;
+                               tangential = ccpMult(tangential, p->mode.A.tangentialAccel);
+                               
+                               // (gravity + radial + tangential) * dt
+                               tmp = ccpAdd( ccpAdd( radial, tangential), mode.A.gravity);
+                               tmp = ccpMult( tmp, dt);
+                               p->mode.A.dir = ccpAdd( p->mode.A.dir, tmp);
+                               tmp = ccpMult(p->mode.A.dir, dt);
+                               p->pos = ccpAdd( p->pos, tmp );
+                       }
+                       
+                       // Mode B: radius movement
+                       else {                          
+                               // Update the angle and radius of the particle.
+                               p->mode.B.angle += p->mode.B.degreesPerSecond * dt;
+                               p->mode.B.radius += p->mode.B.deltaRadius * dt;
+                               
+                               p->pos.x = - cosf(p->mode.B.angle) * p->mode.B.radius;
+                               p->pos.y = - sinf(p->mode.B.angle) * p->mode.B.radius;
+                       }
+                       
+                       // color
+                       p->color.r += (p->deltaColor.r * dt);
+                       p->color.g += (p->deltaColor.g * dt);
+                       p->color.b += (p->deltaColor.b * dt);
+                       p->color.a += (p->deltaColor.a * dt);
+                       
+                       // size
+                       p->size += (p->deltaSize * dt);
+                       p->size = MAX( 0, p->size );
+                       
+                       // angle
+                       p->rotation += (p->deltaRotation * dt);
+                                               
+                       //
+                       // update values in quad
+                       //
+                       
+                       CGPoint newPos;
+                       
+                       if( positionType_ == kCCPositionTypeFree || positionType_ == kCCPositionTypeRelative ) {
+                               CGPoint diff = ccpSub( currentPosition, p->startPos );
+                               newPos = ccpSub(p->pos, diff);
+                               
+                       } else
+                               newPos = p->pos;
+
+                       
+                       updateParticleImp(self, updateParticleSel, p, newPos);
+                       
+                       // update particle counter
+                       particleIdx++;
+                       
+               } else {
+                       // life < 0
+                       if( particleIdx != particleCount-1 )
+                               particles[particleIdx] = particles[particleCount-1];
+                       particleCount--;
+                       
+                       if( particleCount == 0 && autoRemoveOnFinish_ ) {
+                               [self unscheduleUpdate];
+                               [parent_ removeChild:self cleanup:YES];
+                               return;
+                       }
+               }
+       }
+       
+#if CC_ENABLE_PROFILERS
+       CCProfilingEndTimingBlock(_profilingTimer);
+#endif
+       
+#ifdef CC_USES_VBO
+       [self postStep];
+#endif
+}
+
+-(void) updateQuadWithParticle:(tCCParticle*)particle newPosition:(CGPoint)pos;
+{
+       // should be overriden
+}
+
+-(void) postStep
+{
+       // should be overriden
+}
+
+#pragma mark ParticleSystem - CCTexture protocol
+
+-(void) setTexture:(CCTexture2D*) texture
+{
+       [texture_ release];
+       texture_ = [texture retain];
+
+       // If the new texture has No premultiplied alpha, AND the blendFunc hasn't been changed, then update it
+       if( texture_ && ! [texture hasPremultipliedAlpha] &&            
+          ( blendFunc_.src == CC_BLEND_SRC && blendFunc_.dst == CC_BLEND_DST ) ) {
+       
+               blendFunc_.src = GL_SRC_ALPHA;
+               blendFunc_.dst = GL_ONE_MINUS_SRC_ALPHA;
+       }
+}
+
+-(CCTexture2D*) texture
+{
+       return texture_;
+}
+
+#pragma mark ParticleSystem - Additive Blending
+-(void) setBlendAdditive:(BOOL)additive
+{
+       if( additive ) {
+               blendFunc_.src = GL_SRC_ALPHA;
+               blendFunc_.dst = GL_ONE;
+
+       } else {
+               
+               if( texture_ && ! [texture_ hasPremultipliedAlpha] ) {
+                       blendFunc_.src = GL_SRC_ALPHA;
+                       blendFunc_.dst = GL_ONE_MINUS_SRC_ALPHA;
+               } else {
+                       blendFunc_.src = CC_BLEND_SRC;
+                       blendFunc_.dst = CC_BLEND_DST;
+               }
+       }
+}
+
+-(BOOL) blendAdditive
+{
+       return( blendFunc_.src == GL_SRC_ALPHA && blendFunc_.dst == GL_ONE);
+}
+
+#pragma mark ParticleSystem - Properties of Gravity Mode 
+-(void) setTangentialAccel:(float)t
+{
+       NSAssert( emitterMode_ == kCCParticleModeGravity, @"Particle Mode should be Gravity");
+       mode.A.tangentialAccel = t;
+}
+-(float) tangentialAccel
+{
+       NSAssert( emitterMode_ == kCCParticleModeGravity, @"Particle Mode should be Gravity");
+       return mode.A.tangentialAccel;
+}
+
+-(void) setTangentialAccelVar:(float)t
+{
+       NSAssert( emitterMode_ == kCCParticleModeGravity, @"Particle Mode should be Gravity");
+       mode.A.tangentialAccelVar = t;
+}
+-(float) tangentialAccelVar
+{
+       NSAssert( emitterMode_ == kCCParticleModeGravity, @"Particle Mode should be Gravity");
+       return mode.A.tangentialAccelVar;
+}
+
+-(void) setRadialAccel:(float)t
+{
+       NSAssert( emitterMode_ == kCCParticleModeGravity, @"Particle Mode should be Gravity");
+       mode.A.radialAccel = t;
+}
+-(float) radialAccel
+{
+       NSAssert( emitterMode_ == kCCParticleModeGravity, @"Particle Mode should be Gravity");
+       return mode.A.radialAccel;
+}
+
+-(void) setRadialAccelVar:(float)t
+{
+       NSAssert( emitterMode_ == kCCParticleModeGravity, @"Particle Mode should be Gravity");
+       mode.A.radialAccelVar = t;
+}
+-(float) radialAccelVar
+{
+       NSAssert( emitterMode_ == kCCParticleModeGravity, @"Particle Mode should be Gravity");
+       return mode.A.radialAccelVar;
+}
+
+-(void) setGravity:(CGPoint)g
+{
+       NSAssert( emitterMode_ == kCCParticleModeGravity, @"Particle Mode should be Gravity");
+       mode.A.gravity = g;
+}
+-(CGPoint) gravity
+{
+       NSAssert( emitterMode_ == kCCParticleModeGravity, @"Particle Mode should be Gravity");
+       return mode.A.gravity;
+}
+
+-(void) setSpeed:(float)speed
+{
+       NSAssert( emitterMode_ == kCCParticleModeGravity, @"Particle Mode should be Gravity");
+       mode.A.speed = speed;
+}
+-(float) speed
+{
+       NSAssert( emitterMode_ == kCCParticleModeGravity, @"Particle Mode should be Gravity");
+       return mode.A.speed;
+}
+
+-(void) setSpeedVar:(float)speedVar
+{
+       NSAssert( emitterMode_ == kCCParticleModeGravity, @"Particle Mode should be Gravity");
+       mode.A.speedVar = speedVar;
+}
+-(float) speedVar
+{
+       NSAssert( emitterMode_ == kCCParticleModeGravity, @"Particle Mode should be Gravity");
+       return mode.A.speedVar;
+}
+
+#pragma mark ParticleSystem - Properties of Radius Mode
+
+-(void) setStartRadius:(float)startRadius
+{
+       NSAssert( emitterMode_ == kCCParticleModeRadius, @"Particle Mode should be Radius");
+       mode.B.startRadius = startRadius;
+}
+-(float) startRadius
+{
+       NSAssert( emitterMode_ == kCCParticleModeRadius, @"Particle Mode should be Radius");
+       return mode.B.startRadius;
+}
+
+-(void) setStartRadiusVar:(float)startRadiusVar
+{
+       NSAssert( emitterMode_ == kCCParticleModeRadius, @"Particle Mode should be Radius");
+       mode.B.startRadiusVar = startRadiusVar;
+}
+-(float) startRadiusVar
+{
+       NSAssert( emitterMode_ == kCCParticleModeRadius, @"Particle Mode should be Radius");
+       return mode.B.startRadiusVar;
+}
+
+-(void) setEndRadius:(float)endRadius
+{
+       NSAssert( emitterMode_ == kCCParticleModeRadius, @"Particle Mode should be Radius");
+       mode.B.endRadius = endRadius;
+}
+-(float) endRadius
+{
+       NSAssert( emitterMode_ == kCCParticleModeRadius, @"Particle Mode should be Radius");
+       return mode.B.endRadius;
+}
+
+-(void) setEndRadiusVar:(float)endRadiusVar
+{
+       NSAssert( emitterMode_ == kCCParticleModeRadius, @"Particle Mode should be Radius");
+       mode.B.endRadiusVar = endRadiusVar;
+}
+-(float) endRadiusVar
+{
+       NSAssert( emitterMode_ == kCCParticleModeRadius, @"Particle Mode should be Radius");
+       return mode.B.endRadiusVar;
+}
+
+-(void) setRotatePerSecond:(float)degrees
+{
+       NSAssert( emitterMode_ == kCCParticleModeRadius, @"Particle Mode should be Radius");
+       mode.B.rotatePerSecond = degrees;
+}
+-(float) rotatePerSecond
+{
+       NSAssert( emitterMode_ == kCCParticleModeRadius, @"Particle Mode should be Radius");
+       return mode.B.rotatePerSecond;
+}
+
+-(void) setRotatePerSecondVar:(float)degrees
+{
+       NSAssert( emitterMode_ == kCCParticleModeRadius, @"Particle Mode should be Radius");
+       mode.B.rotatePerSecondVar = degrees;
+}
+-(float) rotatePerSecondVar
+{
+       NSAssert( emitterMode_ == kCCParticleModeRadius, @"Particle Mode should be Radius");
+       return mode.B.rotatePerSecondVar;
+}
+@end
+
+
diff --git a/libs/cocos2d/CCParticleSystemPoint.h b/libs/cocos2d/CCParticleSystemPoint.h
new file mode 100644 (file)
index 0000000..f0918fe
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2008-2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+
+#import <Availability.h>
+#import "CCParticleSystem.h"
+
+#define CC_MAX_PARTICLE_SIZE 64
+
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+
+/** CCParticleSystemPoint is a subclass of CCParticleSystem
+ Attributes of a Particle System:
+ * All the attributes of Particle System
+
+ Features:
+  * consumes small memory: uses 1 vertex (x,y) per particle, no need to assign tex coordinates
+  * size can't be bigger than 64
+  * the system can't be scaled since the particles are rendered using GL_POINT_SPRITE
+ Limitations:
+  * On 3rd gen iPhone devices and iPads, this node performs MUCH slower than CCParticleSystemQuad.
+ */
+@interface CCParticleSystemPoint : CCParticleSystem
+{      
+       // Array of (x,y,size) 
+       ccPointSprite *vertices;
+       // vertices buffer id
+#if CC_USES_VBO
+       GLuint  verticesID;
+#endif
+}
+@end
+
+#elif __MAC_OS_X_VERSION_MAX_ALLOWED
+
+#import "CCParticleSystemQuad.h"
+
+@interface CCParticleSystemPoint : CCParticleSystemQuad
+@end
+
+#endif
diff --git a/libs/cocos2d/CCParticleSystemPoint.m b/libs/cocos2d/CCParticleSystemPoint.m
new file mode 100644 (file)
index 0000000..74ccc96
--- /dev/null
@@ -0,0 +1,209 @@
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2008-2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+#import <Availability.h>
+#import "CCParticleSystemPoint.h"
+
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+
+// opengl
+#import "Platforms/CCGL.h"
+
+// cocos2d
+#import "CCTextureCache.h"
+#import "ccMacros.h"
+
+// support
+#import "Support/OpenGL_Internal.h"
+#import "Support/CGPointExtension.h"
+
+@implementation CCParticleSystemPoint
+
+-(id) initWithTotalParticles:(NSUInteger) numberOfParticles
+{
+       if( (self=[super initWithTotalParticles:numberOfParticles]) ) {
+
+               vertices = malloc( sizeof(ccPointSprite) * totalParticles );
+
+               if( ! vertices ) {
+                       NSLog(@"cocos2d: Particle system: not enough memory");
+                       [self release];
+                       return nil;
+               }
+
+#if CC_USES_VBO
+               glGenBuffers(1, &verticesID);
+               
+               // initial binding
+               glBindBuffer(GL_ARRAY_BUFFER, verticesID);
+               glBufferData(GL_ARRAY_BUFFER, sizeof(ccPointSprite)*totalParticles, vertices, GL_DYNAMIC_DRAW);
+               glBindBuffer(GL_ARRAY_BUFFER, 0);
+#endif
+       }
+
+       return self;
+}
+
+-(void) dealloc
+{
+       free(vertices);
+#if CC_USES_VBO
+       glDeleteBuffers(1, &verticesID);
+#endif
+       
+       [super dealloc];
+}
+
+-(void) updateQuadWithParticle:(tCCParticle*)p newPosition:(CGPoint)newPos
+{      
+       // place vertices and colos in array
+       vertices[particleIdx].pos = (ccVertex2F) {newPos.x, newPos.y};
+       vertices[particleIdx].size = p->size;
+       ccColor4B color =  { p->color.r*255, p->color.g*255, p->color.b*255, p->color.a*255 };
+       vertices[particleIdx].color = color;
+}
+
+-(void) postStep
+{
+#if CC_USES_VBO
+       glBindBuffer(GL_ARRAY_BUFFER, verticesID);
+       glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(ccPointSprite)*particleCount, vertices);
+       glBindBuffer(GL_ARRAY_BUFFER, 0);
+#endif
+}
+
+-(void) draw
+{
+    if (particleIdx==0)
+        return;
+       
+       // Default GL states: GL_TEXTURE_2D, GL_VERTEX_ARRAY, GL_COLOR_ARRAY, GL_TEXTURE_COORD_ARRAY
+       // Needed states: GL_TEXTURE_2D, GL_VERTEX_ARRAY, GL_COLOR_ARRAY
+       // Unneeded states: GL_TEXTURE_COORD_ARRAY
+       glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+       
+       glBindTexture(GL_TEXTURE_2D, texture_.name);
+       
+       glEnable(GL_POINT_SPRITE_OES);
+       glTexEnvi( GL_POINT_SPRITE_OES, GL_COORD_REPLACE_OES, GL_TRUE );
+       
+#define kPointSize sizeof(vertices[0])
+
+#if CC_USES_VBO
+       glBindBuffer(GL_ARRAY_BUFFER, verticesID);
+
+       glVertexPointer(2,GL_FLOAT, kPointSize, 0);
+
+       glColorPointer(4, GL_UNSIGNED_BYTE, kPointSize, (GLvoid*) offsetof(ccPointSprite, color) );
+
+       glEnableClientState(GL_POINT_SIZE_ARRAY_OES);
+       glPointSizePointerOES(GL_FLOAT, kPointSize, (GLvoid*) offsetof(ccPointSprite, size) );
+#else // Uses Vertex Array List
+       int offset = (int)vertices;
+       glVertexPointer(2,GL_FLOAT, kPointSize, (GLvoid*) offset);
+       
+       int diff = offsetof(ccPointSprite, color);
+       glColorPointer(4, GL_UNSIGNED_BYTE, kPointSize, (GLvoid*) (offset+diff));
+       
+       glEnableClientState(GL_POINT_SIZE_ARRAY_OES);
+       diff = offsetof(ccPointSprite, size);
+       glPointSizePointerOES(GL_FLOAT, kPointSize, (GLvoid*) (offset+diff));
+#endif
+
+       BOOL newBlend = blendFunc_.src != CC_BLEND_SRC || blendFunc_.dst != CC_BLEND_DST;
+       if( newBlend )
+               glBlendFunc( blendFunc_.src, blendFunc_.dst );
+
+
+       glDrawArrays(GL_POINTS, 0, particleIdx);
+       
+       // restore blend state
+       if( newBlend )
+               glBlendFunc( CC_BLEND_SRC, CC_BLEND_DST);
+
+       
+#if CC_USES_VBO
+       // unbind VBO buffer
+       glBindBuffer(GL_ARRAY_BUFFER, 0);
+#endif
+       
+       glDisableClientState(GL_POINT_SIZE_ARRAY_OES);
+       glDisable(GL_POINT_SPRITE_OES);
+
+       // restore GL default state
+       glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+}
+
+#pragma mark Non supported properties
+
+//
+// SPIN IS NOT SUPPORTED
+//
+-(void) setStartSpin:(float)a
+{
+       NSAssert(a == 0, @"PointParticleSystem doesn't support spinning");
+       [super setStartSpin:a];
+}
+-(void) setStartSpinVar:(float)a
+{
+       NSAssert(a == 0, @"PointParticleSystem doesn't support spinning");
+       [super setStartSpin:a];
+}
+-(void) setEndSpin:(float)a
+{
+       NSAssert(a == 0, @"PointParticleSystem doesn't support spinning");
+       [super setStartSpin:a];
+}
+-(void) setEndSpinVar:(float)a
+{
+       NSAssert(a == 0, @"PointParticleSystem doesn't support spinning");
+       [super setStartSpin:a];
+}
+
+//
+// SIZE > 64 IS NOT SUPPORTED
+//
+-(void) setStartSize:(float)size
+{
+       NSAssert(size >= 0 && size <= CC_MAX_PARTICLE_SIZE, @"PointParticleSystem only supports 0 <= size <= 64");
+       [super setStartSize:size];
+}
+
+-(void) setEndSize:(float)size
+{
+       NSAssert( (size == kCCParticleStartSizeEqualToEndSize) ||
+                        ( size >= 0 && size <= CC_MAX_PARTICLE_SIZE), @"PointParticleSystem only supports 0 <= size <= 64");
+       [super setEndSize:size];
+}
+@end
+
+#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED)
+@implementation CCParticleSystemPoint
+@end
+
+#endif // __MAC_OS_X_VERSION_MAX_ALLOWED
+
+
diff --git a/libs/cocos2d/CCParticleSystemQuad.h b/libs/cocos2d/CCParticleSystemQuad.h
new file mode 100644 (file)
index 0000000..74a9d93
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2009 Leonardo Kasperavičius
+ *
+ * Copyright (c) 2008-2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+
+#import "CCParticleSystem.h"
+#import "ccConfig.h"
+
+@class CCSpriteFrame;
+
+/** CCParticleSystemQuad is a subclass of CCParticleSystem
+
+ It includes all the features of ParticleSystem.
+ Special features and Limitations:     
+  - Particle size can be any float number.
+  - The system can be scaled
+  - The particles can be rotated
+  - On 1st and 2nd gen iPhones: It is only a bit slower that CCParticleSystemPoint
+  - On 3rd gen iPhone and iPads: It is MUCH faster than CCParticleSystemPoint
+  - It consumes more RAM and more GPU memory than CCParticleSystemPoint
+  - It supports subrects
+ @since v0.8
+ */
+@interface CCParticleSystemQuad : CCParticleSystem
+{
+       ccV2F_C4B_T2F_Quad      *quads_;                // quads to be rendered
+       GLushort                        *indices_;              // indices
+#if CC_USES_VBO
+       GLuint                          quadsID_;               // VBO id
+#endif
+}
+
+/** initialices the indices for the vertices */
+-(void) initIndices;
+
+/** initilizes the texture with a rectangle measured Points */
+-(void) initTexCoordsWithRect:(CGRect)rect;
+
+/** Sets a new CCSpriteFrame as particle.
+ WARNING: this method is experimental. Use setTexture:withRect instead.
+ @since v0.99.4
+ */
+-(void)setDisplayFrame:(CCSpriteFrame*)spriteFrame;
+
+/** Sets a new texture with a rect. The rect is in Points.
+ @since v0.99.4
+ */
+-(void) setTexture:(CCTexture2D *)texture withRect:(CGRect)rect;
+
+@end
+
diff --git a/libs/cocos2d/CCParticleSystemQuad.m b/libs/cocos2d/CCParticleSystemQuad.m
new file mode 100644 (file)
index 0000000..55754b5
--- /dev/null
@@ -0,0 +1,316 @@
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2009 Leonardo Kasperavičius
+ *
+ * Copyright (c) 2008-2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+
+// opengl
+#import "Platforms/CCGL.h"
+
+// cocos2d
+#import "ccConfig.h"
+#import "CCParticleSystemQuad.h"
+#import "CCTextureCache.h"
+#import "ccMacros.h"
+#import "CCSpriteFrame.h"
+
+// support
+#import "Support/OpenGL_Internal.h"
+#import "Support/CGPointExtension.h"
+
+@implementation CCParticleSystemQuad
+
+
+// overriding the init method
+-(id) initWithTotalParticles:(NSUInteger) numberOfParticles
+{
+       // base initialization
+       if( (self=[super initWithTotalParticles:numberOfParticles]) ) {
+       
+               // allocating data space
+               quads_ = calloc( sizeof(quads_[0]) * totalParticles, 1 );
+               indices_ = calloc( sizeof(indices_[0]) * totalParticles * 6, 1 );
+               
+               if( !quads_ || !indices_) {
+                       NSLog(@"cocos2d: Particle system: not enough memory");
+                       if( quads_ )
+                               free( quads_ );
+                       if(indices_)
+                               free(indices_);
+                       
+                       [self release];
+                       return nil;
+               }
+               
+               // initialize only once the texCoords and the indices
+               [self initTexCoordsWithRect:CGRectMake(0, 0, [texture_ pixelsWide], [texture_ pixelsHigh])];
+               [self initIndices];
+
+#if CC_USES_VBO
+               // create the VBO buffer
+               glGenBuffers(1, &quadsID_);
+               
+               // initial binding
+               glBindBuffer(GL_ARRAY_BUFFER, quadsID_);
+               glBufferData(GL_ARRAY_BUFFER, sizeof(quads_[0])*totalParticles, quads_,GL_DYNAMIC_DRAW);        
+               glBindBuffer(GL_ARRAY_BUFFER, 0);
+#endif
+       }
+               
+       return self;
+}
+
+-(void) dealloc
+{
+       free(quads_);
+       free(indices_);
+#if CC_USES_VBO
+       glDeleteBuffers(1, &quadsID_);
+#endif
+       
+       [super dealloc];
+}
+
+// pointRect is in Points coordinates.
+-(void) initTexCoordsWithRect:(CGRect)pointRect
+{
+       // convert to pixels coords
+       CGRect rect = CGRectMake(
+                                                        pointRect.origin.x * CC_CONTENT_SCALE_FACTOR(),
+                                                        pointRect.origin.y * CC_CONTENT_SCALE_FACTOR(),
+                                                        pointRect.size.width * CC_CONTENT_SCALE_FACTOR(),
+                                                        pointRect.size.height * CC_CONTENT_SCALE_FACTOR() );
+
+       GLfloat wide = [texture_ pixelsWide];
+       GLfloat high = [texture_ pixelsHigh];
+
+#if CC_FIX_ARTIFACTS_BY_STRECHING_TEXEL
+       GLfloat left = (rect.origin.x*2+1) / (wide*2);
+       GLfloat bottom = (rect.origin.y*2+1) / (high*2);
+       GLfloat right = left + (rect.size.width*2-2) / (wide*2);
+       GLfloat top = bottom + (rect.size.height*2-2) / (high*2);
+#else
+       GLfloat left = rect.origin.x / wide;
+       GLfloat bottom = rect.origin.y / high;
+       GLfloat right = left + rect.size.width / wide;
+       GLfloat top = bottom + rect.size.height / high;
+#endif // ! CC_FIX_ARTIFACTS_BY_STRECHING_TEXEL
+       
+       // Important. Texture in cocos2d are inverted, so the Y component should be inverted
+       CC_SWAP( top, bottom);
+       
+       for(NSUInteger i=0; i<totalParticles; i++) {
+               // bottom-left vertex:
+               quads_[i].bl.texCoords.u = left;
+               quads_[i].bl.texCoords.v = bottom;
+               // bottom-right vertex:
+               quads_[i].br.texCoords.u = right;
+               quads_[i].br.texCoords.v = bottom;
+               // top-left vertex:
+               quads_[i].tl.texCoords.u = left;
+               quads_[i].tl.texCoords.v = top;
+               // top-right vertex:
+               quads_[i].tr.texCoords.u = right;
+               quads_[i].tr.texCoords.v = top;
+       }
+}
+
+-(void) setTexture:(CCTexture2D *)texture withRect:(CGRect)rect
+{
+       // Only update the texture if is different from the current one
+       if( [texture name] != [texture_ name] )
+               [super setTexture:texture];
+       
+       [self initTexCoordsWithRect:rect];
+}
+
+-(void) setTexture:(CCTexture2D *)texture
+{
+       CGSize s = [texture contentSize];
+       [self setTexture:texture withRect:CGRectMake(0,0, s.width, s.height)];
+}
+
+-(void) setDisplayFrame:(CCSpriteFrame *)spriteFrame
+{
+
+       NSAssert( CGPointEqualToPoint( spriteFrame.offsetInPixels , CGPointZero ), @"QuadParticle only supports SpriteFrames with no offsets");
+
+       // update texture before updating texture rect
+       if ( spriteFrame.texture.name != texture_.name )
+               [self setTexture: spriteFrame.texture]; 
+}
+
+-(void) initIndices
+{
+       for( NSUInteger i=0;i< totalParticles;i++) {
+               const NSUInteger i6 = i*6;
+               const NSUInteger i4 = i*4;
+               indices_[i6+0] = (GLushort) i4+0;
+               indices_[i6+1] = (GLushort) i4+1;
+               indices_[i6+2] = (GLushort) i4+2;
+               
+               indices_[i6+5] = (GLushort) i4+1;
+               indices_[i6+4] = (GLushort) i4+2;
+               indices_[i6+3] = (GLushort) i4+3;
+       }
+}
+
+-(void) updateQuadWithParticle:(tCCParticle*)p newPosition:(CGPoint)newPos
+{
+       // colors
+       ccV2F_C4B_T2F_Quad *quad = &(quads_[particleIdx]);
+       
+       ccColor4B color = { p->color.r*255, p->color.g*255, p->color.b*255, p->color.a*255};
+       quad->bl.colors = color;
+       quad->br.colors = color;
+       quad->tl.colors = color;
+       quad->tr.colors = color;
+       
+       // vertices
+       GLfloat size_2 = p->size/2;
+       if( p->rotation ) {
+               GLfloat x1 = -size_2;
+               GLfloat y1 = -size_2;
+               
+               GLfloat x2 = size_2;
+               GLfloat y2 = size_2;
+               GLfloat x = newPos.x;
+               GLfloat y = newPos.y;
+               
+               GLfloat r = (GLfloat)-CC_DEGREES_TO_RADIANS(p->rotation);
+               GLfloat cr = cosf(r);
+               GLfloat sr = sinf(r);
+               GLfloat ax = x1 * cr - y1 * sr + x;
+               GLfloat ay = x1 * sr + y1 * cr + y;
+               GLfloat bx = x2 * cr - y1 * sr + x;
+               GLfloat by = x2 * sr + y1 * cr + y;
+               GLfloat cx = x2 * cr - y2 * sr + x;
+               GLfloat cy = x2 * sr + y2 * cr + y;
+               GLfloat dx = x1 * cr - y2 * sr + x;
+               GLfloat dy = x1 * sr + y2 * cr + y;
+               
+               // bottom-left
+               quad->bl.vertices.x = ax;
+               quad->bl.vertices.y = ay;
+               
+               // bottom-right vertex:
+               quad->br.vertices.x = bx;
+               quad->br.vertices.y = by;
+               
+               // top-left vertex:
+               quad->tl.vertices.x = dx;
+               quad->tl.vertices.y = dy;
+               
+               // top-right vertex:
+               quad->tr.vertices.x = cx;
+               quad->tr.vertices.y = cy;
+       } else {
+               // bottom-left vertex:
+               quad->bl.vertices.x = newPos.x - size_2;
+               quad->bl.vertices.y = newPos.y - size_2;
+               
+               // bottom-right vertex:
+               quad->br.vertices.x = newPos.x + size_2;
+               quad->br.vertices.y = newPos.y - size_2;
+               
+               // top-left vertex:
+               quad->tl.vertices.x = newPos.x - size_2;
+               quad->tl.vertices.y = newPos.y + size_2;
+               
+               // top-right vertex:
+               quad->tr.vertices.x = newPos.x + size_2;
+               quad->tr.vertices.y = newPos.y + size_2;                                
+       }
+}
+
+-(void) postStep
+{
+#if CC_USES_VBO
+       glBindBuffer(GL_ARRAY_BUFFER, quadsID_);
+       glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(quads_[0])*particleCount, quads_);
+       glBindBuffer(GL_ARRAY_BUFFER, 0);
+#endif
+}
+
+// overriding draw method
+-(void) draw
+{      
+       // Default GL states: GL_TEXTURE_2D, GL_VERTEX_ARRAY, GL_COLOR_ARRAY, GL_TEXTURE_COORD_ARRAY
+       // Needed states: GL_TEXTURE_2D, GL_VERTEX_ARRAY, GL_COLOR_ARRAY, GL_TEXTURE_COORD_ARRAY
+       // Unneeded states: -
+
+       glBindTexture(GL_TEXTURE_2D, [texture_ name]);
+
+#define kQuadSize sizeof(quads_[0].bl)
+
+#if CC_USES_VBO
+       glBindBuffer(GL_ARRAY_BUFFER, quadsID_);
+
+       glVertexPointer(2,GL_FLOAT, kQuadSize, 0);
+
+       glColorPointer(4, GL_UNSIGNED_BYTE, kQuadSize, (GLvoid*) offsetof(ccV2F_C4B_T2F,colors) );
+       
+       glTexCoordPointer(2, GL_FLOAT, kQuadSize, (GLvoid*) offsetof(ccV2F_C4B_T2F,texCoords) );
+#else // vertex array list
+
+       NSUInteger offset = (NSUInteger) quads_;
+
+       // vertex
+       NSUInteger diff = offsetof( ccV2F_C4B_T2F, vertices);
+       glVertexPointer(2,GL_FLOAT, kQuadSize, (GLvoid*) (offset+diff) );
+       
+       // color
+       diff = offsetof( ccV2F_C4B_T2F, colors);
+       glColorPointer(4, GL_UNSIGNED_BYTE, kQuadSize, (GLvoid*)(offset + diff));
+       
+       // tex coords
+       diff = offsetof( ccV2F_C4B_T2F, texCoords);
+       glTexCoordPointer(2, GL_FLOAT, kQuadSize, (GLvoid*)(offset + diff));            
+
+#endif // ! CC_USES_VBO
+       
+       BOOL newBlend = blendFunc_.src != CC_BLEND_SRC || blendFunc_.dst != CC_BLEND_DST;
+       if( newBlend )
+               glBlendFunc( blendFunc_.src, blendFunc_.dst );
+       
+       NSAssert( particleIdx == particleCount, @"Abnormal error in particle quad");
+       glDrawElements(GL_TRIANGLES, (GLsizei) particleIdx*6, GL_UNSIGNED_SHORT, indices_);
+       
+       // restore blend state
+       if( newBlend )
+               glBlendFunc( CC_BLEND_SRC, CC_BLEND_DST );
+
+#if CC_USES_VBO
+       glBindBuffer(GL_ARRAY_BUFFER, 0);
+#endif
+
+       // restore GL default state
+       // -
+}
+
+@end
+
+
diff --git a/libs/cocos2d/CCProgressTimer.h b/libs/cocos2d/CCProgressTimer.h
new file mode 100644 (file)
index 0000000..9a07f2f
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2010 Lam Pham
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+#import <Foundation/Foundation.h>
+#import "CCSprite.h"
+
+/** Types of progress
+ @since v0.99.1
+ */
+typedef enum {
+       /// Radial Counter-Clockwise 
+       kCCProgressTimerTypeRadialCCW,
+       /// Radial ClockWise
+       kCCProgressTimerTypeRadialCW,
+       /// Horizontal Left-Right
+       kCCProgressTimerTypeHorizontalBarLR,
+       /// Horizontal Right-Left
+       kCCProgressTimerTypeHorizontalBarRL,
+       /// Vertical Bottom-top
+       kCCProgressTimerTypeVerticalBarBT,
+       /// Vertical Top-Bottom
+       kCCProgressTimerTypeVerticalBarTB,
+} CCProgressTimerType;
+
+/**
+ CCProgresstimer is a subclass of CCNode.
+ It renders the inner sprite according to the percentage.
+ The progress can be Radial, Horizontal or vertical.
+ @since v0.99.1
+ */
+@interface CCProgressTimer : CCNode
+{
+       CCProgressTimerType     type_;
+       float                           percentage_;
+       CCSprite                        *sprite_;
+       
+       int                                     vertexDataCount_;
+       ccV2F_C4B_T2F           *vertexData_;
+}
+
+/**    Change the percentage to change progress. */
+@property (nonatomic, readwrite) CCProgressTimerType type;
+
+/** Percentages are from 0 to 100 */
+@property (nonatomic, readwrite) float percentage;
+
+/** The image to show the progress percentage */
+@property (nonatomic, readwrite, retain) CCSprite *sprite;
+
+
+/** Creates a progress timer with an image filename as the shape the timer goes through */
++ (id) progressWithFile:(NSString*) filename;
+/** Initializes  a progress timer with an image filename as the shape the timer goes through */
+- (id) initWithFile:(NSString*) filename;
+
+/** Creates a progress timer with the texture as the shape the timer goes through */
++ (id) progressWithTexture:(CCTexture2D*) texture;
+/** Creates a progress timer with the texture as the shape the timer goes through */
+- (id) initWithTexture:(CCTexture2D*) texture;
+
+@end
diff --git a/libs/cocos2d/CCProgressTimer.m b/libs/cocos2d/CCProgressTimer.m
new file mode 100644 (file)
index 0000000..ca11a80
--- /dev/null
@@ -0,0 +1,491 @@
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2010 Lam Pham
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+#import "CCProgressTimer.h"
+
+#import "ccMacros.h"
+#import "CCTextureCache.h"
+#import "Support/CGPointExtension.h"
+
+
+
+#define kProgressTextureCoordsCount 4
+//  kProgressTextureCoords holds points {0,0} {0,1} {1,1} {1,0} we can represent it as bits
+const char kProgressTextureCoords = 0x1e;
+
+@interface CCProgressTimer (Internal)
+
+-(void)updateProgress;
+-(void)updateBar;
+-(void)updateRadial;
+-(void)updateColor;
+-(CGPoint)boundaryTexCoord:(char)index;
+@end
+
+
+@implementation CCProgressTimer
+@synthesize percentage = percentage_;
+@synthesize sprite = sprite_;
+@synthesize type = type_;
+
++(id)progressWithFile:(NSString*) filename
+{
+       return [[[self alloc]initWithFile:filename] autorelease];
+}
+-(id)initWithFile:(NSString*) filename
+{
+       return [self initWithTexture:[[CCTextureCache sharedTextureCache] addImage: filename]];
+}
+
++(id)progressWithTexture:(CCTexture2D*) texture
+{
+       return [[[self alloc]initWithTexture:texture] autorelease];
+}
+-(id)initWithTexture:(CCTexture2D*) texture
+{
+       if(( self = [super init] )){
+               self.sprite = [CCSprite spriteWithTexture:texture];
+               percentage_ = 0.f;
+               vertexData_ = NULL;
+               vertexDataCount_ = 0;
+               self.anchorPoint = ccp(.5f,.5f);
+               self.contentSize = sprite_.contentSize;
+               self.type = kCCProgressTimerTypeRadialCCW;
+       }
+       return self;
+}
+-(void)dealloc
+{
+       if(vertexData_)
+               free(vertexData_);
+       
+       [sprite_ release];
+       [super dealloc];
+}
+
+-(void)setPercentage:(float) percentage
+{
+       if(percentage_ != percentage) {
+        percentage_ = clampf( percentage, 0, 100);             
+               [self updateProgress];
+       }
+}
+-(void)setSprite:(CCSprite *)newSprite
+{
+       if(sprite_ != newSprite){
+               [sprite_ release]; 
+               sprite_ = [newSprite retain];
+               
+               //      Everytime we set a new sprite, we free the current vertex data
+               if(vertexData_){
+                       free(vertexData_);
+                       vertexData_ = NULL;
+                       vertexDataCount_ = 0;
+               }
+       }
+}
+-(void)setType:(CCProgressTimerType)newType
+{
+       if (newType != type_) {
+               
+               //      release all previous information
+               if(vertexData_){
+                       free(vertexData_);
+                       vertexData_ = NULL;
+                       vertexDataCount_ = 0;
+               }
+               type_ = newType;
+       }
+}
+@end
+
+@implementation CCProgressTimer(Internal)
+
+///
+//     @returns the vertex position from the texture coordinate
+///
+-(ccVertex2F)vertexFromTexCoord:(CGPoint) texCoord
+{
+       CGPoint tmp;
+       ccVertex2F ret;
+       if (sprite_.texture) {
+               CCTexture2D *texture = [sprite_ texture];
+               CGSize texSize = [texture contentSizeInPixels];
+               tmp = ccp(texSize.width * texCoord.x/texture.maxS,
+                                  texSize.height * (1 - (texCoord.y/texture.maxT)));
+       } else
+               tmp = CGPointZero;
+       
+       ret.x = tmp.x;
+       ret.y = tmp.y;
+       return ret;
+}
+
+-(void)updateColor
+{
+       GLubyte op = sprite_.opacity;
+       ccColor3B c3b = sprite_.color;
+       
+       ccColor4B color = { c3b.r, c3b.g, c3b.b, op };
+       if([sprite_.texture hasPremultipliedAlpha]){
+               color.r *= op/255;
+               color.g *= op/255;
+               color.b *= op/255;
+       }
+       
+       if(vertexData_){
+               for (int i=0; i < vertexDataCount_; ++i) {
+                       vertexData_[i].colors = color;
+               }
+       }
+}
+
+-(void)updateProgress
+{
+       switch (type_) {
+               case kCCProgressTimerTypeRadialCW:
+               case kCCProgressTimerTypeRadialCCW:
+                       [self updateRadial];
+                       break;
+               case kCCProgressTimerTypeHorizontalBarLR:
+               case kCCProgressTimerTypeHorizontalBarRL:
+               case kCCProgressTimerTypeVerticalBarBT:
+               case kCCProgressTimerTypeVerticalBarTB:
+                       [self updateBar];
+                       break;
+               default:
+                       break;
+       }
+}
+
+///
+//     Update does the work of mapping the texture onto the triangles
+//     It now doesn't occur the cost of free/alloc data every update cycle.
+//     It also only changes the percentage point but no other points if they have not
+//     been modified.
+//     
+//     It now deals with flipped texture. If you run into this problem, just use the
+//     sprite property and enable the methods flipX, flipY.
+///
+-(void)updateRadial
+{              
+       //      Texture Max is the actual max coordinates to deal with non-power of 2 textures
+       CGPoint tMax = ccp(sprite_.texture.maxS,sprite_.texture.maxT);
+       
+       //      Grab the midpoint
+       CGPoint midpoint = ccpCompMult(self.anchorPoint, tMax);
+       
+       float alpha = percentage_ / 100.f;
+       
+       //      Otherwise we can get the angle from the alpha
+       float angle = 2.f*((float)M_PI) * ( type_ == kCCProgressTimerTypeRadialCW? alpha : 1.f - alpha);
+       
+       //      We find the vector to do a hit detection based on the percentage
+       //      We know the first vector is the one @ 12 o'clock (top,mid) so we rotate 
+       //      from that by the progress angle around the midpoint pivot
+       CGPoint topMid = ccp(midpoint.x, 0.f);
+       CGPoint percentagePt = ccpRotateByAngle(topMid, midpoint, angle);
+       
+       
+       int index = 0;
+       CGPoint hit = CGPointZero;
+       
+       if (alpha == 0.f) {
+               //      More efficient since we don't always need to check intersection
+               //      If the alpha is zero then the hit point is top mid and the index is 0.
+               hit = topMid;
+               index = 0;
+       } else if (alpha == 1.f) {
+               //      More efficient since we don't always need to check intersection
+               //      If the alpha is one then the hit point is top mid and the index is 4.
+               hit = topMid;
+               index = 4;
+       } else {
+               //      We run a for loop checking the edges of the texture to find the
+               //      intersection point
+               //      We loop through five points since the top is split in half
+               
+               float min_t = FLT_MAX;
+               
+               for (int i = 0; i <= kProgressTextureCoordsCount; ++i) {
+                       int pIndex = (i + (kProgressTextureCoordsCount - 1))%kProgressTextureCoordsCount;
+                       
+                       CGPoint edgePtA = ccpCompMult([self boundaryTexCoord:i % kProgressTextureCoordsCount],tMax);
+                       CGPoint edgePtB = ccpCompMult([self boundaryTexCoord:pIndex],tMax);
+                       
+                       //      Remember that the top edge is split in half for the 12 o'clock position
+                       //      Let's deal with that here by finding the correct endpoints
+                       if(i == 0){
+                               edgePtB = ccpLerp(edgePtA,edgePtB,.5f);
+                       } else if(i == 4){
+                               edgePtA = ccpLerp(edgePtA,edgePtB,.5f);
+                       }
+                       
+                       //      s and t are returned by ccpLineIntersect
+                       float s = 0, t = 0;
+                       if(ccpLineIntersect(edgePtA, edgePtB, midpoint, percentagePt, &s, &t))
+                       {
+                               
+                               //      Since our hit test is on rays we have to deal with the top edge
+                               //      being in split in half so we have to test as a segment
+                               if ((i == 0 || i == 4)) {
+                                       //      s represents the point between edgePtA--edgePtB
+                                       if (!(0.f <= s && s <= 1.f)) {
+                                               continue;
+                                       }
+                               }
+                               //      As long as our t isn't negative we are at least finding a 
+                               //      correct hitpoint from midpoint to percentagePt.
+                               if (t >= 0.f) {
+                                       //      Because the percentage line and all the texture edges are
+                                       //      rays we should only account for the shortest intersection
+                                       if (t < min_t) {
+                                               min_t = t;
+                                               index = i;
+                                       }
+                               }
+                       }
+               }
+               
+               //      Now that we have the minimum magnitude we can use that to find our intersection
+               hit = ccpAdd(midpoint, ccpMult(ccpSub(percentagePt, midpoint),min_t));
+               
+       }
+       
+       
+       //      The size of the vertex data is the index from the hitpoint
+       //      the 3 is for the midpoint, 12 o'clock point and hitpoint position.
+       
+       BOOL sameIndexCount = YES;
+       if(vertexDataCount_ != index + 3){
+               sameIndexCount = NO;
+               if(vertexData_){
+                       free(vertexData_);
+                       vertexData_ = NULL;
+                       vertexDataCount_ = 0;
+               }
+       }
+       
+       
+       if(!vertexData_) {
+               vertexDataCount_ = index + 3;
+               vertexData_ = malloc(vertexDataCount_ * sizeof(ccV2F_C4B_T2F));
+               NSAssert( vertexData_, @"CCProgressTimer. Not enough memory");
+               
+               [self updateColor];
+       }
+       
+       if (!sameIndexCount) {
+               
+               //      First we populate the array with the midpoint, then all 
+               //      vertices/texcoords/colors of the 12 'o clock start and edges and the hitpoint
+               vertexData_[0].texCoords = (ccTex2F){midpoint.x, midpoint.y};
+               vertexData_[0].vertices = [self vertexFromTexCoord:midpoint];
+               
+               vertexData_[1].texCoords = (ccTex2F){midpoint.x, 0.f};
+               vertexData_[1].vertices = [self vertexFromTexCoord:ccp(midpoint.x, 0.f)];
+               
+               for(int i = 0; i < index; ++i){
+                       CGPoint texCoords = ccpCompMult([self boundaryTexCoord:i], tMax);
+                       
+                       vertexData_[i+2].texCoords = (ccTex2F){texCoords.x, texCoords.y};
+                       vertexData_[i+2].vertices = [self vertexFromTexCoord:texCoords];
+               }
+               
+               //      Flip the texture coordinates if set
+               if (sprite_.flipY || sprite_.flipX) {
+                       for(int i = 0; i < vertexDataCount_ - 1; ++i){
+                               if (sprite_.flipX) {
+                                       vertexData_[i].texCoords.u = tMax.x - vertexData_[i].texCoords.u;
+                               }
+                               if(sprite_.flipY){
+                                       vertexData_[i].texCoords.v = tMax.y - vertexData_[i].texCoords.v;
+                               }
+                       }
+               }
+       }
+       
+       //      hitpoint will go last
+       vertexData_[vertexDataCount_ - 1].texCoords = (ccTex2F){hit.x, hit.y};
+       vertexData_[vertexDataCount_ - 1].vertices = [self vertexFromTexCoord:hit];
+       
+       if (sprite_.flipY || sprite_.flipX) {
+               if (sprite_.flipX) {
+                       vertexData_[vertexDataCount_ - 1].texCoords.u = tMax.x - vertexData_[vertexDataCount_ - 1].texCoords.u;
+               }
+               if(sprite_.flipY){
+                       vertexData_[vertexDataCount_ - 1].texCoords.v = tMax.y - vertexData_[vertexDataCount_ - 1].texCoords.v;
+               }
+       }
+}
+
+///
+//     Update does the work of mapping the texture onto the triangles for the bar
+//     It now doesn't occur the cost of free/alloc data every update cycle.
+//     It also only changes the percentage point but no other points if they have not
+//     been modified.
+//     
+//     It now deals with flipped texture. If you run into this problem, just use the
+//     sprite property and enable the methods flipX, flipY.
+///
+-(void)updateBar
+{      
+       
+       float alpha = percentage_ / 100.f;
+       
+       CGPoint tMax = ccp(sprite_.texture.maxS,sprite_.texture.maxT);
+       
+       unsigned char vIndexes[2] = {0,0};
+       unsigned char index = 0;
+       
+       //      We know vertex data is always equal to the 4 corners
+       //      If we don't have vertex data then we create it here and populate
+       //      the side of the bar vertices that won't ever change.
+       if (!vertexData_) {
+               vertexDataCount_ = kProgressTextureCoordsCount;
+               vertexData_ = malloc(vertexDataCount_ * sizeof(ccV2F_C4B_T2F));
+               NSAssert( vertexData_, @"CCProgressTimer. Not enough memory");
+               
+               if(type_ == kCCProgressTimerTypeHorizontalBarLR){
+                       vertexData_[vIndexes[0] = 0].texCoords = (ccTex2F){0,0};
+                       vertexData_[vIndexes[1] = 1].texCoords = (ccTex2F){0, tMax.y};
+               }else if (type_ == kCCProgressTimerTypeHorizontalBarRL) {
+                       vertexData_[vIndexes[0] = 2].texCoords = (ccTex2F){tMax.x, tMax.y};
+                       vertexData_[vIndexes[1] = 3].texCoords = (ccTex2F){tMax.x, 0.f};
+               }else if (type_ == kCCProgressTimerTypeVerticalBarBT) {
+                       vertexData_[vIndexes[0] = 1].texCoords = (ccTex2F){0, tMax.y};
+                       vertexData_[vIndexes[1] = 3].texCoords = (ccTex2F){tMax.x, tMax.y};
+               }else if (type_ == kCCProgressTimerTypeVerticalBarTB) {
+                       vertexData_[vIndexes[0] = 0].texCoords = (ccTex2F){0, 0};
+                       vertexData_[vIndexes[1] = 2].texCoords = (ccTex2F){tMax.x, 0};
+               }
+               
+               index = vIndexes[0];
+               vertexData_[index].vertices = [self vertexFromTexCoord:ccp(vertexData_[index].texCoords.u, vertexData_[index].texCoords.v)];
+               
+               index = vIndexes[1];
+               vertexData_[index].vertices = [self vertexFromTexCoord:ccp(vertexData_[index].texCoords.u, vertexData_[index].texCoords.v)];
+               
+               if (sprite_.flipY || sprite_.flipX) {
+                       if (sprite_.flipX) {
+                               index = vIndexes[0];
+                               vertexData_[index].texCoords.u = tMax.x - vertexData_[index].texCoords.u;
+                               index = vIndexes[1];
+                               vertexData_[index].texCoords.u = tMax.x - vertexData_[index].texCoords.u;
+                       }
+                       if(sprite_.flipY){
+                               index = vIndexes[0];
+                               vertexData_[index].texCoords.v = tMax.y - vertexData_[index].texCoords.v;
+                               index = vIndexes[1];
+                               vertexData_[index].texCoords.v = tMax.y - vertexData_[index].texCoords.v;
+                       }
+               }
+               
+               [self updateColor];
+       }
+       
+       if(type_ == kCCProgressTimerTypeHorizontalBarLR){
+               vertexData_[vIndexes[0] = 3].texCoords = (ccTex2F){tMax.x*alpha, tMax.y};
+               vertexData_[vIndexes[1] = 2].texCoords = (ccTex2F){tMax.x*alpha, 0};
+       }else if (type_ == kCCProgressTimerTypeHorizontalBarRL) {
+               vertexData_[vIndexes[0] = 1].texCoords = (ccTex2F){tMax.x*(1.f - alpha), 0};
+               vertexData_[vIndexes[1] = 0].texCoords = (ccTex2F){tMax.x*(1.f - alpha), tMax.y};
+       }else if (type_ == kCCProgressTimerTypeVerticalBarBT) {
+               vertexData_[vIndexes[0] = 0].texCoords = (ccTex2F){0, tMax.y*(1.f - alpha)};
+               vertexData_[vIndexes[1] = 2].texCoords = (ccTex2F){tMax.x, tMax.y*(1.f - alpha)};
+       }else if (type_ == kCCProgressTimerTypeVerticalBarTB) {
+               vertexData_[vIndexes[0] = 1].texCoords = (ccTex2F){0, tMax.y*alpha};
+               vertexData_[vIndexes[1] = 3].texCoords = (ccTex2F){tMax.x, tMax.y*alpha};
+       }
+       
+       index = vIndexes[0];
+       vertexData_[index].vertices = [self vertexFromTexCoord:ccp(vertexData_[index].texCoords.u, vertexData_[index].texCoords.v)];
+       index = vIndexes[1];
+       vertexData_[index].vertices = [self vertexFromTexCoord:ccp(vertexData_[index].texCoords.u, vertexData_[index].texCoords.v)];
+       
+       if (sprite_.flipY || sprite_.flipX) {
+               if (sprite_.flipX) {
+                       index = vIndexes[0];
+                       vertexData_[index].texCoords.u = tMax.x - vertexData_[index].texCoords.u;
+                       index = vIndexes[1];
+                       vertexData_[index].texCoords.u = tMax.x - vertexData_[index].texCoords.u;
+               }
+               if(sprite_.flipY){
+                       index = vIndexes[0];
+                       vertexData_[index].texCoords.v = tMax.y - vertexData_[index].texCoords.v;
+                       index = vIndexes[1];
+                       vertexData_[index].texCoords.v = tMax.y - vertexData_[index].texCoords.v;
+               }
+       }
+       
+}
+
+-(CGPoint)boundaryTexCoord:(char)index
+{
+       if (index < kProgressTextureCoordsCount) {
+               switch (type_) {
+                       case kCCProgressTimerTypeRadialCW:
+                               return ccp((kProgressTextureCoords>>((index<<1)+1))&1,(kProgressTextureCoords>>(index<<1))&1);
+                       case kCCProgressTimerTypeRadialCCW:
+                               return ccp((kProgressTextureCoords>>(7-(index<<1)))&1,(kProgressTextureCoords>>(7-((index<<1)+1)))&1);
+                       default:
+                               break;
+               }
+       }
+       return CGPointZero;
+}
+
+-(void)draw
+{
+       if(!vertexData_)return;
+       if(!sprite_)return;
+       ccBlendFunc blendFunc = sprite_.blendFunc;
+       BOOL newBlend = blendFunc.src != CC_BLEND_SRC || blendFunc.dst != CC_BLEND_DST;
+       if( newBlend )
+               glBlendFunc( blendFunc.src, blendFunc.dst );
+       
+       ///     ========================================================================
+       //      Replaced [texture_ drawAtPoint:CGPointZero] with my own vertexData
+       //      Everything above me and below me is copied from CCTextureNode's draw
+       glBindTexture(GL_TEXTURE_2D, sprite_.texture.name);
+       glVertexPointer(2, GL_FLOAT, sizeof(ccV2F_C4B_T2F), &vertexData_[0].vertices);
+       glTexCoordPointer(2, GL_FLOAT, sizeof(ccV2F_C4B_T2F), &vertexData_[0].texCoords);
+       glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(ccV2F_C4B_T2F), &vertexData_[0].colors);
+       if(type_ == kCCProgressTimerTypeRadialCCW || type_ == kCCProgressTimerTypeRadialCW){
+               glDrawArrays(GL_TRIANGLE_FAN, 0, vertexDataCount_);
+       } else if (type_ == kCCProgressTimerTypeHorizontalBarLR ||
+                          type_ == kCCProgressTimerTypeHorizontalBarRL ||
+                          type_ == kCCProgressTimerTypeVerticalBarBT ||
+                          type_ == kCCProgressTimerTypeVerticalBarTB) {
+               glDrawArrays(GL_TRIANGLE_STRIP, 0, vertexDataCount_);
+       }
+       //glDrawElements(GL_TRIANGLES, indicesCount_, GL_UNSIGNED_BYTE, indices_);
+       ///     ========================================================================
+       
+       if( newBlend )
+               glBlendFunc(CC_BLEND_SRC, CC_BLEND_DST);
+}
+
+@end
diff --git a/libs/cocos2d/CCProtocols.h b/libs/cocos2d/CCProtocols.h
new file mode 100644 (file)
index 0000000..8046cb2
--- /dev/null
@@ -0,0 +1,125 @@
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2008-2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+
+#import "ccTypes.h"
+#import "CCTexture2D.h"
+
+#pragma mark -
+#pragma mark CCRGBAProtocol
+
+/// CC RGBA protocol
+@protocol CCRGBAProtocol <NSObject>
+/** sets Color
+ @since v0.8
+ */
+-(void) setColor:(ccColor3B)color;
+/** returns the color
+ @since v0.8
+ */
+-(ccColor3B) color;
+
+/// returns the opacity
+-(GLubyte) opacity;
+/** sets the opacity.
+ @warning If the the texture has premultiplied alpha then, the R, G and B channels will be modifed.
+ Values goes from 0 to 255, where 255 means fully opaque.
+ */
+-(void) setOpacity: (GLubyte) opacity;
+@optional
+/** sets the premultipliedAlphaOpacity property.
+ If set to NO then opacity will be applied as: glColor(R,G,B,opacity);
+ If set to YES then oapcity will be applied as: glColor(opacity, opacity, opacity, opacity );
+ Textures with premultiplied alpha will have this property by default on YES. Otherwise the default value is NO
+ @since v0.8
+ */
+-(void) setOpacityModifyRGB:(BOOL)boolean;
+/** returns whether or not the opacity will be applied using glColor(R,G,B,opacity) or glColor(opacity, opacity, opacity, opacity);
+ @since v0.8
+ */
+ -(BOOL) doesOpacityModifyRGB;
+@end
+
+#pragma mark -
+#pragma mark CCBlendProtocol
+/**
+ You can specify the blending fuction.
+ @since v0.99.0
+ */
+@protocol CCBlendProtocol <NSObject>
+/** set the source blending function for the texture */
+-(void) setBlendFunc:(ccBlendFunc)blendFunc;
+/** returns the blending function used for the texture */
+-(ccBlendFunc) blendFunc;
+@end
+
+
+#pragma mark -
+#pragma mark CCTextureProtocol
+
+/** CCNode objects that uses a Texture2D to render the images.
+ The texture can have a blending function.
+ If the texture has alpha premultiplied the default blending function is:
+    src=GL_ONE dst= GL_ONE_MINUS_SRC_ALPHA
+ else
+       src=GL_SRC_ALPHA dst= GL_ONE_MINUS_SRC_ALPHA
+ But you can change the blending funtion at any time.
+ @since v0.8.0
+ */
+@protocol CCTextureProtocol <CCBlendProtocol>
+/** returns the used texture */
+-(CCTexture2D*) texture;
+/** sets a new texture. it will be retained */
+-(void) setTexture:(CCTexture2D*)texture;
+@end
+
+#pragma mark -
+#pragma mark CCLabelProtocol
+/** Common interface for Labels */
+@protocol CCLabelProtocol <NSObject>
+/** sets a new label using an NSString.
+ The string will be copied.
+ */
+-(void) setString:(NSString*)label;
+/** returns the string that is rendered */
+-(NSString*) string;
+@optional
+/** sets a new label using a CString.
+ It is faster than setString since it doesn't require to alloc/retain/release an NString object.
+ @since v0.99.0
+ */
+-(void) setCString:(char*)label;
+@end
+
+
+#pragma mark -
+#pragma mark CCProjectionProtocol
+/** OpenGL projection protocol */
+@protocol CCProjectionProtocol <NSObject>
+/** Called by CCDirector when the porjection is updated, and "custom" projection is used
+ @since v0.99.5
+ */
+-(void) updateProjection;
+@end
\ No newline at end of file
diff --git a/libs/cocos2d/CCRenderTexture.h b/libs/cocos2d/CCRenderTexture.h
new file mode 100644 (file)
index 0000000..d5e39cc
--- /dev/null
@@ -0,0 +1,108 @@
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2009 Jason Booth
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+#import <Foundation/Foundation.h>
+#import "CCNode.h"
+#import "CCSprite.h"
+#import "Support/OpenGL_Internal.h"
+
+#import <Availability.h>
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+#import <UIKit/UIKit.h>
+#endif // iPHone
+
+enum  
+{
+       kCCImageFormatJPG = 0,
+       kCCImageFormatPNG = 1,
+       kCCImageFormatRawData =2
+};
+
+
+/**
+ CCRenderTexture is a generic rendering target. To render things into it,
+ simply construct a render target, call begin on it, call visit on any cocos
+ scenes or objects to render them, and call end. For convienience, render texture
+ adds a sprite as it's display child with the results, so you can simply add
+ the render texture to your scene and treat it like any other CocosNode.
+ There are also functions for saving the render texture to disk in PNG or JPG format.
+ @since v0.8.1
+ */
+@interface CCRenderTexture : CCNode 
+{
+       GLuint                          fbo_;
+       GLint                           oldFBO_;
+       CCTexture2D*            texture_;
+       CCSprite*                       sprite_;
+       
+       GLenum                          pixelFormat_;
+}
+
+/** The CCSprite being used.
+ The sprite, by default, will use the following blending function: GL_ONE, GL_ONE_MINUS_SRC_ALPHA.
+ The blending function can be changed in runtime by calling:
+       - [[renderTexture sprite] setBlendFunc:(ccBlendFunc){GL_ONE, GL_ONE_MINUS_SRC_ALPHA}];
+*/
+@property (nonatomic,readwrite, assign) CCSprite* sprite;
+
+/** creates a RenderTexture object with width and height in Points and a pixel format, only RGB and RGBA formats are valid */
++(id)renderTextureWithWidth:(int)w height:(int)h pixelFormat:(CCTexture2DPixelFormat) format;
+
+/** creates a RenderTexture object with width and height in Points, pixel format is RGBA8888 */
++(id)renderTextureWithWidth:(int)w height:(int)h;
+
+/** initializes a RenderTexture object with width and height in Points and a pixel format, only RGB and RGBA formats are valid */
+-(id)initWithWidth:(int)w height:(int)h pixelFormat:(CCTexture2DPixelFormat) format;
+
+/** starts grabbing */
+-(void)begin;
+
+/** starts rendering to the texture while clearing the texture first.
+ This is more efficient then calling -clear first and then -begin */
+-(void)beginWithClear:(float)r g:(float)g b:(float)b a:(float)a;
+
+/** ends grabbing */
+-(void)end;
+
+/** clears the texture with a color */
+-(void)clear:(float)r g:(float)g b:(float)b a:(float)a;
+
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+
+/** saves the texture into a file */
+-(BOOL)saveBuffer:(NSString*)name;
+/** saves the texture into a file. The format can be JPG or PNG */
+-(BOOL)saveBuffer:(NSString*)name format:(int)format;
+/* get buffer as UIImage, can only save a render buffer which has a RGBA8888 pixel format */
+-(NSData*)getUIImageAsDataFromBuffer:(int) format;
+/* get buffer as UIImage */
+-(UIImage *)getUIImageFromBuffer;
+
+#endif // __IPHONE_OS_VERSION_MAX_ALLOWED
+
+@end
+
+
diff --git a/libs/cocos2d/CCRenderTexture.m b/libs/cocos2d/CCRenderTexture.m
new file mode 100644 (file)
index 0000000..4a4768e
--- /dev/null
@@ -0,0 +1,340 @@
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2009 Jason Booth
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+#import <Availability.h>
+#import "CCRenderTexture.h"
+#import "CCDirector.h"
+#import "ccMacros.h"
+#import "Support/ccUtils.h"
+#import "Support/CCFileUtils.h"
+
+@implementation CCRenderTexture
+
+@synthesize sprite=sprite_;
+
+// issue #994 
++(id)renderTextureWithWidth:(int)w height:(int)h pixelFormat:(CCTexture2DPixelFormat) format
+{
+       return [[[self alloc] initWithWidth:w height:h pixelFormat:format] autorelease];
+}
+
++(id)renderTextureWithWidth:(int)w height:(int)h
+{
+       return [[[self alloc] initWithWidth:w height:h pixelFormat:kCCTexture2DPixelFormat_RGBA8888] autorelease];      
+}
+
+-(id)initWithWidth:(int)w height:(int)h 
+{
+       return [self initWithWidth:w height:h pixelFormat:kCCTexture2DPixelFormat_RGBA8888];
+}
+
+-(id)initWithWidth:(int)w height:(int)h pixelFormat:(CCTexture2DPixelFormat) format
+{
+       if ((self = [super init]))
+       {
+               NSAssert(format != kCCTexture2DPixelFormat_A8,@"only RGB and RGBA formats are valid for a render texture");
+               
+               w *= CC_CONTENT_SCALE_FACTOR();
+               h *= CC_CONTENT_SCALE_FACTOR();
+
+               glGetIntegerv(CC_GL_FRAMEBUFFER_BINDING, &oldFBO_);
+               
+               // textures must be power of two
+               NSUInteger powW = ccNextPOT(w);
+               NSUInteger powH = ccNextPOT(h);
+               
+               void *data = malloc((int)(powW * powH * 4));
+               memset(data, 0, (int)(powW * powH * 4));
+               pixelFormat_=format; 
+               
+               texture_ = [[CCTexture2D alloc] initWithData:data pixelFormat:pixelFormat_ pixelsWide:powW pixelsHigh:powH contentSize:CGSizeMake(w, h)];
+               free( data );
+    
+               // generate FBO
+               ccglGenFramebuffers(1, &fbo_);
+               ccglBindFramebuffer(CC_GL_FRAMEBUFFER, fbo_);
+    
+               // associate texture with FBO
+               ccglFramebufferTexture2D(CC_GL_FRAMEBUFFER, CC_GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture_.name, 0);
+    
+               // check if it worked (probably worth doing :) )
+               GLuint status = ccglCheckFramebufferStatus(CC_GL_FRAMEBUFFER);
+               if (status != CC_GL_FRAMEBUFFER_COMPLETE)
+               {
+                       [NSException raise:@"Render Texture" format:@"Could not attach texture to framebuffer"];
+               }
+               [texture_ setAliasTexParameters];
+               
+               sprite_ = [CCSprite spriteWithTexture:texture_];
+               
+               [texture_ release];
+               [sprite_ setScaleY:-1];
+               [self addChild:sprite_];
+
+               // issue #937
+               [sprite_ setBlendFunc:(ccBlendFunc){GL_ONE, GL_ONE_MINUS_SRC_ALPHA}];
+
+               ccglBindFramebuffer(CC_GL_FRAMEBUFFER, oldFBO_);
+       }
+       return self;
+}
+
+-(void)dealloc
+{
+//     [self removeAllChildrenWithCleanup:YES];
+       ccglDeleteFramebuffers(1, &fbo_);
+       [super dealloc];
+}
+
+-(void)begin
+{
+       // Save the current matrix
+       glPushMatrix();
+       
+       CGSize texSize = [texture_ contentSizeInPixels];
+       
+       
+       // Calculate the adjustment ratios based on the old and new projections
+       CGSize size = [[CCDirector sharedDirector] displaySizeInPixels];
+       float widthRatio = size.width / texSize.width;
+       float heightRatio = size.height / texSize.height;
+       
+       
+       // Adjust the orthographic propjection and viewport
+       ccglOrtho((float)-1.0 / widthRatio,  (float)1.0 / widthRatio, (float)-1.0 / heightRatio, (float)1.0 / heightRatio, -1,1);
+       glViewport(0, 0, texSize.width, texSize.height);
+       
+       
+       glGetIntegerv(CC_GL_FRAMEBUFFER_BINDING, &oldFBO_);
+       ccglBindFramebuffer(CC_GL_FRAMEBUFFER, fbo_);//Will direct drawing to the frame buffer created above
+       
+       // Issue #1145
+       // There is no need to enable the default GL states here
+       // but since CCRenderTexture is mostly used outside the "render" loop
+       // these states needs to be enabled.
+       // Since this bug was discovered in API-freeze (very close of 1.0 release)
+       // This bug won't be fixed to prevent incompatibilities with code.
+       // 
+       // If you understand the above mentioned message, then you can comment the following line
+       // and enable the gl states manually, in case you need them.
+       CC_ENABLE_DEFAULT_GL_STATES();
+}
+
+-(void)beginWithClear:(float)r g:(float)g b:(float)b a:(float)a
+{
+       [self begin];
+
+       // save clear color
+       GLfloat clearColor[4];
+       glGetFloatv(GL_COLOR_CLEAR_VALUE,clearColor); 
+
+       glClearColor(r, g, b, a);
+       glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+       // restore clear color
+       glClearColor(clearColor[0], clearColor[1], clearColor[2], clearColor[3]);
+}
+
+-(void)end
+{
+       ccglBindFramebuffer(CC_GL_FRAMEBUFFER, oldFBO_);
+       // Restore the original matrix and viewport
+       glPopMatrix();
+       CGSize size = [[CCDirector sharedDirector] displaySizeInPixels];
+       glViewport(0, 0, size.width, size.height);
+}
+
+-(void)clear:(float)r g:(float)g b:(float)b a:(float)a
+{
+       [self beginWithClear:r g:g b:b a:a];
+       [self end];
+}
+
+#pragma mark RenderTexture - Save Image
+
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+-(BOOL)saveBuffer:(NSString*)name
+{
+       return [self saveBuffer:name format:kCCImageFormatJPG];
+}
+
+-(BOOL)saveBuffer:(NSString*)fileName format:(int)format
+{
+    NSString *fullPath = [CCFileUtils fullPathFromRelativePath:fileName];
+       
+       NSData *data = [self getUIImageAsDataFromBuffer:format];
+       
+       return [data writeToFile:fullPath atomically:YES];
+}
+
+/* get buffer as UIImage */
+-(UIImage *)getUIImageFromBuffer
+{
+    NSAssert(pixelFormat_ == kCCTexture2DPixelFormat_RGBA8888,@"only RGBA8888 can be saved as image");
+       
+       CGSize s = [texture_ contentSizeInPixels];
+       int tx = s.width;
+       int ty = s.height;
+       
+       int bitsPerComponent                    = 8;
+       int bitsPerPixel                                = 32;
+       int bytesPerPixel                               = (bitsPerComponent * 4)/8;
+       int bytesPerRow                                 = bytesPerPixel * tx;
+       NSInteger myDataLength                  = bytesPerRow * ty;
+       
+       NSMutableData *buffer   = [[NSMutableData alloc] initWithCapacity:myDataLength];
+       NSMutableData *pixels   = [[NSMutableData alloc] initWithCapacity:myDataLength];
+       
+       if( ! (buffer && pixels) ) {
+               CCLOG(@"cocos2d: CCRenderTexture#getUIImageFromBuffer: not enough memory");
+               [buffer release];
+               [pixels release];
+               return nil;
+       }
+       
+       [self begin];
+       glReadPixels(0,0,tx,ty,GL_RGBA,GL_UNSIGNED_BYTE, [buffer mutableBytes]);
+       [self end];
+       
+       // make data provider with data.
+       
+       CGBitmapInfo bitmapInfo = kCGImageAlphaPremultipliedLast | kCGBitmapByteOrderDefault;
+       CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, [buffer mutableBytes], myDataLength, NULL);
+       CGColorSpaceRef colorSpaceRef = CGColorSpaceCreateDeviceRGB();
+       CGImageRef iref = CGImageCreate(tx, ty,
+                                                                       bitsPerComponent, bitsPerPixel, bytesPerRow,
+                                                                       colorSpaceRef, bitmapInfo, provider,
+                                                                       NULL, false,
+                                                                       kCGRenderingIntentDefault);
+       
+       CGContextRef context = CGBitmapContextCreate([pixels mutableBytes], tx,
+                                                                                                ty, CGImageGetBitsPerComponent(iref),
+                                                                                                CGImageGetBytesPerRow(iref), CGImageGetColorSpace(iref),
+                                                                                                bitmapInfo);
+       CGContextTranslateCTM(context, 0.0f, ty);
+       CGContextScaleCTM(context, 1.0f, -1.0f);
+       CGContextDrawImage(context, CGRectMake(0.0f, 0.0f, tx, ty), iref);
+       CGImageRef outputRef = CGBitmapContextCreateImage(context);
+       UIImage* image  = [[UIImage alloc] initWithCGImage:outputRef];
+       
+       CGImageRelease(iref);
+       CGContextRelease(context);
+       CGColorSpaceRelease(colorSpaceRef);
+       CGDataProviderRelease(provider);
+       CGImageRelease(outputRef);
+       
+       [pixels release];
+       [buffer release];
+       
+       return [image autorelease];
+}
+
+-(NSData*)getUIImageAsDataFromBuffer:(int) format
+{
+       NSAssert(pixelFormat_ == kCCTexture2DPixelFormat_RGBA8888,@"only RGBA8888 can be saved as image");
+       
+       CGSize s = [texture_ contentSizeInPixels];
+       int tx = s.width;
+       int ty = s.height;
+       
+       int bitsPerComponent=8;                 
+       int bitsPerPixel=32;                            
+       
+       int bytesPerRow                                 = (bitsPerPixel/8) * tx;
+       NSInteger myDataLength                  = bytesPerRow * ty;
+       
+       GLubyte *buffer = malloc(sizeof(GLubyte)*myDataLength);
+       GLubyte *pixels = malloc(sizeof(GLubyte)*myDataLength);
+       
+       if( ! (buffer && pixels) ) {
+               CCLOG(@"cocos2d: CCRenderTexture#getUIImageFromBuffer: not enough memory");
+               free(buffer);
+               free(pixels);
+               return nil;
+       }
+       
+       [self begin];
+       glReadPixels(0,0,tx,ty,GL_RGBA,GL_UNSIGNED_BYTE, buffer);
+       [self end];
+       
+       int x,y;
+       
+       for(y = 0; y <ty; y++) {
+               for(x = 0; x <tx * 4; x++) {
+                       pixels[((ty - 1 - y) * tx * 4 + x)] = buffer[(y * 4 * tx + x)];
+               }
+       }
+       
+       NSData* data;
+       
+       if (format == kCCImageFormatRawData)
+       {
+               free(buffer);
+               //data frees buffer when it is deallocated
+               data = [NSData dataWithBytesNoCopy:pixels length:myDataLength];
+               
+       } else {
+               
+               /*
+                CGImageCreate(size_t width, size_t height,
+                size_t bitsPerComponent, size_t bitsPerPixel, size_t bytesPerRow,
+                CGColorSpaceRef space, CGBitmapInfo bitmapInfo, CGDataProviderRef provider,
+                const CGFloat decode[], bool shouldInterpolate,
+                CGColorRenderingIntent intent)
+                */
+               // make data provider with data.
+               CGBitmapInfo bitmapInfo = kCGImageAlphaPremultipliedLast | kCGBitmapByteOrderDefault;
+               CGDataProviderRef provider              = CGDataProviderCreateWithData(NULL, pixels, myDataLength, NULL);
+               CGColorSpaceRef colorSpaceRef   = CGColorSpaceCreateDeviceRGB();
+               CGImageRef iref                                 = CGImageCreate(tx, ty,
+                                                                                                               bitsPerComponent, bitsPerPixel, bytesPerRow,
+                                                                                                               colorSpaceRef, bitmapInfo, provider,
+                                                                                                               NULL, false,
+                                                                                                               kCGRenderingIntentDefault);
+               
+               UIImage* image                                  = [[UIImage alloc] initWithCGImage:iref];
+               
+               CGImageRelease(iref);   
+               CGColorSpaceRelease(colorSpaceRef);
+               CGDataProviderRelease(provider);
+               
+               
+               
+               if (format == kCCImageFormatPNG)
+                       data = UIImagePNGRepresentation(image);
+               else
+                       data = UIImageJPEGRepresentation(image, 1.0f);
+               
+               [image release];
+               
+               free(pixels);
+               free(buffer);
+       }
+       
+       return data;
+}
+
+#endif // __IPHONE_OS_VERSION_MAX_ALLOWED
+@end
diff --git a/libs/cocos2d/CCRibbon.h b/libs/cocos2d/CCRibbon.h
new file mode 100644 (file)
index 0000000..53898e6
--- /dev/null
@@ -0,0 +1,117 @@
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2008, 2009 Jason Booth
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+
+#import "CCNode.h"
+#import "CCTexture2D.h"
+#import "CCProtocols.h"
+#import "Platforms/CCGL.h"
+
+/**
+ * A CCRibbon is a dynamically generated list of polygons drawn as a single or series
+ * of triangle strips. The primary use of CCRibbon is as the drawing class of Motion Streak,
+ * but it is quite useful on it's own. When manually drawing a ribbon, you can call addPointAt
+ * and pass in the parameters for the next location in the ribbon. The system will automatically
+ * generate new polygons, texture them accourding to your texture width, etc, etc.
+ *
+ * CCRibbon data is stored in a CCRibbonSegment class. This class statically allocates enough verticies and
+ * texture coordinates for 50 locations (100 verts or 48 triangles). The ribbon class will allocate
+ * new segments when they are needed, and reuse old ones if available. The idea is to avoid constantly
+ * allocating new memory and prefer a more static method. However, since there is no way to determine
+ * the maximum size of some ribbons (motion streaks), a truely static allocation is not possible.
+ *
+ * @since v0.8.1
+ */
+@interface CCRibbon : CCNode <CCTextureProtocol>
+{
+       NSMutableArray* segments_;
+       NSMutableArray* deletedSegments_;
+
+       CGPoint                 lastPoint1_;
+       CGPoint                 lastPoint2_;
+       CGPoint                 lastLocation_;
+       int                                     vertCount_;
+       float                           texVPos_;
+       float                           curTime_;
+       float                           fadeTime_;
+       float                           delta_;
+       float                           lastWidth_;
+       float                           lastSign_;
+       BOOL                            pastFirstPoint_;
+
+       // Texture used
+       CCTexture2D*            texture_;
+
+       // texture length
+       float                   textureLength_;
+
+       // RGBA protocol
+       ccColor4B color_;
+
+       // blend func
+       ccBlendFunc             blendFunc_;
+}
+
+/** Texture used by the ribbon. Conforms to CCTextureProtocol protocol */
+@property (nonatomic,readwrite,retain) CCTexture2D* texture;
+
+/** Texture lengths in pixels */
+@property (nonatomic,readwrite) float textureLength;
+
+/** GL blendind function */
+@property (nonatomic,readwrite,assign) ccBlendFunc blendFunc;
+
+/** color used by the Ribbon (RGBA) */
+@property (nonatomic,readwrite) ccColor4B color;
+
+/** creates the ribbon */
++(id)ribbonWithWidth:(float)w image:(NSString*)path length:(float)l color:(ccColor4B)color fade:(float)fade;
+/** init the ribbon */
+-(id)initWithWidth:(float)w image:(NSString*)path length:(float)l color:(ccColor4B)color fade:(float)fade;
+/** add a point to the ribbon */
+-(void)addPointAt:(CGPoint)location width:(float)w;
+/** polling function */
+-(void)update:(ccTime)delta;
+/** determine side of line */
+-(float)sideOfLine:(CGPoint)p l1:(CGPoint)l1 l2:(CGPoint)l2;
+
+@end
+
+/** object to hold ribbon segment data */
+@interface CCRibbonSegment : NSObject
+{
+@public
+       GLfloat verts[50*6];
+       GLfloat coords[50*4];
+       GLubyte colors[50*8];
+       float           creationTime[50];
+       BOOL            finished;
+       uint            end;
+       uint            begin;
+}
+-(id)init;
+-(void)reset;
+-(void)draw:(float)curTime fadeTime:(float)fadeTime color:(ccColor4B)color;
+@end
diff --git a/libs/cocos2d/CCRibbon.m b/libs/cocos2d/CCRibbon.m
new file mode 100644 (file)
index 0000000..9555455
--- /dev/null
@@ -0,0 +1,381 @@
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2008, 2009 Jason Booth
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+/*
+ * A ribbon is a dynamically generated list of polygons drawn as a single or series
+ * of triangle strips. The primary use of Ribbon is as the drawing class of Motion Streak,
+ * but it is quite useful on it's own. When manually drawing a ribbon, you can call addPointAt
+ * and pass in the parameters for the next location in the ribbon. The system will automatically
+ * generate new polygons, texture them accourding to your texture width, etc, etc.
+ *
+ * Ribbon data is stored in a RibbonSegment class. This class statically allocates enough verticies and
+ * texture coordinates for 50 locations (100 verts or 48 triangles). The ribbon class will allocate
+ * new segments when they are needed, and reuse old ones if available. The idea is to avoid constantly
+ * allocating new memory and prefer a more static method. However, since there is no way to determine
+ * the maximum size of some ribbons (motion streaks), a truely static allocation is not possible.
+ *
+ */
+
+
+#import "CCRibbon.h"
+#import "CCTextureCache.h"
+#import "Support/CGPointExtension.h"
+#import "ccMacros.h"
+
+//
+// Ribbon
+//
+@implementation CCRibbon
+@synthesize blendFunc=blendFunc_;
+@synthesize color=color_;
+@synthesize textureLength = textureLength_;
+
++(id)ribbonWithWidth:(float)w image:(NSString*)path length:(float)l color:(ccColor4B)color fade:(float)fade
+{
+       self = [[[self alloc] initWithWidth:w image:path length:l color:color fade:fade] autorelease];
+       return self;
+}
+
+-(id)initWithWidth:(float)w image:(NSString*)path length:(float)l color:(ccColor4B)color fade:(float)fade
+{
+       self = [super init];
+       if (self)
+       {
+               
+               segments_ = [[NSMutableArray alloc] init];
+               deletedSegments_ = [[NSMutableArray alloc] init];
+
+               /* 1 initial segment */
+               CCRibbonSegment* seg = [[CCRibbonSegment alloc] init];
+               [segments_ addObject:seg];
+               [seg release];
+               
+               textureLength_ = l;
+               
+               color_ = color;
+               fadeTime_ = fade;
+               lastLocation_ = CGPointZero;
+               lastWidth_ = w/2;
+               texVPos_ = 0.0f;
+               
+               curTime_ = 0;
+               pastFirstPoint_ = NO;
+               
+               /* XXX:
+                Ribbon, by default uses this blend function, which might not be correct
+                if you are using premultiplied alpha images,
+                but 99% you might want to use this blending function regarding of the texture
+                */
+               blendFunc_.src = GL_SRC_ALPHA;
+               blendFunc_.dst = GL_ONE_MINUS_SRC_ALPHA;
+               
+               self.texture = [[CCTextureCache sharedTextureCache] addImage:path];
+
+               /* default texture parameter */
+               ccTexParams params = { GL_LINEAR, GL_LINEAR, GL_REPEAT, GL_REPEAT };
+               [texture_ setTexParameters:&params];
+       }
+       return self;
+}
+
+-(void)dealloc
+{
+       [segments_ release];
+       [deletedSegments_ release];
+       [texture_ release];
+       [super dealloc];
+}
+
+// rotates a point around 0, 0
+-(CGPoint)rotatePoint:(CGPoint)vec rotation:(float)a
+{
+       float xtemp = (vec.x * cosf(a)) - (vec.y * sinf(a));
+       vec.y = (vec.x * sinf(a)) + (vec.y * cosf(a));
+       vec.x = xtemp;
+       return vec;
+}
+
+-(void)update:(ccTime)delta
+{
+       curTime_+= delta;
+       delta_ = delta;
+}
+
+-(float)sideOfLine:(CGPoint)p l1:(CGPoint)l1 l2:(CGPoint)l2
+{
+       CGPoint vp = ccpPerp(ccpSub(l1, l2));
+       CGPoint vx = ccpSub(p, l1);
+       return ccpDot(vx, vp);
+}
+
+// adds a new segment to the ribbon
+-(void)addPointAt:(CGPoint)location width:(float)w
+{
+       location.x *= CC_CONTENT_SCALE_FACTOR();
+       location.y *= CC_CONTENT_SCALE_FACTOR();
+
+       w = w*0.5f;
+       // if this is the first point added, cache it and return
+       if (!pastFirstPoint_)
+       {
+               lastWidth_ = w;
+               lastLocation_ = location;
+               pastFirstPoint_ = YES;
+               return;
+       }
+
+       CGPoint sub = ccpSub(lastLocation_, location);
+       float r = ccpToAngle(sub) + (float)M_PI_2;
+       CGPoint p1 = ccpAdd([self rotatePoint:ccp(-w, 0) rotation:r], location);
+       CGPoint p2 = ccpAdd([self rotatePoint:ccp(w, 0) rotation:r], location);
+       float len = sqrtf(powf(lastLocation_.x - location.x, 2) + powf(lastLocation_.y - location.y, 2));
+       float tend = texVPos_ + len/textureLength_;
+       CCRibbonSegment* seg;
+       // grab last segment
+       seg = [segments_ lastObject];
+       // lets kill old segments
+       for (CCRibbonSegment* seg2 in segments_)
+       {
+               if (seg2 != seg && seg2->finished)
+               {
+                       [deletedSegments_ addObject:seg2];
+               }
+       }
+       [segments_ removeObjectsInArray:deletedSegments_];
+       // is the segment full?
+       if (seg->end >= 50)
+               [segments_ removeObjectsInArray:deletedSegments_];
+       // grab last segment and append to it if it's not full
+       seg = [segments_ lastObject];
+       // is the segment full?
+       if (seg->end >= 50)
+       {
+               CCRibbonSegment* newSeg;
+               // grab it from the cache if we can
+               if ([deletedSegments_ count] > 0)
+               {
+                       newSeg = [deletedSegments_ objectAtIndex:0];
+                       [newSeg retain];                                                        // will be released later
+                       [deletedSegments_ removeObject:newSeg];
+                       [newSeg reset];
+               }
+               else
+               {
+                       newSeg = [[CCRibbonSegment alloc] init]; // will be released later
+               }
+               
+               newSeg->creationTime[0] = seg->creationTime[seg->end - 1];
+               int v = (seg->end-1)*6;
+               int c = (seg->end-1)*4; 
+               newSeg->verts[0] = seg->verts[v];
+               newSeg->verts[1] = seg->verts[v+1];
+               newSeg->verts[2] = seg->verts[v+2];
+               newSeg->verts[3] = seg->verts[v+3];
+               newSeg->verts[4] = seg->verts[v+4];
+               newSeg->verts[5] = seg->verts[v+5];
+               
+               newSeg->coords[0] = seg->coords[c];
+               newSeg->coords[1] = seg->coords[c+1];
+               newSeg->coords[2] = seg->coords[c+2];
+               newSeg->coords[3] = seg->coords[c+3];     
+               newSeg->end++;
+               seg = newSeg;
+               [segments_ addObject:seg];
+               [newSeg release];        // it was retained before
+               
+       }  
+       if (seg->end == 0)
+       {
+               // first edge has to get rotation from the first real polygon
+               CGPoint lp1 = ccpAdd([self rotatePoint:ccp(-lastWidth_, 0) rotation:r], lastLocation_);
+               CGPoint lp2 = ccpAdd([self rotatePoint:ccp(+lastWidth_, 0) rotation:r], lastLocation_);
+               seg->creationTime[0] = curTime_ - delta_;
+               seg->verts[0] = lp1.x;
+               seg->verts[1] = lp1.y;
+               seg->verts[2] = 0.0f;
+               seg->verts[3] = lp2.x;
+               seg->verts[4] = lp2.y;
+               seg->verts[5] = 0.0f;
+               seg->coords[0] = 0.0f;
+               seg->coords[1] = texVPos_;
+               seg->coords[2] = 1.0f;
+               seg->coords[3] = texVPos_;
+               seg->end++;
+       }
+
+       int v = seg->end*6;
+       int c = seg->end*4;
+       // add new vertex
+       seg->creationTime[seg->end] = curTime_;
+       seg->verts[v] = p1.x;
+       seg->verts[v+1] = p1.y;
+       seg->verts[v+2] = 0.0f;
+       seg->verts[v+3] = p2.x;
+       seg->verts[v+4] = p2.y;
+       seg->verts[v+5] = 0.0f;
+
+
+       seg->coords[c] = 0.0f;
+       seg->coords[c+1] = tend;
+       seg->coords[c+2] = 1.0f;
+       seg->coords[c+3] = tend;
+
+       texVPos_ = tend;
+       lastLocation_ = location;
+       lastPoint1_ = p1;
+       lastPoint2_ = p2;
+       lastWidth_ = w;
+       seg->end++;
+}
+
+-(void) draw
+{
+       if ([segments_ count] > 0)
+       {
+               // Default GL states: GL_TEXTURE_2D, GL_VERTEX_ARRAY, GL_COLOR_ARRAY, GL_TEXTURE_COORD_ARRAY
+               // Needed states: GL_TEXTURE_2D, GL_VERTEX_ARRAY, GL_TEXTURE_COORD_ARRAY
+               // Unneeded states: GL_COLOR_ARRAY
+               glDisableClientState(GL_COLOR_ARRAY);
+               
+               glBindTexture(GL_TEXTURE_2D, [texture_ name]);
+
+               BOOL newBlend = blendFunc_.src != CC_BLEND_SRC || blendFunc_.dst != CC_BLEND_DST;
+               if( newBlend )
+                       glBlendFunc( blendFunc_.src, blendFunc_.dst );
+
+               for (CCRibbonSegment* seg in segments_)
+                       [seg draw:curTime_ fadeTime:fadeTime_ color:color_];
+
+               if( newBlend )
+                       glBlendFunc(CC_BLEND_SRC, CC_BLEND_DST);
+               
+               // restore default GL state
+               glEnableClientState( GL_COLOR_ARRAY );
+       }
+}
+
+#pragma mark Ribbon - CocosNodeTexture protocol
+-(void) setTexture:(CCTexture2D*) texture
+{
+       [texture_ release];
+       texture_ = [texture retain];
+       [self setContentSizeInPixels: texture.contentSizeInPixels];
+       /* XXX Don't update blending function in Ribbons */
+}
+
+-(CCTexture2D*) texture
+{
+       return texture_;
+}
+
+@end
+
+
+#pragma mark -
+#pragma mark RibbonSegment
+
+@implementation CCRibbonSegment
+
+-(id)init
+{
+       self = [super init];
+       if (self)
+       {
+               [self reset];
+       }
+       return self;
+}
+
+- (NSString*) description
+{
+       return [NSString stringWithFormat:@"<%@ = %08X | end = %i, begin = %i>", [self class], self, end, begin];
+}
+
+- (void) dealloc
+{
+       CCLOGINFO(@"cocos2d: deallocing %@", self);
+       [super dealloc];
+}
+
+-(void)reset
+{
+       end = 0;
+       begin = 0;
+       finished = NO;
+}
+
+-(void)draw:(float)curTime fadeTime:(float)fadeTime color:(ccColor4B)color
+{
+       GLubyte r = color.r;
+       GLubyte g = color.g;
+       GLubyte b = color.b;
+       GLubyte a = color.a;
+
+       if (begin < 50)
+       {
+               // the motion streak class will call update and cause time to change, thus, if curTime_ != 0
+               // we have to generate alpha for the ribbon each frame.
+               if (curTime == 0)
+               {
+                       // no alpha over time, so just set the color
+                       glColor4ub(r,g,b,a);
+               }
+               else
+               {
+                       // generate alpha/color for each point
+                       glEnableClientState(GL_COLOR_ARRAY);
+                       uint i = begin;
+                       for (; i < end; ++i)
+                       {
+                               int idx = i*8;
+                               colors[idx] = r;
+                               colors[idx+1] = g;
+                               colors[idx+2] = b;
+                               colors[idx+4] = r;
+                               colors[idx+5] = g;
+                               colors[idx+6] = b;
+                               float alive = ((curTime - creationTime[i]) / fadeTime);
+                               if (alive > 1)
+                               {
+                                       begin++;
+                                       colors[idx+3] = 0;
+                                       colors[idx+7] = 0;
+                               }
+                               else
+                               {
+                                       colors[idx+3] = (GLubyte)(255.f - (alive * 255.f));
+                                       colors[idx+7] = colors[idx+3];
+                               }
+                       }
+                       glColorPointer(4, GL_UNSIGNED_BYTE, 0, &colors[begin*8]);
+               }
+               glVertexPointer(3, GL_FLOAT, 0, &verts[begin*6]);
+               glTexCoordPointer(2, GL_FLOAT, 0, &coords[begin*4]);
+               glDrawArrays(GL_TRIANGLE_STRIP, 0, (end - begin) * 2);
+       }
+       else
+               finished = YES;
+}
+@end
+
diff --git a/libs/cocos2d/CCScene.h b/libs/cocos2d/CCScene.h
new file mode 100644 (file)
index 0000000..1d104bc
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2008-2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+
+#import "CCNode.h"
+
+/** CCScene is a subclass of CCNode that is used only as an abstract concept.
+ CCScene an CCNode are almost identical with the difference that CCScene has it's
+ anchor point (by default) at the center of the screen.
+
+ For the moment CCScene has no other logic than that, but in future releases it might have
+ additional logic.
+
+ It is a good practice to use and CCScene as the parent of all your nodes.
+*/
+@interface CCScene : CCNode
+{
+}
+@end
diff --git a/libs/cocos2d/CCScene.m b/libs/cocos2d/CCScene.m
new file mode 100644 (file)
index 0000000..e991d6e
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2008-2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+
+#import "CCScene.h"
+#import "Support/CGPointExtension.h"
+#import "CCDirector.h"
+
+
+@implementation CCScene
+-(id) init
+{
+       if( (self=[super init]) ) {
+               CGSize s = [[CCDirector sharedDirector] winSize];
+               self.isRelativeAnchorPoint = NO;
+               anchorPoint_ = ccp(0.5f, 0.5f);
+               [self setContentSize:s];        
+       }
+       
+       return self;
+}
+@end
diff --git a/libs/cocos2d/CCScheduler.h b/libs/cocos2d/CCScheduler.h
new file mode 100644 (file)
index 0000000..315a7d2
--- /dev/null
@@ -0,0 +1,194 @@
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2008-2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+
+
+#import "Support/uthash.h"
+#import "ccTypes.h"
+
+typedef void (*TICK_IMP)(id, SEL, ccTime);
+
+//
+// CCTimer
+//
+/** Light weight timer */
+@interface CCTimer : NSObject
+{
+       id target;
+       TICK_IMP impMethod;
+       
+       ccTime elapsed;
+
+@public                                        // optimization
+       ccTime interval;
+       SEL selector;
+}
+
+/** interval in seconds */
+@property (nonatomic,readwrite,assign) ccTime interval;
+
+/** Allocates a timer with a target and a selector.
+*/
++(id) timerWithTarget:(id) t selector:(SEL)s;
+
+/** Allocates a timer with a target, a selector and an interval in seconds.
+*/
++(id) timerWithTarget:(id) t selector:(SEL)s interval:(ccTime)seconds;
+
+/** Initializes a timer with a target and a selector.
+*/
+ -(id) initWithTarget:(id) t selector:(SEL)s;
+
+/** Initializes a timer with a target, a selector and an interval in seconds.
+*/
+-(id) initWithTarget:(id) t selector:(SEL)s interval:(ccTime)seconds;
+
+
+/** triggers the timer */
+-(void) update: (ccTime) dt;
+@end
+
+
+
+//
+// CCScheduler
+//
+/** Scheduler is responsible of triggering the scheduled callbacks.
+ You should not use NSTimer. Instead use this class.
+ There are 2 different types of callbacks (selectors):
+
+       - update selector: the 'update' selector will be called every frame. You can customize the priority.
+       - custom selector: A custom selector will be called every frame, or with a custom interval of time
+ The 'custom selectors' should be avoided when possible. It is faster, and consumes less memory to use the 'update selector'.
+
+*/
+
+struct _listEntry;
+struct _hashSelectorEntry;
+struct _hashUpdateEntry;
+
+@interface CCScheduler : NSObject
+{      
+       ccTime                          timeScale_;
+       
+       //
+       // "updates with priority" stuff
+       //
+       struct _listEntry                       *updatesNeg;    // list of priority < 0
+       struct _listEntry                       *updates0;              // list priority == 0
+       struct _listEntry                       *updatesPos;    // list priority > 0
+       struct _hashUpdateEntry         *hashForUpdates;        // hash used to fetch quickly the list entries for pause,delete,etc.
+               
+       // Used for "selectors with interval"
+       struct _hashSelectorEntry       *hashForSelectors;
+       struct _hashSelectorEntry       *currentTarget;
+       BOOL                                            currentTargetSalvaged;
+       
+       // Optimization
+       TICK_IMP                        impMethod;
+       SEL                                     updateSelector;
+    
+    BOOL updateHashLocked; // If true unschedule will not remove anything from a hash. Elements will only be marked for deletion.
+}
+
+/** Modifies the time of all scheduled callbacks.
+ You can use this property to create a 'slow motion' or 'fast fordward' effect.
+ Default is 1.0. To create a 'slow motion' effect, use values below 1.0.
+ To create a 'fast fordward' effect, use values higher than 1.0.
+ @since v0.8
+ @warning It will affect EVERY scheduled selector / action.
+ */
+@property (nonatomic,readwrite) ccTime timeScale;
+
+/** returns a shared instance of the Scheduler */
++(CCScheduler *)sharedScheduler;
+
+/** purges the shared scheduler. It releases the retained instance.
+ @since v0.99.0
+ */
++(void)purgeSharedScheduler;
+
+/** 'tick' the scheduler.
+ You should NEVER call this method, unless you know what you are doing.
+ */
+-(void) tick:(ccTime)dt;
+
+/** The scheduled method will be called every 'interval' seconds.
+ If paused is YES, then it won't be called until it is resumed.
+ If 'interval' is 0, it will be called every frame, but if so, it recommened to use 'scheduleUpdateForTarget:' instead.
+ If the selector is already scheduled, then only the interval parameter will be updated without re-scheduling it again.
+
+ @since v0.99.3
+ */
+-(void) scheduleSelector:(SEL)selector forTarget:(id)target interval:(ccTime)interval paused:(BOOL)paused;
+
+/** Schedules the 'update' selector for a given target with a given priority.
+ The 'update' selector will be called every frame.
+ The lower the priority, the earlier it is called.
+ @since v0.99.3
+ */
+-(void) scheduleUpdateForTarget:(id)target priority:(NSInteger)priority paused:(BOOL)paused;
+
+/** Unshedules a selector for a given target.
+ If you want to unschedule the "update", use unscheudleUpdateForTarget.
+ @since v0.99.3
+ */
+-(void) unscheduleSelector:(SEL)selector forTarget:(id)target;
+
+/** Unschedules the update selector for a given target
+ @since v0.99.3
+ */
+-(void) unscheduleUpdateForTarget:(id)target;
+
+/** Unschedules all selectors for a given target.
+ This also includes the "update" selector.
+ @since v0.99.3
+ */
+-(void) unscheduleAllSelectorsForTarget:(id)target;
+
+/** Unschedules all selectors from all targets.
+ You should NEVER call this method, unless you know what you are doing.
+
+ @since v0.99.3
+ */
+-(void) unscheduleAllSelectors;
+
+/** Pauses the target.
+ All scheduled selectors/update for a given target won't be 'ticked' until the target is resumed.
+ If the target is not present, nothing happens.
+ @since v0.99.3
+ */
+-(void) pauseTarget:(id)target;
+
+/** Resumes the target.
+ The 'target' will be unpaused, so all schedule selectors/update will be 'ticked' again.
+ If the target is not present, nothing happens.
+ @since v0.99.3
+ */
+-(void) resumeTarget:(id)target;
+
+@end
diff --git a/libs/cocos2d/CCScheduler.m b/libs/cocos2d/CCScheduler.m
new file mode 100644 (file)
index 0000000..cddc3c4
--- /dev/null
@@ -0,0 +1,642 @@
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2008-2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+
+// cocos2d imports
+#import "CCScheduler.h"
+#import "ccMacros.h"
+#import "Support/uthash.h"
+#import "Support/utlist.h"
+#import "Support/ccCArray.h"
+
+//
+// Data structures
+//
+#pragma mark -
+#pragma mark Data Structures
+
+// A list double-linked list used for "updates with priority"
+typedef struct _listEntry
+{
+       struct  _listEntry *prev, *next;
+       TICK_IMP        impMethod;
+       id                      target;                         // not retained (retained by hashUpdateEntry)
+       NSInteger       priority;
+       BOOL            paused; 
+    BOOL    markedForDeletion;         // selector will no longer be called and entry will be removed at end of the next tick
+} tListEntry;
+
+typedef struct _hashUpdateEntry
+{
+       tListEntry              **list;         // Which list does it belong to ?
+       tListEntry              *entry;         // entry in the list
+       id                              target;         // hash key (retained)
+       UT_hash_handle  hh;
+} tHashUpdateEntry;
+
+// Hash Element used for "selectors with interval"
+typedef struct _hashSelectorEntry
+{
+       struct ccArray  *timers;
+       id                              target;         // hash key (retained)
+       unsigned int    timerIndex;
+       CCTimer                 *currentTimer;
+       BOOL                    currentTimerSalvaged;
+       BOOL                    paused;
+       UT_hash_handle  hh;
+} tHashSelectorEntry;
+
+
+
+//
+// CCTimer
+//
+#pragma mark -
+#pragma mark - CCTimer
+
+@implementation CCTimer
+
+@synthesize interval;
+
+-(id) init
+{
+       NSAssert(NO, @"CCTimer: Init not supported.");
+       [self release];
+       return nil;
+}
+
++(id) timerWithTarget:(id)t selector:(SEL)s
+{
+       return [[[self alloc] initWithTarget:t selector:s] autorelease];
+}
+
++(id) timerWithTarget:(id)t selector:(SEL)s interval:(ccTime) i
+{
+       return [[[self alloc] initWithTarget:t selector:s interval:i] autorelease];
+}
+
+-(id) initWithTarget:(id)t selector:(SEL)s
+{
+       return [self initWithTarget:t selector:s interval:0];
+}
+
+-(id) initWithTarget:(id)t selector:(SEL)s interval:(ccTime) seconds
+{
+       if( (self=[super init]) ) {
+#if COCOS2D_DEBUG
+               NSMethodSignature *sig = [t methodSignatureForSelector:s];
+               NSAssert(sig !=0 , @"Signature not found for selector - does it have the following form? -(void) name: (ccTime) dt");
+#endif
+               
+               // target is not retained. It is retained in the hash structure
+               target = t;
+               selector = s;
+               impMethod = (TICK_IMP) [t methodForSelector:s];
+               elapsed = -1;
+               interval = seconds;
+       }
+       return self;
+}
+
+- (NSString*) description
+{
+       return [NSString stringWithFormat:@"<%@ = %08X | target:%@ selector:(%@)>", [self class], self, [target class], NSStringFromSelector(selector)];
+}
+
+-(void) dealloc
+{
+       CCLOGINFO(@"cocos2d: deallocing %@", self);
+       [super dealloc];
+}
+
+-(void) update: (ccTime) dt
+{
+       if( elapsed == - 1)
+               elapsed = 0;
+       else
+               elapsed += dt;
+       if( elapsed >= interval ) {
+               impMethod(target, selector, elapsed);
+               elapsed = 0;
+       }
+}
+@end
+
+//
+// CCScheduler
+//
+#pragma mark -
+#pragma mark - CCScheduler
+
+@interface CCScheduler (Private)
+-(void) removeHashElement:(tHashSelectorEntry*)element;
+@end
+
+@implementation CCScheduler
+
+static CCScheduler *sharedScheduler;
+
+@synthesize timeScale = timeScale_;
+
++ (CCScheduler *)sharedScheduler
+{
+       if (!sharedScheduler)
+               sharedScheduler = [[CCScheduler alloc] init];
+
+       return sharedScheduler;
+}
+
++(id)alloc
+{
+       NSAssert(sharedScheduler == nil, @"Attempted to allocate a second instance of a singleton.");
+       return [super alloc];
+}
+
++(void)purgeSharedScheduler
+{
+       [sharedScheduler release];
+       sharedScheduler = nil;
+}
+
+- (id) init
+{
+       if( (self=[super init]) ) {             
+               timeScale_ = 1.0f;
+
+               // used to trigger CCTimer#update
+               updateSelector = @selector(update:);
+               impMethod = (TICK_IMP) [CCTimer instanceMethodForSelector:updateSelector];
+
+               // updates with priority
+               updates0 = NULL;
+               updatesNeg = NULL;
+               updatesPos = NULL;
+               hashForUpdates = NULL;
+               
+               // selectors with interval
+               currentTarget = nil;
+               currentTargetSalvaged = NO;
+               hashForSelectors = nil;
+        updateHashLocked = NO;
+       }
+
+       return self;
+}
+
+- (void) dealloc
+{
+       CCLOG(@"cocos2d: deallocing %@", self);
+
+       [self unscheduleAllSelectors];
+
+       sharedScheduler = nil;
+
+       [super dealloc];
+}
+
+
+#pragma mark CCScheduler - Custom Selectors
+
+-(void) removeHashElement:(tHashSelectorEntry*)element
+{
+       ccArrayFree(element->timers);
+       [element->target release];
+       HASH_DEL(hashForSelectors, element);
+       free(element);
+}
+
+-(void) scheduleSelector:(SEL)selector forTarget:(id)target interval:(ccTime)interval paused:(BOOL)paused
+{
+       NSAssert( selector != nil, @"Argument selector must be non-nil");
+       NSAssert( target != nil, @"Argument target must be non-nil");   
+       
+       tHashSelectorEntry *element = NULL;
+       HASH_FIND_INT(hashForSelectors, &target, element);
+       
+       if( ! element ) {
+               element = calloc( sizeof( *element ), 1 );
+               element->target = [target retain];
+               HASH_ADD_INT( hashForSelectors, target, element );
+       
+               // Is this the 1st element ? Then set the pause level to all the selectors of this target
+               element->paused = paused;
+       
+       } else
+               NSAssert( element->paused == paused, @"CCScheduler. Trying to schedule a selector with a pause value different than the target");
+       
+       
+       if( element->timers == nil )
+               element->timers = ccArrayNew(10);
+       else
+       {
+               for( unsigned int i=0; i< element->timers->num; i++ ) {
+                       CCTimer *timer = element->timers->arr[i];
+                       if( selector == timer->selector ) {
+                               CCLOG(@"CCScheduler#scheduleSelector. Selector already scheduled. Updating interval from: %.2f to %.2f", timer->interval, interval);
+                               timer->interval = interval;
+                               return;
+                       }
+               }
+               ccArrayEnsureExtraCapacity(element->timers, 1);
+       }
+       
+       CCTimer *timer = [[CCTimer alloc] initWithTarget:target selector:selector interval:interval];
+       ccArrayAppendObject(element->timers, timer);
+       [timer release];
+}
+
+-(void) unscheduleSelector:(SEL)selector forTarget:(id)target
+{
+       // explicity handle nil arguments when removing an object
+       if( target==nil && selector==NULL)
+               return;
+       
+       NSAssert( target != nil, @"Target MUST not be nil");
+       NSAssert( selector != NULL, @"Selector MUST not be NULL");
+       
+       tHashSelectorEntry *element = NULL;
+       HASH_FIND_INT(hashForSelectors, &target, element);
+       
+       if( element ) {
+               
+               for( unsigned int i=0; i< element->timers->num; i++ ) {
+                       CCTimer *timer = element->timers->arr[i];
+                       
+                       
+                       if( selector == timer->selector ) {
+                               
+                               if( timer == element->currentTimer && !element->currentTimerSalvaged ) {
+                                       [element->currentTimer retain];
+                                       element->currentTimerSalvaged = YES;
+                               }
+
+                               ccArrayRemoveObjectAtIndex(element->timers, i );
+                               
+                               // update timerIndex in case we are in tick:, looping over the actions
+                               if( element->timerIndex >= i )
+                                       element->timerIndex--;
+
+                               if( element->timers->num == 0 ) {
+                                       if( currentTarget == element )
+                                               currentTargetSalvaged = YES;
+                                       else
+                                               [self removeHashElement: element];
+                               }
+                               return;
+                       }
+               }
+       }
+       
+       // Not Found
+//     NSLog(@"CCScheduler#unscheduleSelector:forTarget: selector not found: %@", selString);
+
+}
+
+#pragma mark CCScheduler - Update Specific
+
+-(void) priorityIn:(tListEntry**)list target:(id)target priority:(NSInteger)priority paused:(BOOL)paused
+{
+       tListEntry *listElement = malloc( sizeof(*listElement) );
+
+       listElement->target = target;
+       listElement->priority = priority;
+       listElement->paused = paused;
+       listElement->impMethod = (TICK_IMP) [target methodForSelector:updateSelector];
+       listElement->next = listElement->prev = NULL;
+    listElement->markedForDeletion = NO;
+       
+       // empty list ?
+       if( ! *list ) {
+               DL_APPEND( *list, listElement );
+       
+       } else {
+               BOOL added = NO;                
+       
+               for( tListEntry *elem = *list; elem ; elem = elem->next ) {
+                       if( priority < elem->priority ) {
+                               
+                               if( elem == *list )
+                                       DL_PREPEND(*list, listElement);
+                               else {
+                                       listElement->next = elem;
+                                       listElement->prev = elem->prev;
+
+                                       elem->prev->next = listElement;
+                                       elem->prev = listElement;
+                               }
+                               
+                               added = YES;
+                               break;
+                       }
+               }
+               
+               // Not added? priority has the higher value. Append it.
+               if( !added )
+                       DL_APPEND(*list, listElement);
+       }
+       
+       // update hash entry for quicker access
+       tHashUpdateEntry *hashElement = calloc( sizeof(*hashElement), 1 );
+       hashElement->target = [target retain];
+       hashElement->list = list;
+       hashElement->entry = listElement;
+       HASH_ADD_INT(hashForUpdates, target, hashElement );
+}
+
+-(void) appendIn:(tListEntry**)list target:(id)target paused:(BOOL)paused
+{
+       tListEntry *listElement = malloc( sizeof( * listElement ) );
+       
+       listElement->target = target;
+       listElement->paused = paused;
+    listElement->markedForDeletion = NO;
+       listElement->impMethod = (TICK_IMP) [target methodForSelector:updateSelector];
+       
+       DL_APPEND(*list, listElement);
+
+       
+       // update hash entry for quicker access
+       tHashUpdateEntry *hashElement = calloc( sizeof(*hashElement), 1 );
+       hashElement->target = [target retain];
+       hashElement->list = list;
+       hashElement->entry = listElement;
+       HASH_ADD_INT(hashForUpdates, target, hashElement );     
+}
+
+-(void) scheduleUpdateForTarget:(id)target priority:(NSInteger)priority paused:(BOOL)paused
+{
+       tHashUpdateEntry * hashElement = NULL;
+       HASH_FIND_INT(hashForUpdates, &target, hashElement);
+    if(hashElement)
+    {
+#if COCOS2D_DEBUG >= 1        
+        NSAssert( hashElement->entry->markedForDeletion, @"CCScheduler: You can't re-schedule an 'update' selector'. Unschedule it first");
+#endif 
+        // TODO : check if priority has changed!
+        
+        hashElement->entry->markedForDeletion = NO;
+        return;
+    }
+               
+       // most of the updates are going to be 0, that's way there
+       // is an special list for updates with priority 0
+       if( priority == 0 )
+               [self appendIn:&updates0 target:target paused:paused];
+
+       else if( priority < 0 )
+               [self priorityIn:&updatesNeg target:target priority:priority paused:paused];
+
+       else // priority > 0
+               [self priorityIn:&updatesPos target:target priority:priority paused:paused];
+}
+
+- (void) removeUpdateFromHash:(tListEntry*)entry
+{
+    tHashUpdateEntry * element = NULL;
+    
+    HASH_FIND_INT(hashForUpdates, &entry->target, element);
+    if( element ) {
+        // list entry
+        DL_DELETE( *element->list, element->entry );
+        free( element->entry );
+        
+        // hash entry
+        [element->target release];
+        HASH_DEL( hashForUpdates, element);
+        free(element);
+    }
+}
+
+-(void) unscheduleUpdateForTarget:(id)target
+{
+       if( target == nil )
+               return;
+       
+       tHashUpdateEntry * element = NULL;
+       HASH_FIND_INT(hashForUpdates, &target, element);
+       if( element ) {    
+        if(updateHashLocked)
+            element->entry->markedForDeletion = YES;
+        else
+            [self removeUpdateFromHash:element->entry];
+        
+//             // list entry
+//             DL_DELETE( *element->list, element->entry );
+//             free( element->entry );
+//     
+//             // hash entry
+//             [element->target release];
+//             HASH_DEL( hashForUpdates, element);
+//             free(element);
+       }
+}
+
+#pragma mark CCScheduler - Common for Update selector & Custom Selectors
+
+-(void) unscheduleAllSelectors
+{
+       // Custom Selectors
+       for(tHashSelectorEntry *element=hashForSelectors; element != NULL; ) {  
+               id target = element->target;
+               element=element->hh.next;
+               [self unscheduleAllSelectorsForTarget:target];
+       }
+
+       // Updates selectors
+       tListEntry *entry, *tmp;
+       DL_FOREACH_SAFE( updates0, entry, tmp ) {
+               [self unscheduleUpdateForTarget:entry->target];
+       }
+       DL_FOREACH_SAFE( updatesNeg, entry, tmp ) {
+               [self unscheduleUpdateForTarget:entry->target];
+       }
+       DL_FOREACH_SAFE( updatesPos, entry, tmp ) {
+               [self unscheduleUpdateForTarget:entry->target];
+       }
+       
+}
+
+-(void) unscheduleAllSelectorsForTarget:(id)target
+{
+       // explicit nil handling
+       if( target == nil )
+               return;
+       
+       // Custom Selectors
+       tHashSelectorEntry *element = NULL;
+       HASH_FIND_INT(hashForSelectors, &target, element);
+       
+       if( element ) {
+               if( ccArrayContainsObject(element->timers, element->currentTimer) && !element->currentTimerSalvaged ) {
+                       [element->currentTimer retain];
+                       element->currentTimerSalvaged = YES;
+               }
+               ccArrayRemoveAllObjects(element->timers);
+               if( currentTarget == element )
+                       currentTargetSalvaged = YES;
+               else
+                       [self removeHashElement:element];
+       }
+       
+       // Update Selector
+       [self unscheduleUpdateForTarget:target];
+}
+
+-(void) resumeTarget:(id)target
+{
+       NSAssert( target != nil, @"target must be non nil" );
+       
+       // Custom Selectors
+       tHashSelectorEntry *element = NULL;
+       HASH_FIND_INT(hashForSelectors, &target, element);
+       if( element )
+               element->paused = NO;
+       
+       // Update selector
+       tHashUpdateEntry * elementUpdate = NULL;
+       HASH_FIND_INT(hashForUpdates, &target, elementUpdate);
+       if( elementUpdate ) {
+               NSAssert( elementUpdate->entry != NULL, @"resumeTarget: unknown error");
+               elementUpdate->entry->paused = NO;
+       }       
+}
+
+-(void) pauseTarget:(id)target
+{
+       NSAssert( target != nil, @"target must be non nil" );
+       
+       // Custom selectors
+       tHashSelectorEntry *element = NULL;
+       HASH_FIND_INT(hashForSelectors, &target, element);
+       if( element )
+               element->paused = YES;
+       
+       // Update selector
+       tHashUpdateEntry * elementUpdate = NULL;
+       HASH_FIND_INT(hashForUpdates, &target, elementUpdate);
+       if( elementUpdate ) {
+               NSAssert( elementUpdate->entry != NULL, @"pauseTarget: unknown error");
+               elementUpdate->entry->paused = YES;
+       }
+       
+}
+
+#pragma mark CCScheduler - Main Loop
+
+-(void) tick: (ccTime) dt
+{
+    updateHashLocked = YES;
+    
+       if( timeScale_ != 1.0f )
+               dt *= timeScale_;
+       
+       // Iterate all over the Updates selectors
+       tListEntry *entry, *tmp;
+
+       // updates with priority < 0
+       DL_FOREACH_SAFE( updatesNeg, entry, tmp ) {
+               if( ! entry->paused && !entry->markedForDeletion )
+                       entry->impMethod( entry->target, updateSelector, dt );
+       }
+
+       // updates with priority == 0
+       DL_FOREACH_SAFE( updates0, entry, tmp ) {
+               if( ! entry->paused && !entry->markedForDeletion )
+        {
+                       entry->impMethod( entry->target, updateSelector, dt );
+        }
+       }
+       
+       // updates with priority > 0
+       DL_FOREACH_SAFE( updatesPos, entry, tmp ) {
+               if( ! entry->paused  && !entry->markedForDeletion )
+                       entry->impMethod( entry->target, updateSelector, dt );
+       }
+       
+       // Iterate all over the  custome selectors
+       for(tHashSelectorEntry *elt=hashForSelectors; elt != NULL; ) {  
+               
+               currentTarget = elt;
+               currentTargetSalvaged = NO;
+
+               if( ! currentTarget->paused ) {
+                       
+                       // The 'timers' ccArray may change while inside this loop.
+                       for( elt->timerIndex = 0; elt->timerIndex < elt->timers->num; elt->timerIndex++) {
+                               elt->currentTimer = elt->timers->arr[elt->timerIndex];
+                               elt->currentTimerSalvaged = NO;
+
+                               impMethod( elt->currentTimer, updateSelector, dt);
+                               
+                               if( elt->currentTimerSalvaged ) {
+                                       // The currentTimer told the remove itself. To prevent the timer from
+                                       // accidentally deallocating itself before finishing its step, we retained
+                                       // it. Now that step is done, it's safe to release it.
+                                       [elt->currentTimer release];
+                               }
+                               
+                               elt->currentTimer = nil;
+                       }                       
+               }
+               
+               // elt, at this moment, is still valid
+               // so it is safe to ask this here (issue #490)
+               elt = elt->hh.next;
+               
+               // only delete currentTarget if no actions were scheduled during the cycle (issue #481)
+               if( currentTargetSalvaged && currentTarget->timers->num == 0 )
+                       [self removeHashElement:currentTarget];         
+       }
+       
+    // delete all updates that are morked for deletion
+    // updates with priority < 0
+       DL_FOREACH_SAFE( updatesNeg, entry, tmp ) {
+               if(entry->markedForDeletion )
+        {
+            [self removeUpdateFromHash:entry];
+        }
+       }
+    
+       // updates with priority == 0
+       DL_FOREACH_SAFE( updates0, entry, tmp ) {
+               if(entry->markedForDeletion )
+        {
+            [self removeUpdateFromHash:entry];
+        }
+       }
+       
+       // updates with priority > 0
+       DL_FOREACH_SAFE( updatesPos, entry, tmp ) {
+               if(entry->markedForDeletion )
+        {
+            [self removeUpdateFromHash:entry];
+        }
+       }
+
+    updateHashLocked = NO;
+       currentTarget = nil;
+}
+@end
+
diff --git a/libs/cocos2d/CCSprite.h b/libs/cocos2d/CCSprite.h
new file mode 100644 (file)
index 0000000..f599399
--- /dev/null
@@ -0,0 +1,354 @@
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2008-2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+
+#import "CCNode.h"
+#import "CCProtocols.h"
+#import "CCTextureAtlas.h"
+
+@class CCSpriteBatchNode;
+@class CCSpriteFrame;
+@class CCAnimation;
+
+#pragma mark CCSprite
+
+enum {
+       /// CCSprite invalid index on the CCSpriteBatchode
+       CCSpriteIndexNotInitialized = 0xffffffff,
+};
+
+/**
+ Whether or not an CCSprite will rotate, scale or translate with it's parent.
+ Useful in health bars, when you want that the health bar translates with it's parent but you don't
+ want it to rotate with its parent.
+ @since v0.99.0
+ */
+typedef enum {
+       //! Translate with it's parent
+       CC_HONOR_PARENT_TRANSFORM_TRANSLATE =  1 << 0,
+       //! Rotate with it's parent
+       CC_HONOR_PARENT_TRANSFORM_ROTATE        =  1 << 1,
+       //! Scale with it's parent
+       CC_HONOR_PARENT_TRANSFORM_SCALE         =  1 << 2,
+       //! Skew with it's parent
+       CC_HONOR_PARENT_TRANSFORM_SKEW          =  1 << 3,
+
+       //! All possible transformation enabled. Default value.
+       CC_HONOR_PARENT_TRANSFORM_ALL           =  CC_HONOR_PARENT_TRANSFORM_TRANSLATE | CC_HONOR_PARENT_TRANSFORM_ROTATE | CC_HONOR_PARENT_TRANSFORM_SCALE | CC_HONOR_PARENT_TRANSFORM_SKEW,
+
+} ccHonorParentTransform;
+
+/** CCSprite is a 2d image ( http://en.wikipedia.org/wiki/Sprite_(computer_graphics) )
+ *
+ * CCSprite can be created with an image, or with a sub-rectangle of an image.
+ *
+ * If the parent or any of its ancestors is a CCSpriteBatchNode then the following features/limitations are valid
+ *     - Features when the parent is a CCBatchNode:
+ *             - MUCH faster rendering, specially if the CCSpriteBatchNode has many children. All the children will be drawn in a single batch.
+ *
+ *     - Limitations
+ *             - Camera is not supported yet (eg: CCOrbitCamera action doesn't work)
+ *             - GridBase actions are not supported (eg: CCLens, CCRipple, CCTwirl)
+ *             - The Alias/Antialias property belongs to CCSpriteBatchNode, so you can't individually set the aliased property.
+ *             - The Blending function property belongs to CCSpriteBatchNode, so you can't individually set the blending function property.
+ *             - Parallax scroller is not supported, but can be simulated with a "proxy" sprite.
+ *
+ *  If the parent is an standard CCNode, then CCSprite behaves like any other CCNode:
+ *    - It supports blending functions
+ *    - It supports aliasing / antialiasing
+ *    - But the rendering will be slower: 1 draw per children.
+ *
+ * The default anchorPoint in CCSprite is (0.5, 0.5).
+ */
+@interface CCSprite : CCNode <CCRGBAProtocol, CCTextureProtocol>
+{
+       
+       //
+       // Data used when the sprite is rendered using a CCSpriteBatchNode
+       //
+       CCTextureAtlas                  *textureAtlas_;                 // Sprite Sheet texture atlas (weak reference)
+       NSUInteger                              atlasIndex_;                    // Absolute (real) Index on the batch node
+       CCSpriteBatchNode               *batchNode_;                    // Used batch node (weak reference)
+       ccHonorParentTransform  honorParentTransform_;  // whether or not to transform according to its parent transformations
+       BOOL                                    dirty_;                                 // Sprite needs to be updated
+       BOOL                                    recursiveDirty_;                // Subchildren needs to be updated
+       BOOL                                    hasChildren_;                   // optimization to check if it contain children
+       
+       //
+       // Data used when the sprite is self-rendered
+       //
+       ccBlendFunc                             blendFunc_;                             // Needed for the texture protocol
+       CCTexture2D                             *texture_;                              // Texture used to render the sprite
+
+       //
+       // Shared data
+       //
+
+       // whether or not it's parent is a CCSpriteBatchNode
+       BOOL    usesBatchNode_;
+
+       // texture
+       CGRect  rect_;
+       CGRect  rectInPixels_;
+       BOOL    rectRotated_;
+       
+       // Offset Position (used by Zwoptex)
+       CGPoint offsetPositionInPixels_;
+       CGPoint unflippedOffsetPositionFromCenter_;
+
+       // vertex coords, texture coords and color info
+       ccV3F_C4B_T2F_Quad quad_;
+       
+       // opacity and RGB protocol
+       GLubyte         opacity_;
+       ccColor3B       color_;
+       ccColor3B       colorUnmodified_;
+       BOOL            opacityModifyRGB_;
+       
+       // image is flipped
+       BOOL    flipX_;
+       BOOL    flipY_;
+       
+       
+       // Animations that belong to the sprite
+       NSMutableDictionary *animations_;
+
+@public
+       // used internally.
+       void (*updateMethod)(id, SEL);
+}
+
+/** whether or not the Sprite needs to be updated in the Atlas */
+@property (nonatomic,readwrite) BOOL dirty;
+/** the quad (tex coords, vertex coords and color) information */
+@property (nonatomic,readonly) ccV3F_C4B_T2F_Quad quad;
+/** The index used on the TextureAtlas. Don't modify this value unless you know what you are doing */
+@property (nonatomic,readwrite) NSUInteger atlasIndex;
+/** returns the rect of the CCSprite in points */
+@property (nonatomic,readonly) CGRect textureRect;
+/** returns whether or not the texture rectangle is rotated */
+@property (nonatomic,readonly) BOOL textureRectRotated;
+/** whether or not the sprite is flipped horizontally. 
+ It only flips the texture of the sprite, and not the texture of the sprite's children.
+ Also, flipping the texture doesn't alter the anchorPoint.
+ If you want to flip the anchorPoint too, and/or to flip the children too use:
+       sprite.scaleX *= -1;
+ */
+@property (nonatomic,readwrite) BOOL flipX;
+/** whether or not the sprite is flipped vertically.
+ It only flips the texture of the sprite, and not the texture of the sprite's children.
+ Also, flipping the texture doesn't alter the anchorPoint.
+ If you want to flip the anchorPoint too, and/or to flip the children too use:
+       sprite.scaleY *= -1;
+ */
+@property (nonatomic,readwrite) BOOL flipY;
+/** opacity: conforms to CCRGBAProtocol protocol */
+@property (nonatomic,readwrite) GLubyte opacity;
+/** RGB colors: conforms to CCRGBAProtocol protocol */
+@property (nonatomic,readwrite) ccColor3B color;
+/** whether or not the Sprite is rendered using a CCSpriteBatchNode */
+@property (nonatomic,readwrite) BOOL usesBatchNode;
+/** weak reference of the CCTextureAtlas used when the sprite is rendered using a CCSpriteBatchNode */
+@property (nonatomic,readwrite,assign) CCTextureAtlas *textureAtlas;
+/** weak reference to the CCSpriteBatchNode that renders the CCSprite */
+@property (nonatomic,readwrite,assign) CCSpriteBatchNode *batchNode;
+/** whether or not to transform according to its parent transfomrations.
+ Useful for health bars. eg: Don't rotate the health bar, even if the parent rotates.
+ IMPORTANT: Only valid if it is rendered using an CCSpriteBatchNode.
+ @since v0.99.0
+ */
+@property (nonatomic,readwrite) ccHonorParentTransform honorParentTransform;
+/** offset position in pixels of the sprite in points. Calculated automatically by editors like Zwoptex.
+ @since v0.99.0
+ */
+@property (nonatomic,readonly) CGPoint offsetPositionInPixels;
+/** conforms to CCTextureProtocol protocol */
+@property (nonatomic,readwrite) ccBlendFunc blendFunc;
+
+#pragma mark CCSprite - Initializers
+
+/** Creates an sprite with a texture.
+ The rect used will be the size of the texture.
+ The offset will be (0,0).
+ */
++(id) spriteWithTexture:(CCTexture2D*)texture;
+
+/** Creates an sprite with a texture and a rect.
+ The offset will be (0,0).
+ */
++(id) spriteWithTexture:(CCTexture2D*)texture rect:(CGRect)rect;
+
+/** Creates an sprite with an sprite frame.
+ */
++(id) spriteWithSpriteFrame:(CCSpriteFrame*)spriteFrame;
+
+/** Creates an sprite with an sprite frame name.
+ An CCSpriteFrame will be fetched from the CCSpriteFrameCache by name.
+ If the CCSpriteFrame doesn't exist it will raise an exception.
+ @since v0.9
+ */
++(id) spriteWithSpriteFrameName:(NSString*)spriteFrameName;
+
+/** Creates an sprite with an image filename.
+ The rect used will be the size of the image.
+ The offset will be (0,0).
+ */
++(id) spriteWithFile:(NSString*)filename;
+
+/** Creates an sprite with an image filename and a rect.
+ The offset will be (0,0).
+ */
++(id) spriteWithFile:(NSString*)filename rect:(CGRect)rect;
+
+/** Creates an sprite with a CGImageRef and a key.
+ The key is used by the CCTextureCache to know if a texture was already created with this CGImage.
+ For example, a valid key is: @"sprite_frame_01".
+ If key is nil, then a new texture will be created each time by the CCTextureCache. 
+ @since v0.99.0
+ */
++(id) spriteWithCGImage: (CGImageRef)image key:(NSString*)key;
+
+
+/** Creates an sprite with an CCBatchNode and a rect
+ */
++(id) spriteWithBatchNode:(CCSpriteBatchNode*)batchNode rect:(CGRect)rect;
+
+
+/** Initializes an sprite with a texture.
+ The rect used will be the size of the texture.
+ The offset will be (0,0).
+ */
+-(id) initWithTexture:(CCTexture2D*)texture;
+
+/** Initializes an sprite with a texture and a rect in points.
+ The offset will be (0,0).
+ */
+-(id) initWithTexture:(CCTexture2D*)texture rect:(CGRect)rect;
+
+/** Initializes an sprite with an sprite frame.
+ */
+-(id) initWithSpriteFrame:(CCSpriteFrame*)spriteFrame;
+
+/** Initializes an sprite with an sprite frame name.
+ An CCSpriteFrame will be fetched from the CCSpriteFrameCache by name.
+ If the CCSpriteFrame doesn't exist it will raise an exception.
+ @since v0.9
+ */
+-(id) initWithSpriteFrameName:(NSString*)spriteFrameName;
+
+/** Initializes an sprite with an image filename.
+ The rect used will be the size of the image.
+ The offset will be (0,0).
+ */
+-(id) initWithFile:(NSString*)filename;
+
+/** Initializes an sprite with an image filename, and a rect.
+ The offset will be (0,0).
+ */
+-(id) initWithFile:(NSString*)filename rect:(CGRect)rect;
+
+/** Initializes an sprite with a CGImageRef and a key
+ The key is used by the CCTextureCache to know if a texture was already created with this CGImage.
+ For example, a valid key is: @"sprite_frame_01".
+ If key is nil, then a new texture will be created each time by the CCTextureCache. 
+ @since v0.99.0
+ */
+-(id) initWithCGImage:(CGImageRef)image key:(NSString*)key;
+
+/** Initializes an sprite with an CCSpriteBatchNode and a rect in points
+ */
+-(id) initWithBatchNode:(CCSpriteBatchNode*)batchNode rect:(CGRect)rect;
+
+/** Initializes an sprite with an CCSpriteBatchNode and a rect in pixels
+ @since v0.99.5
+ */
+-(id) initWithBatchNode:(CCSpriteBatchNode*)batchNode rectInPixels:(CGRect)rect;
+
+
+
+#pragma mark CCSprite - BatchNode methods
+
+/** updates the quad according the the rotation, position, scale values.
+ */
+-(void)updateTransform;
+
+/** updates the texture rect of the CCSprite in points.
+ */
+-(void) setTextureRect:(CGRect) rect;
+/** updates the texture rect, rectRotated and untrimmed size of the CCSprite in pixels
+ */
+-(void) setTextureRectInPixels:(CGRect)rect rotated:(BOOL)rotated untrimmedSize:(CGSize)size;
+
+/** tell the sprite to use self-render.
+ @since v0.99.0
+ */
+-(void) useSelfRender;
+
+/** tell the sprite to use sprite batch node
+ @since v0.99.0
+ */
+-(void) useBatchNode:(CCSpriteBatchNode*)batchNode;
+
+
+#pragma mark CCSprite - Frames
+
+/** sets a new display frame to the CCSprite. */
+-(void) setDisplayFrame:(CCSpriteFrame*)newFrame;
+
+/** returns whether or not a CCSpriteFrame is being displayed */
+-(BOOL) isFrameDisplayed:(CCSpriteFrame*)frame;
+
+/** returns the current displayed frame. */
+-(CCSpriteFrame*) displayedFrame;
+
+#pragma mark CCSprite - Animation
+
+/** changes the display frame based on an animation and an index.
+ @deprecated Will be removed in 1.0.1. Use setDisplayFrameWithAnimationName:index instead
+ */
+-(void) setDisplayFrame: (NSString*) animationName index:(int) frameIndex DEPRECATED_ATTRIBUTE;
+
+/** changes the display frame with animation name and index.
+ The animation name will be get from the CCAnimationCache
+ @since v0.99.5
+ */
+-(void) setDisplayFrameWithAnimationName:(NSString*)animationName index:(int) frameIndex;
+
+/** returns an Animation given it's name.
+ @deprecated Use CCAnimationCache instead. Will be removed in 1.0.1
+ */
+-(CCAnimation*)animationByName: (NSString*) animationName DEPRECATED_ATTRIBUTE;
+
+/** adds an Animation to the Sprite.
+ @deprecated Use CCAnimationCache instead. Will be removed in 1.0.1
+ */
+-(void) addAnimation: (CCAnimation*) animation DEPRECATED_ATTRIBUTE;
+
+@end
diff --git a/libs/cocos2d/CCSprite.m b/libs/cocos2d/CCSprite.m
new file mode 100644 (file)
index 0000000..976d629
--- /dev/null
@@ -0,0 +1,1015 @@
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2008-2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+#import <Availability.h>
+
+#import "ccConfig.h"
+#import "CCSpriteBatchNode.h"
+#import "CCSprite.h"
+#import "CCSpriteFrame.h"
+#import "CCSpriteFrameCache.h"
+#import "CCAnimation.h"
+#import "CCAnimationCache.h"
+#import "CCTextureCache.h"
+#import "Support/CGPointExtension.h"
+#import "CCDrawingPrimitives.h"
+
+#pragma mark -
+#pragma mark CCSprite
+
+#if CC_SPRITEBATCHNODE_RENDER_SUBPIXEL
+#define RENDER_IN_SUBPIXEL
+#else
+#define RENDER_IN_SUBPIXEL(__A__) ( (int)(__A__))
+#endif
+
+// XXX: Optmization
+struct transformValues_ {
+       CGPoint pos;            // position x and y
+       CGPoint scale;          // scale x and y
+       float   rotation;
+       CGPoint skew;           // skew x and y
+       CGPoint ap;                     // anchor point in pixels
+       BOOL    visible;
+};
+
+@interface CCSprite (Private)
+-(void)updateTextureCoords:(CGRect)rect;
+-(void)updateBlendFunc;
+-(void) initAnimationDictionary;
+-(void) getTransformValues:(struct transformValues_*)tv;       // optimization
+@end
+
+@implementation CCSprite
+
+@synthesize dirty = dirty_;
+@synthesize quad = quad_;
+@synthesize atlasIndex = atlasIndex_;
+@synthesize textureRect = rect_;
+@synthesize textureRectRotated = rectRotated_;
+@synthesize blendFunc = blendFunc_;
+@synthesize usesBatchNode = usesBatchNode_;
+@synthesize textureAtlas = textureAtlas_;
+@synthesize batchNode = batchNode_;
+@synthesize honorParentTransform = honorParentTransform_;
+@synthesize offsetPositionInPixels = offsetPositionInPixels_;
+
+
++(id)spriteWithTexture:(CCTexture2D*)texture
+{
+       return [[[self alloc] initWithTexture:texture] autorelease];
+}
+
++(id)spriteWithTexture:(CCTexture2D*)texture rect:(CGRect)rect
+{
+       return [[[self alloc] initWithTexture:texture rect:rect] autorelease];
+}
+
++(id)spriteWithFile:(NSString*)filename
+{
+       return [[[self alloc] initWithFile:filename] autorelease];
+}
+
++(id)spriteWithFile:(NSString*)filename rect:(CGRect)rect
+{
+       return [[[self alloc] initWithFile:filename rect:rect] autorelease];
+}
+
++(id)spriteWithSpriteFrame:(CCSpriteFrame*)spriteFrame
+{
+       return [[[self alloc] initWithSpriteFrame:spriteFrame] autorelease];
+}
+
++(id)spriteWithSpriteFrameName:(NSString*)spriteFrameName
+{
+       CCSpriteFrame *frame = [[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:spriteFrameName];
+       return [self spriteWithSpriteFrame:frame];
+}
+
++(id)spriteWithCGImage:(CGImageRef)image key:(NSString*)key
+{
+       return [[[self alloc] initWithCGImage:image key:key] autorelease];
+}
+
++(id) spriteWithBatchNode:(CCSpriteBatchNode*)batchNode rect:(CGRect)rect
+{
+       return [[[self alloc] initWithBatchNode:batchNode rect:rect] autorelease];
+}
+
+-(id) init
+{
+       if( (self=[super init]) ) {
+               dirty_ = recursiveDirty_ = NO;
+               
+               // by default use "Self Render".
+               // if the sprite is added to a batchnode, then it will automatically switch to "batchnode Render"
+               [self useSelfRender];
+               
+               opacityModifyRGB_                       = YES;
+               opacity_                                        = 255;
+               color_ = colorUnmodified_       = ccWHITE;
+               
+               blendFunc_.src = CC_BLEND_SRC;
+               blendFunc_.dst = CC_BLEND_DST;
+               
+               // update texture (calls updateBlendFunc)
+               [self setTexture:nil];
+               
+               // clean the Quad
+               bzero(&quad_, sizeof(quad_));
+               
+               flipY_ = flipX_ = NO;
+               
+               // lazy alloc
+               animations_ = nil;
+               
+               // default transform anchor: center
+               anchorPoint_ =  ccp(0.5f, 0.5f);
+               
+               // zwoptex default values
+               offsetPositionInPixels_ = CGPointZero;
+               
+               honorParentTransform_ = CC_HONOR_PARENT_TRANSFORM_ALL;
+               hasChildren_ = NO;
+               
+               // Atlas: Color
+               ccColor4B tmpColor = {255,255,255,255};
+               quad_.bl.colors = tmpColor;
+               quad_.br.colors = tmpColor;
+               quad_.tl.colors = tmpColor;
+               quad_.tr.colors = tmpColor;     
+               
+               // Atlas: Vertex
+               
+               // updated in "useSelfRender"
+               
+               // Atlas: TexCoords
+               [self setTextureRectInPixels:CGRectZero rotated:NO untrimmedSize:CGSizeZero];
+               
+               // updateMethod selector
+               updateMethod = (__typeof__(updateMethod))[self methodForSelector:@selector(updateTransform)];
+       }
+       
+       return self;
+}
+
+-(id) initWithTexture:(CCTexture2D*)texture rect:(CGRect)rect
+{
+       NSAssert(texture!=nil, @"Invalid texture for sprite");
+       // IMPORTANT: [self init] and not [super init];
+       if( (self = [self init]) )
+       {
+               [self setTexture:texture];
+               [self setTextureRect:rect];
+       }
+       return self;
+}
+
+-(id) initWithTexture:(CCTexture2D*)texture
+{
+       NSAssert(texture!=nil, @"Invalid texture for sprite");
+
+       CGRect rect = CGRectZero;
+       rect.size = texture.contentSize;
+       return [self initWithTexture:texture rect:rect];
+}
+
+-(id) initWithFile:(NSString*)filename
+{
+       NSAssert(filename!=nil, @"Invalid filename for sprite");
+
+       CCTexture2D *texture = [[CCTextureCache sharedTextureCache] addImage: filename];
+       if( texture ) {
+               CGRect rect = CGRectZero;
+               rect.size = texture.contentSize;
+               return [self initWithTexture:texture rect:rect];
+       }
+
+       [self release];
+       return nil;
+}
+
+-(id) initWithFile:(NSString*)filename rect:(CGRect)rect
+{
+       NSAssert(filename!=nil, @"Invalid filename for sprite");
+
+       CCTexture2D *texture = [[CCTextureCache sharedTextureCache] addImage: filename];
+       if( texture )
+               return [self initWithTexture:texture rect:rect];
+
+       [self release];
+       return nil;
+}
+
+- (id) initWithSpriteFrame:(CCSpriteFrame*)spriteFrame
+{
+       NSAssert(spriteFrame!=nil, @"Invalid spriteFrame for sprite");
+
+       id ret = [self initWithTexture:spriteFrame.texture rect:spriteFrame.rect];
+       [self setDisplayFrame:spriteFrame];
+       return ret;
+}
+
+-(id)initWithSpriteFrameName:(NSString*)spriteFrameName
+{
+       NSAssert(spriteFrameName!=nil, @"Invalid spriteFrameName for sprite");
+
+       CCSpriteFrame *frame = [[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:spriteFrameName];
+       return [self initWithSpriteFrame:frame];
+}
+
+// XXX: deprecated
+- (id) initWithCGImage: (CGImageRef)image
+{
+       NSAssert(image!=nil, @"Invalid CGImageRef for sprite");
+
+       // XXX: possible bug. See issue #349. New API should be added
+       NSString *key = [NSString stringWithFormat:@"%08X",(unsigned long)image];
+       CCTexture2D *texture = [[CCTextureCache sharedTextureCache] addCGImage:image forKey:key];
+       
+       CGRect rect = CGRectZero;
+       rect.size = texture.contentSize;
+       
+       return [self initWithTexture:texture rect:rect];
+}
+
+- (id) initWithCGImage:(CGImageRef)image key:(NSString*)key
+{
+       NSAssert(image!=nil, @"Invalid CGImageRef for sprite");
+       
+       // XXX: possible bug. See issue #349. New API should be added
+       CCTexture2D *texture = [[CCTextureCache sharedTextureCache] addCGImage:image forKey:key];
+       
+       CGRect rect = CGRectZero;
+       rect.size = texture.contentSize;
+       
+       return [self initWithTexture:texture rect:rect];
+}
+
+-(id) initWithBatchNode:(CCSpriteBatchNode*)batchNode rect:(CGRect)rect
+{
+       id ret = [self initWithTexture:batchNode.texture rect:rect];
+       [self useBatchNode:batchNode];
+       
+       return ret;
+}
+
+-(id) initWithBatchNode:(CCSpriteBatchNode*)batchNode rectInPixels:(CGRect)rect
+{
+       id ret = [self initWithTexture:batchNode.texture];
+       [self setTextureRectInPixels:rect rotated:NO untrimmedSize:rect.size];
+       [self useBatchNode:batchNode];
+       
+       return ret;
+}
+
+- (NSString*) description
+{
+       return [NSString stringWithFormat:@"<%@ = %08X | Rect = (%.2f,%.2f,%.2f,%.2f) | tag = %i | atlasIndex = %i>", [self class], self,
+                       rect_.origin.x, rect_.origin.y, rect_.size.width, rect_.size.height,
+                       tag_,
+                       atlasIndex_
+       ];
+}
+
+- (void) dealloc
+{
+       [texture_ release];
+       [animations_ release];
+       [super dealloc];
+}
+
+-(void) useSelfRender
+{
+       atlasIndex_ = CCSpriteIndexNotInitialized;
+       usesBatchNode_ = NO;
+       textureAtlas_ = nil;
+       batchNode_ = nil;
+       dirty_ = recursiveDirty_ = NO;
+       
+       float x1 = 0 + offsetPositionInPixels_.x;
+       float y1 = 0 + offsetPositionInPixels_.y;
+       float x2 = x1 + rectInPixels_.size.width;
+       float y2 = y1 + rectInPixels_.size.height;
+       quad_.bl.vertices = (ccVertex3F) { x1, y1, 0 };
+       quad_.br.vertices = (ccVertex3F) { x2, y1, 0 };
+       quad_.tl.vertices = (ccVertex3F) { x1, y2, 0 };
+       quad_.tr.vertices = (ccVertex3F) { x2, y2, 0 };         
+}
+
+-(void) useBatchNode:(CCSpriteBatchNode*)batchNode
+{
+       usesBatchNode_ = YES;
+       textureAtlas_ = [batchNode textureAtlas]; // weak ref
+       batchNode_ = batchNode; // weak ref
+}
+
+-(void) initAnimationDictionary
+{
+       animations_ = [[NSMutableDictionary alloc] initWithCapacity:2];
+}
+
+-(void)setTextureRect:(CGRect)rect
+{
+       CGRect rectInPixels = CC_RECT_POINTS_TO_PIXELS( rect );
+       [self setTextureRectInPixels:rectInPixels rotated:NO untrimmedSize:rectInPixels.size];
+}
+
+-(void)setTextureRectInPixels:(CGRect)rect rotated:(BOOL)rotated untrimmedSize:(CGSize)untrimmedSize
+{
+       rectInPixels_ = rect;
+       rect_ = CC_RECT_PIXELS_TO_POINTS( rect );
+       rectRotated_ = rotated;
+
+       [self setContentSizeInPixels:untrimmedSize];
+       [self updateTextureCoords:rectInPixels_];
+
+       CGPoint relativeOffsetInPixels = unflippedOffsetPositionFromCenter_;
+       
+       // issue #732
+       if( flipX_ )
+               relativeOffsetInPixels.x = -relativeOffsetInPixels.x;
+       if( flipY_ )
+               relativeOffsetInPixels.y = -relativeOffsetInPixels.y;
+       
+       offsetPositionInPixels_.x = relativeOffsetInPixels.x + (contentSizeInPixels_.width - rectInPixels_.size.width) / 2;
+       offsetPositionInPixels_.y = relativeOffsetInPixels.y + (contentSizeInPixels_.height - rectInPixels_.size.height) / 2;
+       
+       
+       // rendering using batch node
+       if( usesBatchNode_ ) {
+               // update dirty_, don't update recursiveDirty_
+               dirty_ = YES;
+       }
+
+       // self rendering
+       else
+       {
+               // Atlas: Vertex
+               float x1 = 0 + offsetPositionInPixels_.x;
+               float y1 = 0 + offsetPositionInPixels_.y;
+               float x2 = x1 + rectInPixels_.size.width;
+               float y2 = y1 + rectInPixels_.size.height;
+               
+               // Don't update Z.
+               quad_.bl.vertices = (ccVertex3F) { x1, y1, 0 };
+               quad_.br.vertices = (ccVertex3F) { x2, y1, 0 };
+               quad_.tl.vertices = (ccVertex3F) { x1, y2, 0 };
+               quad_.tr.vertices = (ccVertex3F) { x2, y2, 0 }; 
+       }                       
+}
+
+-(void)updateTextureCoords:(CGRect)rect
+{
+       CCTexture2D *tex        = (usesBatchNode_)?[textureAtlas_ texture]:texture_;
+       if(!tex)
+               return;
+       
+       float atlasWidth = (float)tex.pixelsWide;
+       float atlasHeight = (float)tex.pixelsHigh;
+       
+       float left,right,top,bottom;
+       
+       if(rectRotated_){
+#if CC_FIX_ARTIFACTS_BY_STRECHING_TEXEL
+               left    = (2*rect.origin.x+1)/(2*atlasWidth);
+               right   = left+(rect.size.height*2-2)/(2*atlasWidth);
+               top             = (2*rect.origin.y+1)/(2*atlasHeight);
+               bottom  = top+(rect.size.width*2-2)/(2*atlasHeight);
+#else
+               left    = rect.origin.x/atlasWidth;
+               right   = left+(rect.size.height/atlasWidth);
+               top             = rect.origin.y/atlasHeight;
+               bottom  = top+(rect.size.width/atlasHeight);
+#endif // ! CC_FIX_ARTIFACTS_BY_STRECHING_TEXEL
+               
+               if( flipX_)
+                       CC_SWAP(top,bottom);
+               if( flipY_)
+                       CC_SWAP(left,right);
+               
+               quad_.bl.texCoords.u = left;
+               quad_.bl.texCoords.v = top;
+               quad_.br.texCoords.u = left;
+               quad_.br.texCoords.v = bottom;
+               quad_.tl.texCoords.u = right;
+               quad_.tl.texCoords.v = top;
+               quad_.tr.texCoords.u = right;
+               quad_.tr.texCoords.v = bottom;
+       } else {
+#if CC_FIX_ARTIFACTS_BY_STRECHING_TEXEL
+               left    = (2*rect.origin.x+1)/(2*atlasWidth);
+               right   = left + (rect.size.width*2-2)/(2*atlasWidth);
+               top             = (2*rect.origin.y+1)/(2*atlasHeight);
+               bottom  = top + (rect.size.height*2-2)/(2*atlasHeight);
+#else
+               left    = rect.origin.x/atlasWidth;
+               right   = left + rect.size.width/atlasWidth;
+               top             = rect.origin.y/atlasHeight;
+               bottom  = top + rect.size.height/atlasHeight;
+#endif // ! CC_FIX_ARTIFACTS_BY_STRECHING_TEXEL
+               
+               if( flipX_)
+                       CC_SWAP(left,right);
+               if( flipY_)
+                       CC_SWAP(top,bottom);
+               
+               quad_.bl.texCoords.u = left;
+               quad_.bl.texCoords.v = bottom;
+               quad_.br.texCoords.u = right;
+               quad_.br.texCoords.v = bottom;
+               quad_.tl.texCoords.u = left;
+               quad_.tl.texCoords.v = top;
+               quad_.tr.texCoords.u = right;
+               quad_.tr.texCoords.v = top;
+       }
+}
+
+-(void)updateTransform
+{
+       NSAssert( usesBatchNode_, @"updateTransform is only valid when CCSprite is being renderd using an CCSpriteBatchNode");
+
+       // optimization. Quick return if not dirty
+       if( ! dirty_ )
+               return;
+       
+       CGAffineTransform matrix;
+       
+       // Optimization: if it is not visible, then do nothing
+       if( ! visible_ ) {
+               quad_.br.vertices = quad_.tl.vertices = quad_.tr.vertices = quad_.bl.vertices = (ccVertex3F){0,0,0};
+               [textureAtlas_ updateQuad:&quad_ atIndex:atlasIndex_];
+               dirty_ = recursiveDirty_ = NO;
+               return ;
+       }
+       
+
+       // Optimization: If parent is batchnode, or parent is nil
+       // build Affine transform manually
+       if( ! parent_ || parent_ == batchNode_ ) {
+               
+               float radians = -CC_DEGREES_TO_RADIANS(rotation_);
+               float c = cosf(radians);
+               float s = sinf(radians);
+
+               matrix = CGAffineTransformMake( c * scaleX_,  s * scaleX_,
+                                                                          -s * scaleY_, c * scaleY_,
+                                                                          positionInPixels_.x, positionInPixels_.y);
+               if( skewX_ || skewY_ ) {
+                       CGAffineTransform skewMatrix = CGAffineTransformMake(1.0f, tanf(CC_DEGREES_TO_RADIANS(skewY_)),
+                                                                                                                                tanf(CC_DEGREES_TO_RADIANS(skewX_)), 1.0f,
+                                                                                                                                0.0f, 0.0f);
+                       matrix = CGAffineTransformConcat(skewMatrix, matrix);
+               }
+               matrix = CGAffineTransformTranslate(matrix, -anchorPointInPixels_.x, -anchorPointInPixels_.y);          
+
+               
+       }  else {       // parent_ != batchNode_ 
+
+               // else do affine transformation according to the HonorParentTransform
+
+               matrix = CGAffineTransformIdentity;
+               ccHonorParentTransform prevHonor = CC_HONOR_PARENT_TRANSFORM_ALL;
+               
+               for (CCNode *p = self ; p && p != batchNode_ ; p = p.parent) {
+                       
+                       // Might happen. Issue #1053
+                       NSAssert( [p isKindOfClass:[CCSprite class]], @"CCSprite should be a CCSprite subclass. Probably you initialized an sprite with a batchnode, but you didn't add it to the batch node." );
+
+                       struct transformValues_ tv;
+                       [(CCSprite*)p getTransformValues: &tv];
+                       
+                       // If any of the parents are not visible, then don't draw this node
+                       if( ! tv.visible ) {
+                               quad_.br.vertices = quad_.tl.vertices = quad_.tr.vertices = quad_.bl.vertices = (ccVertex3F){0,0,0};
+                               [textureAtlas_ updateQuad:&quad_ atIndex:atlasIndex_];
+                               dirty_ = recursiveDirty_ = NO;
+                               return;
+                       }
+                       CGAffineTransform newMatrix = CGAffineTransformIdentity;
+                       
+                       // 2nd: Translate, Skew, Rotate, Scale
+                       if( prevHonor & CC_HONOR_PARENT_TRANSFORM_TRANSLATE )
+                               newMatrix = CGAffineTransformTranslate(newMatrix, tv.pos.x, tv.pos.y);
+                       if( prevHonor & CC_HONOR_PARENT_TRANSFORM_ROTATE )
+                               newMatrix = CGAffineTransformRotate(newMatrix, -CC_DEGREES_TO_RADIANS(tv.rotation));
+                       if ( prevHonor & CC_HONOR_PARENT_TRANSFORM_SKEW ) {
+                               CGAffineTransform skew = CGAffineTransformMake(1.0f, tanf(CC_DEGREES_TO_RADIANS(tv.skew.y)), tanf(CC_DEGREES_TO_RADIANS(tv.skew.x)), 1.0f, 0.0f, 0.0f);
+                               // apply the skew to the transform
+                               newMatrix = CGAffineTransformConcat(skew, newMatrix);
+                       }
+                       if( prevHonor & CC_HONOR_PARENT_TRANSFORM_SCALE ) {
+                               newMatrix = CGAffineTransformScale(newMatrix, tv.scale.x, tv.scale.y);
+                       }
+                       
+                       // 3rd: Translate anchor point
+                       newMatrix = CGAffineTransformTranslate(newMatrix, -tv.ap.x, -tv.ap.y);
+
+                       // 4th: Matrix multiplication
+                       matrix = CGAffineTransformConcat( matrix, newMatrix);
+                       
+                       prevHonor = [(CCSprite*)p honorParentTransform];
+               }               
+       }
+       
+       
+       //
+       // calculate the Quad based on the Affine Matrix
+       //      
+
+       CGSize size = rectInPixels_.size;
+
+       float x1 = offsetPositionInPixels_.x;
+       float y1 = offsetPositionInPixels_.y;
+       
+       float x2 = x1 + size.width;
+       float y2 = y1 + size.height;
+       float x = matrix.tx;
+       float y = matrix.ty;
+       
+       float cr = matrix.a;
+       float sr = matrix.b;
+       float cr2 = matrix.d;
+       float sr2 = -matrix.c;
+       float ax = x1 * cr - y1 * sr2 + x;
+       float ay = x1 * sr + y1 * cr2 + y;
+       
+       float bx = x2 * cr - y1 * sr2 + x;
+       float by = x2 * sr + y1 * cr2 + y;
+       
+       float cx = x2 * cr - y2 * sr2 + x;
+       float cy = x2 * sr + y2 * cr2 + y;
+       
+       float dx = x1 * cr - y2 * sr2 + x;
+       float dy = x1 * sr + y2 * cr2 + y;
+       
+       quad_.bl.vertices = (ccVertex3F) { RENDER_IN_SUBPIXEL(ax), RENDER_IN_SUBPIXEL(ay), vertexZ_ };
+       quad_.br.vertices = (ccVertex3F) { RENDER_IN_SUBPIXEL(bx), RENDER_IN_SUBPIXEL(by), vertexZ_ };
+       quad_.tl.vertices = (ccVertex3F) { RENDER_IN_SUBPIXEL(dx), RENDER_IN_SUBPIXEL(dy), vertexZ_ };
+       quad_.tr.vertices = (ccVertex3F) { RENDER_IN_SUBPIXEL(cx), RENDER_IN_SUBPIXEL(cy), vertexZ_ };
+               
+       [textureAtlas_ updateQuad:&quad_ atIndex:atlasIndex_];
+       dirty_ = recursiveDirty_ = NO;
+}
+
+// XXX: Optimization: instead of calling 5 times the parent sprite to obtain: position, scale.x, scale.y, anchorpoint and rotation,
+// this fuction return the 5 values in 1 single call
+-(void) getTransformValues:(struct transformValues_*) tv
+{
+       tv->pos                 = positionInPixels_;
+       tv->scale.x             = scaleX_;
+       tv->scale.y             = scaleY_;
+       tv->rotation    = rotation_;
+       tv->skew.x              = skewX_;
+       tv->skew.y              = skewY_;
+       tv->ap                  = anchorPointInPixels_;
+       tv->visible             = visible_;
+}
+
+#pragma mark CCSprite - draw
+
+-(void) draw
+{
+       NSAssert(!usesBatchNode_, @"If CCSprite is being rendered by CCSpriteBatchNode, CCSprite#draw SHOULD NOT be called");
+
+       // Default GL states: GL_TEXTURE_2D, GL_VERTEX_ARRAY, GL_COLOR_ARRAY, GL_TEXTURE_COORD_ARRAY
+       // Needed states: GL_TEXTURE_2D, GL_VERTEX_ARRAY, GL_COLOR_ARRAY, GL_TEXTURE_COORD_ARRAY
+       // Unneeded states: -
+
+       BOOL newBlend = blendFunc_.src != CC_BLEND_SRC || blendFunc_.dst != CC_BLEND_DST;
+       if( newBlend )
+               glBlendFunc( blendFunc_.src, blendFunc_.dst );
+
+#define kQuadSize sizeof(quad_.bl)
+       glBindTexture(GL_TEXTURE_2D, [texture_ name]);
+       
+       long offset = (long)&quad_;
+       
+       // vertex
+       NSInteger diff = offsetof( ccV3F_C4B_T2F, vertices);
+       glVertexPointer(3, GL_FLOAT, kQuadSize, (void*) (offset + diff) );
+       
+       // color
+       diff = offsetof( ccV3F_C4B_T2F, colors);
+       glColorPointer(4, GL_UNSIGNED_BYTE, kQuadSize, (void*)(offset + diff));
+       
+       // tex coords
+       diff = offsetof( ccV3F_C4B_T2F, texCoords);
+       glTexCoordPointer(2, GL_FLOAT, kQuadSize, (void*)(offset + diff));
+       
+       glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
+       
+       if( newBlend )
+               glBlendFunc(CC_BLEND_SRC, CC_BLEND_DST);
+       
+#if CC_SPRITE_DEBUG_DRAW
+       CGSize s = [self contentSize];
+       CGPoint vertices[4]={
+               ccp(0,0),ccp(s.width,0),
+               ccp(s.width,s.height),ccp(0,s.height),
+       };
+       ccDrawPoly(vertices, 4, YES);
+#endif // CC_TEXTURENODE_DEBUG_DRAW
+       
+}
+
+#pragma mark CCSprite - CCNode overrides
+
+-(void) addChild:(CCSprite*)child z:(NSInteger)z tag:(NSInteger) aTag
+{
+       NSAssert( child != nil, @"Argument must be non-nil");
+       
+       [super addChild:child z:z tag:aTag];
+       
+       if( usesBatchNode_ ) {
+               NSAssert( [child isKindOfClass:[CCSprite class]], @"CCSprite only supports CCSprites as children when using CCSpriteBatchNode");
+               NSAssert( child.texture.name == textureAtlas_.texture.name, @"CCSprite is not using the same texture id");
+               
+               NSUInteger index = [batchNode_ atlasIndexForChild:child atZ:z];
+               [batchNode_ insertChild:child inAtlasAtIndex:index];
+       }
+       
+       hasChildren_ = YES;
+}
+
+-(void) reorderChild:(CCSprite*)child z:(NSInteger)z
+{
+       NSAssert( child != nil, @"Child must be non-nil");
+       NSAssert( [children_ containsObject:child], @"Child doesn't belong to Sprite" );
+
+       if( z == child.zOrder )
+               return;
+
+       if( usesBatchNode_ ) {
+               // XXX: Instead of removing/adding, it is more efficient to reorder manually
+               [child retain];
+               [self removeChild:child cleanup:NO];
+               [self addChild:child z:z];
+               [child release];
+       }
+
+       else
+               [super reorderChild:child z:z];
+}
+
+-(void)removeChild: (CCSprite *)sprite cleanup:(BOOL)doCleanup
+{
+       if( usesBatchNode_ )
+               [batchNode_ removeSpriteFromAtlas:sprite];
+
+       [super removeChild:sprite cleanup:doCleanup];
+       
+       hasChildren_ = ( [children_ count] > 0 );
+}
+
+-(void)removeAllChildrenWithCleanup:(BOOL)doCleanup
+{
+       if( usesBatchNode_ ) {
+               CCSprite *child;
+               CCARRAY_FOREACH(children_, child)
+                       [batchNode_ removeSpriteFromAtlas:child];
+       }
+       
+       [super removeAllChildrenWithCleanup:doCleanup];
+       
+       hasChildren_ = NO;
+}
+
+//
+// CCNode property overloads
+// used only when parent is CCSpriteBatchNode
+//
+#pragma mark CCSprite - property overloads
+
+
+-(void) setDirtyRecursively:(BOOL)b
+{
+       dirty_ = recursiveDirty_ = b;
+       // recursively set dirty
+       if( hasChildren_ ) {
+               CCSprite *child;
+               CCARRAY_FOREACH(children_, child)
+                       [child setDirtyRecursively:YES];
+       }
+}
+
+// XXX HACK: optimization
+#define SET_DIRTY_RECURSIVELY() {                                                                      \
+                                       if( usesBatchNode_ && ! recursiveDirty_ ) {     \
+                                               dirty_ = recursiveDirty_ = YES;                         \
+                                               if( hasChildren_)                                                       \
+                                                       [self setDirtyRecursively:YES];                 \
+                                               }                                                                                       \
+                                       }
+
+-(void)setPosition:(CGPoint)pos
+{
+       [super setPosition:pos];
+       SET_DIRTY_RECURSIVELY();
+}
+
+-(void)setPositionInPixels:(CGPoint)pos
+{
+       [super setPositionInPixels:pos];
+       SET_DIRTY_RECURSIVELY();
+}
+
+-(void)setRotation:(float)rot
+{
+       [super setRotation:rot];
+       SET_DIRTY_RECURSIVELY();
+}
+
+-(void)setSkewX:(float)sx
+{
+       [super setSkewX:sx];
+       SET_DIRTY_RECURSIVELY();
+}
+
+-(void)setSkewY:(float)sy
+{
+       [super setSkewY:sy];
+       SET_DIRTY_RECURSIVELY();
+}
+
+-(void)setScaleX:(float) sx
+{
+       [super setScaleX:sx];
+       SET_DIRTY_RECURSIVELY();
+}
+
+-(void)setScaleY:(float) sy
+{
+       [super setScaleY:sy];
+       SET_DIRTY_RECURSIVELY();
+}
+
+-(void)setScale:(float) s
+{
+       [super setScale:s];
+       SET_DIRTY_RECURSIVELY();
+}
+
+-(void) setVertexZ:(float)z
+{
+       [super setVertexZ:z];
+       SET_DIRTY_RECURSIVELY();
+}
+
+-(void)setAnchorPoint:(CGPoint)anchor
+{
+       [super setAnchorPoint:anchor];
+       SET_DIRTY_RECURSIVELY();
+}
+
+-(void)setIsRelativeAnchorPoint:(BOOL)relative
+{
+       NSAssert( ! usesBatchNode_, @"relativeTransformAnchor is invalid in CCSprite");
+       [super setIsRelativeAnchorPoint:relative];
+}
+
+-(void)setVisible:(BOOL)v
+{
+       [super setVisible:v];
+       SET_DIRTY_RECURSIVELY();
+}
+
+-(void)setFlipX:(BOOL)b
+{
+       if( flipX_ != b ) {
+               flipX_ = b;
+               [self setTextureRectInPixels:rectInPixels_ rotated:rectRotated_ untrimmedSize:contentSizeInPixels_];
+       }
+}
+-(BOOL) flipX
+{
+       return flipX_;
+}
+
+-(void) setFlipY:(BOOL)b
+{
+       if( flipY_ != b ) {
+               flipY_ = b;     
+               [self setTextureRectInPixels:rectInPixels_ rotated:rectRotated_ untrimmedSize:contentSizeInPixels_];
+       }       
+}
+-(BOOL) flipY
+{
+       return flipY_;
+}
+
+//
+// RGBA protocol
+//
+#pragma mark CCSprite - RGBA protocol
+-(void) updateColor
+{
+       ccColor4B color4 = {color_.r, color_.g, color_.b, opacity_ };
+       
+       quad_.bl.colors = color4;
+       quad_.br.colors = color4;
+       quad_.tl.colors = color4;
+       quad_.tr.colors = color4;
+       
+       // renders using Sprite Manager
+       if( usesBatchNode_ ) {
+               if( atlasIndex_ != CCSpriteIndexNotInitialized)
+                       [textureAtlas_ updateQuad:&quad_ atIndex:atlasIndex_];
+               else
+                       // no need to set it recursively
+                       // update dirty_, don't update recursiveDirty_
+                       dirty_ = YES;
+       }
+       // self render
+       // do nothing
+}
+
+-(GLubyte) opacity
+{
+       return opacity_;
+}
+
+-(void) setOpacity:(GLubyte) anOpacity
+{
+       opacity_                        = anOpacity;
+
+       // special opacity for premultiplied textures
+       if( opacityModifyRGB_ )
+               [self setColor: colorUnmodified_];
+       
+       [self updateColor];
+}
+
+- (ccColor3B) color
+{
+       if(opacityModifyRGB_)
+               return colorUnmodified_;
+       
+       return color_;
+}
+
+-(void) setColor:(ccColor3B)color3
+{
+       color_ = colorUnmodified_ = color3;
+       
+       if( opacityModifyRGB_ ){
+               color_.r = color3.r * opacity_/255;
+               color_.g = color3.g * opacity_/255;
+               color_.b = color3.b * opacity_/255;
+       }
+       
+       [self updateColor];
+}
+
+-(void) setOpacityModifyRGB:(BOOL)modify
+{
+       ccColor3B oldColor      = self.color;
+       opacityModifyRGB_       = modify;
+       self.color                      = oldColor;
+}
+
+-(BOOL) doesOpacityModifyRGB
+{
+       return opacityModifyRGB_;
+}
+
+//
+// Frames
+//
+#pragma mark CCSprite - Frames
+
+-(void) setDisplayFrame:(CCSpriteFrame*)frame
+{
+       unflippedOffsetPositionFromCenter_ = frame.offsetInPixels;
+
+       CCTexture2D *newTexture = [frame texture];
+       // update texture before updating texture rect
+       if ( newTexture.name != texture_.name )
+               [self setTexture: newTexture];
+       
+       // update rect
+       rectRotated_ = frame.rotated;
+       [self setTextureRectInPixels:frame.rectInPixels rotated:frame.rotated untrimmedSize:frame.originalSizeInPixels];
+}
+
+// XXX deprecated
+-(void) setDisplayFrame: (NSString*) animationName index:(int) frameIndex
+{
+       if( ! animations_ )
+               [self initAnimationDictionary];
+       
+       CCAnimation *a = [animations_ objectForKey: animationName];
+       CCSpriteFrame *frame = [[a frames] objectAtIndex:frameIndex];
+       
+       NSAssert( frame, @"CCSprite#setDisplayFrame. Invalid frame");
+       
+       [self setDisplayFrame:frame];
+}
+
+-(void) setDisplayFrameWithAnimationName: (NSString*) animationName index:(int) frameIndex
+{
+       NSAssert( animationName, @"CCSprite#setDisplayFrameWithAnimationName. animationName must not be nil");
+       
+       CCAnimation *a = [[CCAnimationCache sharedAnimationCache] animationByName:animationName];
+       
+       NSAssert( a, @"CCSprite#setDisplayFrameWithAnimationName: Frame not found");
+       
+       CCSpriteFrame *frame = [[a frames] objectAtIndex:frameIndex];
+       
+       NSAssert( frame, @"CCSprite#setDisplayFrame. Invalid frame");
+       
+       [self setDisplayFrame:frame];
+}
+
+
+-(BOOL) isFrameDisplayed:(CCSpriteFrame*)frame 
+{
+       CGRect r = [frame rect];
+       return ( CGRectEqualToRect(r, rect_) &&
+                       frame.texture.name == self.texture.name );
+}
+
+-(CCSpriteFrame*) displayedFrame
+{      
+       return [CCSpriteFrame frameWithTexture:texture_
+                                                         rectInPixels:rectInPixels_
+                                                                  rotated:rectRotated_
+                                                                       offset:unflippedOffsetPositionFromCenter_
+                                                         originalSize:contentSizeInPixels_];
+}
+
+-(void) addAnimation: (CCAnimation*) anim
+{
+       // lazy alloc
+       if( ! animations_ )
+               [self initAnimationDictionary];
+       
+       [animations_ setObject:anim forKey:[anim name]];
+}
+
+-(CCAnimation*)animationByName: (NSString*) animationName
+{
+       NSAssert( animationName != nil, @"animationName parameter must be non nil");
+    return [animations_ objectForKey:animationName];
+}
+
+#pragma mark CCSprite - CocosNodeTexture protocol
+
+-(void) updateBlendFunc
+{
+       NSAssert( ! usesBatchNode_, @"CCSprite: updateBlendFunc doesn't work when the sprite is rendered using a CCSpriteBatchNode");
+
+       // it's possible to have an untextured sprite
+       if( !texture_ || ! [texture_ hasPremultipliedAlpha] ) {
+               blendFunc_.src = GL_SRC_ALPHA;
+               blendFunc_.dst = GL_ONE_MINUS_SRC_ALPHA;
+               [self setOpacityModifyRGB:NO];
+       } else {
+               blendFunc_.src = CC_BLEND_SRC;
+               blendFunc_.dst = CC_BLEND_DST;
+               [self setOpacityModifyRGB:YES];
+       }
+}
+
+-(void) setTexture:(CCTexture2D*)texture
+{
+       NSAssert( ! usesBatchNode_, @"CCSprite: setTexture doesn't work when the sprite is rendered using a CCSpriteBatchNode");
+       
+       // accept texture==nil as argument
+       NSAssert( !texture || [texture isKindOfClass:[CCTexture2D class]], @"setTexture expects a CCTexture2D. Invalid argument");
+
+       [texture_ release];
+       texture_ = [texture retain];
+       
+       [self updateBlendFunc];
+}
+
+-(CCTexture2D*) texture
+{
+       return texture_;
+}
+
+@end
diff --git a/libs/cocos2d/CCSpriteBatchNode.h b/libs/cocos2d/CCSpriteBatchNode.h
new file mode 100644 (file)
index 0000000..0342e24
--- /dev/null
@@ -0,0 +1,145 @@
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (C) 2009 Matt Oswald
+ *
+ * Copyright (c) 2009-2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+
+#import "CCNode.h"
+#import "CCProtocols.h"
+#import "CCTextureAtlas.h"
+#import "ccMacros.h"
+
+#pragma mark CCSpriteBatchNode
+
+@class CCSprite;
+
+/** CCSpriteBatchNode is like a batch node: if it contains children, it will draw them in 1 single OpenGL call
+ * (often known as "batch draw").
+ *
+ * A CCSpriteBatchNode can reference one and only one texture (one image file, one texture atlas).
+ * Only the CCSprites that are contained in that texture can be added to the CCSpriteBatchNode.
+ * All CCSprites added to a CCSpriteBatchNode are drawn in one OpenGL ES draw call.
+ * If the CCSprites are not added to a CCSpriteBatchNode then an OpenGL ES draw call will be needed for each one, which is less efficient.
+ *
+ *
+ * Limitations:
+ *  - The only object that is accepted as child (or grandchild, grand-grandchild, etc...) is CCSprite or any subclass of CCSprite. eg: particles, labels and layer can't be added to a CCSpriteBatchNode.
+ *  - Either all its children are Aliased or Antialiased. It can't be a mix. This is because "alias" is a property of the texture, and all the sprites share the same texture.
+ * 
+ * @since v0.7.1
+ */
+@interface CCSpriteBatchNode : CCNode <CCTextureProtocol>
+{
+       CCTextureAtlas  *textureAtlas_;
+       ccBlendFunc             blendFunc_;
+
+       // all descendants: chlidren, gran children, etc...
+       CCArray *descendants_;
+}
+
+/** returns the TextureAtlas that is used */
+@property (nonatomic,readwrite,retain) CCTextureAtlas * textureAtlas;
+
+/** conforms to CCTextureProtocol protocol */
+@property (nonatomic,readwrite) ccBlendFunc blendFunc;
+
+/** descendants (children, gran children, etc) */
+@property (nonatomic,readonly) CCArray *descendants;
+
+/** creates a CCSpriteBatchNode with a texture2d and a default capacity of 29 children.
+ The capacity will be increased in 33% in runtime if it run out of space.
+ */
++(id)batchNodeWithTexture:(CCTexture2D *)tex;
++(id)spriteSheetWithTexture:(CCTexture2D *)tex DEPRECATED_ATTRIBUTE;
+
+/** creates a CCSpriteBatchNode with a texture2d and capacity of children.
+ The capacity will be increased in 33% in runtime if it run out of space.
+ */
++(id)batchNodeWithTexture:(CCTexture2D *)tex capacity:(NSUInteger)capacity;
++(id)spriteSheetWithTexture:(CCTexture2D *)tex capacity:(NSUInteger)capacity DEPRECATED_ATTRIBUTE;
+
+/** creates a CCSpriteBatchNode with a file image (.png, .jpeg, .pvr, etc) with a default capacity of 29 children.
+ The capacity will be increased in 33% in runtime if it run out of space.
+ The file will be loaded using the TextureMgr.
+ */
++(id)batchNodeWithFile:(NSString*) fileImage;
++(id)spriteSheetWithFile:(NSString*) fileImage DEPRECATED_ATTRIBUTE;
+
+/** creates a CCSpriteBatchNode with a file image (.png, .jpeg, .pvr, etc) and capacity of children.
+ The capacity will be increased in 33% in runtime if it run out of space.
+ The file will be loaded using the TextureMgr.
+*/
++(id)batchNodeWithFile:(NSString*)fileImage capacity:(NSUInteger)capacity;
++(id)spriteSheetWithFile:(NSString*)fileImage capacity:(NSUInteger)capacity DEPRECATED_ATTRIBUTE;
+
+/** initializes a CCSpriteBatchNode with a texture2d and capacity of children.
+ The capacity will be increased in 33% in runtime if it run out of space.
+ */
+-(id)initWithTexture:(CCTexture2D *)tex capacity:(NSUInteger)capacity;
+/** initializes a CCSpriteBatchNode with a file image (.png, .jpeg, .pvr, etc) and a capacity of children.
+ The capacity will be increased in 33% in runtime if it run out of space.
+ The file will be loaded using the TextureMgr.
+ */
+-(id)initWithFile:(NSString*)fileImage capacity:(NSUInteger)capacity;
+
+-(void) increaseAtlasCapacity;
+
+/** creates an sprite with a rect in the CCSpriteBatchNode.
+ It's the same as:
+   - create an standard CCSsprite
+   - set the usingSpriteSheet = YES
+   - set the textureAtlas to the same texture Atlas as the CCSpriteBatchNode
+ @deprecated Use [CCSprite spriteWithBatchNode:rect:] instead;
+ */
+-(CCSprite*) createSpriteWithRect:(CGRect)rect DEPRECATED_ATTRIBUTE;
+
+/** initializes a previously created sprite with a rect. This sprite will have the same texture as the CCSpriteBatchNode.
+ It's the same as:
+ - initialize an standard CCSsprite
+ - set the usingBatchNode = YES
+ - set the textureAtlas to the same texture Atlas as the CCSpriteBatchNode
+ @since v0.99.0
+ @deprecated Use [CCSprite initWithBatchNode:rect:] instead;
+*/ 
+-(void) initSprite:(CCSprite*)sprite rect:(CGRect)rect DEPRECATED_ATTRIBUTE;
+
+/** removes a child given a certain index. It will also cleanup the running actions depending on the cleanup parameter.
+ @warning Removing a child from a CCSpriteBatchNode is very slow
+ */
+-(void)removeChildAtIndex:(NSUInteger)index cleanup:(BOOL)doCleanup;
+
+/** removes a child given a reference. It will also cleanup the running actions depending on the cleanup parameter.
+ @warning Removing a child from a CCSpriteBatchNode is very slow
+ */
+-(void)removeChild: (CCSprite *)sprite cleanup:(BOOL)doCleanup;
+
+-(void) insertChild:(CCSprite*)child inAtlasAtIndex:(NSUInteger)index;
+-(void) removeSpriteFromAtlas:(CCSprite*)sprite;
+
+-(NSUInteger) rebuildIndexInOrder:(CCSprite*)parent atlasIndex:(NSUInteger)index;
+-(NSUInteger) atlasIndexForChild:(CCSprite*)sprite atZ:(NSInteger)z;
+
+@end
diff --git a/libs/cocos2d/CCSpriteBatchNode.m b/libs/cocos2d/CCSpriteBatchNode.m
new file mode 100644 (file)
index 0000000..f675c67
--- /dev/null
@@ -0,0 +1,501 @@
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (C) 2009 Matt Oswald
+ *
+ * Copyright (c) 2009-2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+
+#import "ccConfig.h"
+#import "CCSprite.h"
+#import "CCSpriteBatchNode.h"
+#import "CCGrid.h"
+#import "CCDrawingPrimitives.h"
+#import "CCTextureCache.h"
+#import "Support/CGPointExtension.h"
+
+const NSUInteger defaultCapacity = 29;
+
+#pragma mark -
+#pragma mark CCSpriteBatchNode
+
+static         SEL selUpdate = NULL;
+
+@interface CCSpriteBatchNode (private)
+-(void) updateBlendFunc;
+@end
+
+@implementation CCSpriteBatchNode
+
+@synthesize textureAtlas = textureAtlas_;
+@synthesize blendFunc = blendFunc_;
+@synthesize descendants = descendants_;
+
+
++(void) initialize
+{
+       if ( self == [CCSpriteBatchNode class] ) {
+               selUpdate = @selector(updateTransform);
+       }
+}
+/*
+ * creation with CCTexture2D
+ */
++(id)batchNodeWithTexture:(CCTexture2D *)tex
+{
+       return [[[self alloc] initWithTexture:tex capacity:defaultCapacity] autorelease];
+}
++(id)spriteSheetWithTexture:(CCTexture2D *)tex // XXX DEPRECATED
+{
+       return [self batchNodeWithTexture:tex];
+}
+
++(id)batchNodeWithTexture:(CCTexture2D *)tex capacity:(NSUInteger)capacity
+{
+       return [[[self alloc] initWithTexture:tex capacity:capacity] autorelease];
+}
++(id)spriteSheetWithTexture:(CCTexture2D *)tex capacity:(NSUInteger)capacity // XXX DEPRECATED
+{
+       return [self batchNodeWithTexture:tex capacity:capacity];
+}
+
+/*
+ * creation with File Image
+ */
++(id)batchNodeWithFile:(NSString*)fileImage capacity:(NSUInteger)capacity
+{
+       return [[[self alloc] initWithFile:fileImage capacity:capacity] autorelease];
+}
++(id)spriteSheetWithFile:(NSString*)fileImage capacity:(NSUInteger)capacity // XXX DEPRECATED
+{
+       return [self batchNodeWithFile:fileImage capacity:capacity];
+}
+
++(id)batchNodeWithFile:(NSString*) imageFile
+{
+       return [[[self alloc] initWithFile:imageFile capacity:defaultCapacity] autorelease];
+}
++(id)spriteSheetWithFile:(NSString*) imageFile // XXX DEPRECATED
+{
+       return [self batchNodeWithFile:imageFile];
+}
+
+
+/*
+ * init with CCTexture2D
+ */
+-(id)initWithTexture:(CCTexture2D *)tex capacity:(NSUInteger)capacity
+{
+       if( (self=[super init])) {
+               
+               blendFunc_.src = CC_BLEND_SRC;
+               blendFunc_.dst = CC_BLEND_DST;
+               textureAtlas_ = [[CCTextureAtlas alloc] initWithTexture:tex capacity:capacity];
+               
+               [self updateBlendFunc];
+               
+               // no lazy alloc in this node
+               children_ = [[CCArray alloc] initWithCapacity:capacity];
+               descendants_ = [[CCArray alloc] initWithCapacity:capacity];
+       }
+       
+       return self;
+}
+
+/*
+ * init with FileImage
+ */
+-(id)initWithFile:(NSString *)fileImage capacity:(NSUInteger)capacity
+{
+       CCTexture2D *tex = [[CCTextureCache sharedTextureCache] addImage:fileImage];
+       return [self initWithTexture:tex capacity:capacity];
+}
+
+- (NSString*) description
+{
+       return [NSString stringWithFormat:@"<%@ = %08X | Tag = %i>", [self class], self, tag_ ];
+}
+
+-(void)dealloc
+{      
+       [textureAtlas_ release];
+       [descendants_ release];
+       
+       [super dealloc];
+}
+
+#pragma mark CCSpriteBatchNode - composition
+
+// override visit.
+// Don't call visit on it's children
+-(void) visit
+{
+       
+       // CAREFUL:
+       // This visit is almost identical to CocosNode#visit
+       // with the exception that it doesn't call visit on it's children
+       //
+       // The alternative is to have a void CCSprite#visit, but
+       // although this is less mantainable, is faster
+       //
+       if (!visible_)
+               return;
+       
+       glPushMatrix();
+       
+       if ( grid_ && grid_.active) {
+               [grid_ beforeDraw];
+               [self transformAncestors];
+       }
+       
+       [self transform];
+       
+       [self draw];
+       
+       if ( grid_ && grid_.active)
+               [grid_ afterDraw:self];
+       
+       glPopMatrix();
+}
+
+// XXX deprecated
+-(CCSprite*) createSpriteWithRect:(CGRect)rect
+{
+       CCSprite *sprite = [CCSprite spriteWithTexture:textureAtlas_.texture rect:rect];
+       [sprite useBatchNode:self];
+       
+       return sprite;
+}
+
+// XXX deprecated
+-(void) initSprite:(CCSprite*)sprite rect:(CGRect)rect
+{
+       [sprite initWithTexture:textureAtlas_.texture rect:rect];
+       [sprite useBatchNode:self];
+}
+
+// override addChild:
+-(void) addChild:(CCSprite*)child z:(NSInteger)z tag:(NSInteger) aTag
+{
+       NSAssert( child != nil, @"Argument must be non-nil");
+       NSAssert( [child isKindOfClass:[CCSprite class]], @"CCSpriteBatchNode only supports CCSprites as children");
+       NSAssert( child.texture.name == textureAtlas_.texture.name, @"CCSprite is not using the same texture id");
+       
+       [super addChild:child z:z tag:aTag];
+       
+       NSUInteger index = [self atlasIndexForChild:child atZ:z];
+       [self insertChild:child inAtlasAtIndex:index];  
+}
+
+// override reorderChild
+-(void) reorderChild:(CCSprite*)child z:(NSInteger)z
+{
+       NSAssert( child != nil, @"Child must be non-nil");
+       NSAssert( [children_ containsObject:child], @"Child doesn't belong to Sprite" );
+       
+       if( z == child.zOrder )
+               return;
+       
+       // XXX: Instead of removing/adding, it is more efficient to reorder manually
+       [child retain];
+       [self removeChild:child cleanup:NO];
+       [self addChild:child z:z];
+       [child release];
+}
+
+// override removeChild:
+-(void)removeChild: (CCSprite *)sprite cleanup:(BOOL)doCleanup
+{
+       // explicit nil handling
+       if (sprite == nil)
+               return;
+       
+       NSAssert([children_ containsObject:sprite], @"CCSpriteBatchNode doesn't contain the sprite. Can't remove it");
+       
+       // cleanup before removing
+       [self removeSpriteFromAtlas:sprite];
+       
+       [super removeChild:sprite cleanup:doCleanup];
+}
+
+-(void)removeChildAtIndex:(NSUInteger)index cleanup:(BOOL)doCleanup
+{
+       [self removeChild:(CCSprite *)[children_ objectAtIndex:index] cleanup:doCleanup];
+}
+
+-(void)removeAllChildrenWithCleanup:(BOOL)doCleanup
+{
+       // Invalidate atlas index. issue #569
+       [children_ makeObjectsPerformSelector:@selector(useSelfRender)];
+       
+       [super removeAllChildrenWithCleanup:doCleanup];
+       
+       [descendants_ removeAllObjects];
+       [textureAtlas_ removeAllQuads];
+}
+
+#pragma mark CCSpriteBatchNode - draw
+-(void) draw
+{
+       // Optimization: Fast Dispatch  
+       if( textureAtlas_.totalQuads == 0 )
+               return; 
+       
+       CCSprite *child;
+       ccArray *array = descendants_->data;
+       
+       NSUInteger i = array->num;
+       id *arr = array->arr;
+
+       if( i > 0 ) {
+               
+               while (i-- > 0) {
+                       child = *arr++;
+                       
+                       // fast dispatch
+                       child->updateMethod(child, selUpdate);
+                       
+#if CC_SPRITEBATCHNODE_DEBUG_DRAW
+                       //Issue #528
+                       CGRect rect = [child boundingBox];
+                       CGPoint vertices[4]={
+                               ccp(rect.origin.x,rect.origin.y),
+                               ccp(rect.origin.x+rect.size.width,rect.origin.y),
+                               ccp(rect.origin.x+rect.size.width,rect.origin.y+rect.size.height),
+                               ccp(rect.origin.x,rect.origin.y+rect.size.height),
+                       };
+                       ccDrawPoly(vertices, 4, YES);
+#endif // CC_SPRITEBATCHNODE_DEBUG_DRAW
+               }
+       }
+       
+       // Default GL states: GL_TEXTURE_2D, GL_VERTEX_ARRAY, GL_COLOR_ARRAY, GL_TEXTURE_COORD_ARRAY
+       // Needed states: GL_TEXTURE_2D, GL_VERTEX_ARRAY, GL_COLOR_ARRAY, GL_TEXTURE_COORD_ARRAY
+       // Unneeded states: -
+       
+       BOOL newBlend = blendFunc_.src != CC_BLEND_SRC || blendFunc_.dst != CC_BLEND_DST;
+       if( newBlend )
+               glBlendFunc( blendFunc_.src, blendFunc_.dst );
+       
+       [textureAtlas_ drawQuads];
+       if( newBlend )
+               glBlendFunc(CC_BLEND_SRC, CC_BLEND_DST);
+}
+
+#pragma mark CCSpriteBatchNode - private
+-(void) increaseAtlasCapacity
+{
+       // if we're going beyond the current TextureAtlas's capacity,
+       // all the previously initialized sprites will need to redo their texture coords
+       // this is likely computationally expensive
+       NSUInteger quantity = (textureAtlas_.capacity + 1) * 4 / 3;
+       
+       CCLOG(@"cocos2d: CCSpriteBatchNode: resizing TextureAtlas capacity from [%lu] to [%lu].",
+                 (long)textureAtlas_.capacity,
+                 (long)quantity);
+       
+       
+       if( ! [textureAtlas_ resizeCapacity:quantity] ) {
+               // serious problems
+               CCLOG(@"cocos2d: WARNING: Not enough memory to resize the atlas");
+               NSAssert(NO,@"XXX: SpriteSheet#increaseAtlasCapacity SHALL handle this assert");
+       }       
+}
+
+
+#pragma mark CCSpriteBatchNode - Atlas Index Stuff
+
+-(NSUInteger) rebuildIndexInOrder:(CCSprite*)node atlasIndex:(NSUInteger)index
+{
+       CCSprite *sprite;
+       CCARRAY_FOREACH(node.children, sprite){
+               if( sprite.zOrder < 0 )
+                       index = [self rebuildIndexInOrder:sprite atlasIndex:index];
+       }
+       
+       // ignore self (batch node)
+       if( ! [node isEqual:self]) {
+               node.atlasIndex = index;
+               index++;
+       }
+       
+       CCARRAY_FOREACH(node.children, sprite){
+               if( sprite.zOrder >= 0 )
+                       index = [self rebuildIndexInOrder:sprite atlasIndex:index];
+       }
+       
+       return index;
+}
+
+-(NSUInteger) highestAtlasIndexInChild:(CCSprite*)sprite
+{
+       CCArray *array = [sprite children];
+       NSUInteger count = [array count];
+       if( count == 0 )
+               return sprite.atlasIndex;
+       else
+               return [self highestAtlasIndexInChild:[array lastObject]];
+}
+
+-(NSUInteger) lowestAtlasIndexInChild:(CCSprite*)sprite
+{
+       CCArray *array = [sprite children];
+       NSUInteger count = [array count];
+       if( count == 0 )
+               return sprite.atlasIndex;
+       else
+               return [self lowestAtlasIndexInChild:[array objectAtIndex:0] ];
+}
+
+
+-(NSUInteger)atlasIndexForChild:(CCSprite*)sprite atZ:(NSInteger)z
+{
+       CCArray *brothers = [[sprite parent] children];
+       NSUInteger childIndex = [brothers indexOfObject:sprite];
+       
+       // ignore parent Z if parent is batchnode
+       BOOL ignoreParent = ( sprite.parent == self );
+       CCSprite *previous = nil;
+       if( childIndex > 0 )
+               previous = [brothers objectAtIndex:childIndex-1];
+       
+       // first child of the sprite sheet
+       if( ignoreParent ) {
+               if( childIndex == 0 )
+                       return 0;
+               // else
+               return [self highestAtlasIndexInChild: previous] + 1;
+       }
+       
+       // parent is a CCSprite, so, it must be taken into account
+       
+       // first child of an CCSprite ?
+       if( childIndex == 0 )
+       {
+               CCSprite *p = (CCSprite*) sprite.parent;
+               
+               // less than parent and brothers
+               if( z < 0 )
+                       return p.atlasIndex;
+               else
+                       return p.atlasIndex+1;
+               
+       } else {
+               // previous & sprite belong to the same branch
+               if( ( previous.zOrder < 0 && z < 0 )|| (previous.zOrder >= 0 && z >= 0) )
+                       return [self highestAtlasIndexInChild:previous] + 1;
+               
+               // else (previous < 0 and sprite >= 0 )
+               CCSprite *p = (CCSprite*) sprite.parent;
+               return p.atlasIndex + 1;
+       }
+       
+       NSAssert( NO, @"Should not happen. Error calculating Z on Batch Node");
+       return 0;
+}
+
+#pragma mark CCSpriteBatchNode - add / remove / reorder helper methods
+// add child helper
+-(void) insertChild:(CCSprite*)sprite inAtlasAtIndex:(NSUInteger)index
+{
+       [sprite useBatchNode:self];
+       [sprite setAtlasIndex:index];
+       [sprite setDirty: YES];
+       
+       if(textureAtlas_.totalQuads == textureAtlas_.capacity)
+               [self increaseAtlasCapacity];
+       
+       ccV3F_C4B_T2F_Quad quad = [sprite quad];
+       [textureAtlas_ insertQuad:&quad atIndex:index];
+       
+       ccArray *descendantsData = descendants_->data;
+       
+       ccArrayInsertObjectAtIndex(descendantsData, sprite, index);
+       
+       // update indices
+       NSUInteger i = index+1;
+       CCSprite *child;
+       for(; i<descendantsData->num; i++){
+               child = descendantsData->arr[i];
+               child.atlasIndex = child.atlasIndex + 1;
+       }
+       
+       // add children recursively
+       CCARRAY_FOREACH(sprite.children, child){
+               NSUInteger idx = [self atlasIndexForChild:child atZ: child.zOrder];
+               [self insertChild:child inAtlasAtIndex:idx];
+       }
+}
+
+// remove child helper
+-(void) removeSpriteFromAtlas:(CCSprite*)sprite
+{
+       // remove from TextureAtlas
+       [textureAtlas_ removeQuadAtIndex:sprite.atlasIndex];
+       
+       // Cleanup sprite. It might be reused (issue #569)
+       [sprite useSelfRender];
+       
+       ccArray *descendantsData = descendants_->data;
+       NSUInteger index = ccArrayGetIndexOfObject(descendantsData, sprite);
+       if( index != NSNotFound ) {
+               ccArrayRemoveObjectAtIndex(descendantsData, index);
+               
+               // update all sprites beyond this one
+               NSUInteger count = descendantsData->num;
+               
+               for(; index < count; index++)
+               {
+                       CCSprite *s = descendantsData->arr[index];
+                       s.atlasIndex = s.atlasIndex - 1;
+               }
+       }
+       
+       // remove children recursively
+       CCSprite *child;
+       CCARRAY_FOREACH(sprite.children, child)
+               [self removeSpriteFromAtlas:child];
+}
+
+#pragma mark CCSpriteBatchNode - CocosNodeTexture protocol
+
+-(void) updateBlendFunc
+{
+       if( ! [textureAtlas_.texture hasPremultipliedAlpha] ) {
+               blendFunc_.src = GL_SRC_ALPHA;
+               blendFunc_.dst = GL_ONE_MINUS_SRC_ALPHA;
+       }
+}
+
+-(void) setTexture:(CCTexture2D*)texture
+{
+       textureAtlas_.texture = texture;
+       [self updateBlendFunc];
+}
+
+-(CCTexture2D*) texture
+{
+       return textureAtlas_.texture;
+}
+@end
diff --git a/libs/cocos2d/CCSpriteFrame.h b/libs/cocos2d/CCSpriteFrame.h
new file mode 100644 (file)
index 0000000..983aeed
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2008-2011 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+#import <Foundation/Foundation.h>
+#import "CCNode.h"
+#import "CCProtocols.h"
+
+/** A CCSpriteFrame has:
+       - texture: A CCTexture2D that will be used by the CCSprite
+       - rectangle: A rectangle of the texture
+
+
+ You can modify the frame of a CCSprite by doing:
+       CCSpriteFrame *frame = [CCSpriteFrame frameWithTexture:texture rect:rect offset:offset];
+       [sprite setDisplayFrame:frame];
+ */
+@interface CCSpriteFrame : NSObject <NSCopying>
+{
+       CGRect                  rect_;
+       CGRect                  rectInPixels_;
+       BOOL                    rotated_;
+       CGPoint                 offsetInPixels_;
+       CGSize                  originalSizeInPixels_;
+       CCTexture2D             *texture_;
+}
+/** rect of the frame in points. If it is updated, then rectInPixels will be updated too. */
+@property (nonatomic,readwrite) CGRect rect;
+
+/** rect of the frame in pixels. If it is updated, then rect (points) will be udpated too. */
+@property (nonatomic,readwrite) CGRect rectInPixels;
+
+/** whether or not the rect of the frame is rotated ( x = x+width, y = y+height, width = height, height = width ) */
+@property (nonatomic,readwrite) BOOL rotated;
+
+/** offset of the frame in pixels */
+@property (nonatomic,readwrite) CGPoint offsetInPixels;
+
+/** original size of the trimmed image in pixels */
+@property (nonatomic,readwrite) CGSize originalSizeInPixels;
+
+/** texture of the frame */
+@property (nonatomic, retain, readwrite) CCTexture2D *texture;
+
+/** Create a CCSpriteFrame with a texture, rect in points.
+ It is assumed that the frame was not trimmed.
+ */
++(id) frameWithTexture:(CCTexture2D*)texture rect:(CGRect)rect;
+
+/** Create a CCSpriteFrame with a texture, rect, rotated, offset and originalSize in pixels.
+ The originalSize is the size in points of the frame before being trimmed.
+ */
++(id) frameWithTexture:(CCTexture2D*)texture rectInPixels:(CGRect)rect rotated:(BOOL)rotated offset:(CGPoint)offset originalSize:(CGSize)originalSize;
+
+
+/** Initializes a CCSpriteFrame with a texture, rect in points;
+ It is assumed that the frame was not trimmed.
+ */
+-(id) initWithTexture:(CCTexture2D*)texture rect:(CGRect)rect;
+
+/** Initializes a CCSpriteFrame with a texture, rect, rotated, offset and originalSize in pixels.
+ The originalSize is the size in points of the frame before being trimmed.
+ */
+-(id) initWithTexture:(CCTexture2D*)texture rectInPixels:(CGRect)rect rotated:(BOOL)rotated offset:(CGPoint)offset originalSize:(CGSize)originalSize;
+
+@end
+
diff --git a/libs/cocos2d/CCSpriteFrame.m b/libs/cocos2d/CCSpriteFrame.m
new file mode 100644 (file)
index 0000000..e9ebd04
--- /dev/null
@@ -0,0 +1,111 @@
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2008-2011 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+
+#import "CCTextureCache.h"
+#import "CCSpriteFrame.h"
+#import "ccMacros.h"
+
+@implementation CCSpriteFrame
+@synthesize rotated = rotated_, offsetInPixels = offsetInPixels_, texture = texture_;
+@synthesize originalSizeInPixels=originalSizeInPixels_;
+
++(id) frameWithTexture:(CCTexture2D*)texture rect:(CGRect)rect
+{
+       return [[[self alloc] initWithTexture:texture rect:rect] autorelease];
+}
+
++(id) frameWithTexture:(CCTexture2D*)texture rectInPixels:(CGRect)rect rotated:(BOOL)rotated offset:(CGPoint)offset originalSize:(CGSize)originalSize
+{
+       return [[[self alloc] initWithTexture:texture rectInPixels:rect rotated:rotated offset:offset originalSize:originalSize] autorelease];
+}
+
+-(id) initWithTexture:(CCTexture2D*)texture rect:(CGRect)rect
+{
+       CGRect rectInPixels = CC_RECT_POINTS_TO_PIXELS( rect );
+       return [self initWithTexture:texture rectInPixels:rectInPixels rotated:NO offset:CGPointZero originalSize:rectInPixels.size];
+}
+
+-(id) initWithTexture:(CCTexture2D*)texture rectInPixels:(CGRect)rect rotated:(BOOL)rotated offset:(CGPoint)offset originalSize:(CGSize)originalSize
+{
+       if( (self=[super init]) ) {
+               self.texture = texture;
+               rectInPixels_ = rect;
+               rect_ = CC_RECT_PIXELS_TO_POINTS( rect );
+               rotated_ = rotated;
+               offsetInPixels_ = offset;
+               originalSizeInPixels_ = originalSize;
+       }
+       return self;    
+}
+
+- (NSString*) description
+{
+       return [NSString stringWithFormat:@"<%@ = %08X | TextureName=%d, Rect = (%.2f,%.2f,%.2f,%.2f)> rotated:%d", [self class], self,
+                       texture_.name,
+                       rect_.origin.x,
+                       rect_.origin.y,
+                       rect_.size.width,
+                       rect_.size.height,
+                       rotated_
+                       ];
+}
+
+- (void) dealloc
+{
+       CCLOGINFO( @"cocos2d: deallocing %@",self);
+       [texture_ release];
+       [super dealloc];
+}
+
+-(id) copyWithZone: (NSZone*) zone
+{
+       CCSpriteFrame *copy = [[[self class] allocWithZone: zone] initWithTexture:texture_ rectInPixels:rectInPixels_ rotated:rotated_ offset:offsetInPixels_ originalSize:originalSizeInPixels_];
+       return copy;
+}
+
+-(CGRect) rect
+{
+       return rect_;
+}
+
+-(CGRect) rectInPixels
+{
+       return rectInPixels_;
+}
+
+-(void) setRect:(CGRect)rect
+{
+       rect_ = rect;
+       rectInPixels_ = CC_RECT_POINTS_TO_PIXELS( rect_ );
+}
+
+-(void) setRectInPixels:(CGRect)rectInPixels
+{
+       rectInPixels_ = rectInPixels;
+       rect_ = CC_RECT_PIXELS_TO_POINTS(rectInPixels);
+}
+@end
diff --git a/libs/cocos2d/CCSpriteFrameCache.h b/libs/cocos2d/CCSpriteFrameCache.h
new file mode 100644 (file)
index 0000000..d3119a6
--- /dev/null
@@ -0,0 +1,137 @@
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2009 Jason Booth
+ *
+ * Copyright (c) 2009 Robert J Payne
+ *
+ * Copyright (c) 2008-2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ *
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+
+/*
+ * To create sprite frames and texture atlas, use this tool:
+ * http://zwoptex.zwopple.com/
+ */
+
+#import <Foundation/Foundation.h>
+
+#import "CCSpriteFrame.h"
+#import "CCTexture2D.h"
+
+@class CCSprite;
+
+/** Singleton that handles the loading of the sprite frames.
+ It saves in a cache the sprite frames.
+ @since v0.9
+ */
+@interface CCSpriteFrameCache : NSObject
+{
+       NSMutableDictionary *spriteFrames_;
+       NSMutableDictionary *spriteFramesAliases_;
+}
+
+/** Retruns ths shared instance of the Sprite Frame cache */
++ (CCSpriteFrameCache *) sharedSpriteFrameCache;
+
+/** Purges the cache. It releases all the Sprite Frames and the retained instance.
+ */
++(void)purgeSharedSpriteFrameCache;
+
+
+/** Adds multiple Sprite Frames with a dictionary. The texture will be associated with the created sprite frames.
+ */
+-(void) addSpriteFramesWithDictionary:(NSDictionary*)dictionary texture:(CCTexture2D*)texture;
+
+/** Adds multiple Sprite Frames from a plist file.
+ * A texture will be loaded automatically. The texture name will composed by replacing the .plist suffix with .png
+ * If you want to use another texture, you should use the addSpriteFramesWithFile:texture method.
+ */
+-(void) addSpriteFramesWithFile:(NSString*)plist;
+
+/** Adds multiple Sprite Frames from a plist file. The texture will be associated with the created sprite frames.
+ */
+-(void) addSpriteFramesWithFile:(NSString*)plist texture:(CCTexture2D*)texture;
+
+/** Adds multiple Sprite Frames from a plist file. The texture will be associated with the created sprite frames.
+ @since v0.99.5
+ */
+-(void) addSpriteFramesWithFile:(NSString*)plist textureFile:(NSString*)textureFileName;
+
+/** Adds an sprite frame with a given name.
+ If the name already exists, then the contents of the old name will be replaced with the new one.
+ */
+-(void) addSpriteFrame:(CCSpriteFrame*)frame name:(NSString*)frameName;
+
+
+/** Purges the dictionary of loaded sprite frames.
+ * Call this method if you receive the "Memory Warning".
+ * In the short term: it will free some resources preventing your app from being killed.
+ * In the medium term: it will allocate more resources.
+ * In the long term: it will be the same.
+ */
+-(void) removeSpriteFrames;
+
+/** Removes unused sprite frames.
+ * Sprite Frames that have a retain count of 1 will be deleted.
+ * It is convinient to call this method after when starting a new Scene.
+ */
+-(void) removeUnusedSpriteFrames;
+
+/** Deletes an sprite frame from the sprite frame cache.
+ */
+-(void) removeSpriteFrameByName:(NSString*)name;
+
+/** Removes multiple Sprite Frames from a plist file.
+* Sprite Frames stored in this file will be removed.
+* It is convinient to call this method when a specific texture needs to be removed.
+* @since v0.99.5
+*/
+- (void) removeSpriteFramesFromFile:(NSString*) plist;
+
+/** Removes multiple Sprite Frames from NSDictionary.
+ * @since v0.99.5
+ */
+- (void) removeSpriteFramesFromDictionary:(NSDictionary*) dictionary;
+
+/** Removes all Sprite Frames associated with the specified textures.
+ * It is convinient to call this method when a specific texture needs to be removed.
+ * @since v0.995.
+ */
+- (void) removeSpriteFramesFromTexture:(CCTexture2D*) texture;
+
+/** Returns an Sprite Frame that was previously added.
+ If the name is not found it will return nil.
+ You should retain the returned copy if you are going to use it.
+ */
+-(CCSpriteFrame*) spriteFrameByName:(NSString*)name;
+
+/** Creates an sprite with the name of an sprite frame.
+ The created sprite will contain the texture, rect and offset of the sprite frame.
+ It returns an autorelease object.
+ @deprecated use [CCSprite spriteWithSpriteFrameName:name]. This method will be removed on final v0.9
+ */
+-(CCSprite*) createSpriteWithFrameName:(NSString*)name DEPRECATED_ATTRIBUTE;
+
+@end
diff --git a/libs/cocos2d/CCSpriteFrameCache.m b/libs/cocos2d/CCSpriteFrameCache.m
new file mode 100644 (file)
index 0000000..5acb521
--- /dev/null
@@ -0,0 +1,347 @@
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2009 Jason Booth
+ *
+ * Copyright (c) 2009 Robert J Payne
+ *
+ * Copyright (c) 2008-2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+/*
+ * To create sprite frames and texture atlas, use this tool:
+ * http://zwoptex.zwopple.com/
+ */
+
+#import "Platforms/CCNS.h"
+#import "ccMacros.h"
+#import "CCTextureCache.h"
+#import "CCSpriteFrameCache.h"
+#import "CCSpriteFrame.h"
+#import "CCSprite.h"
+#import "Support/CCFileUtils.h"
+
+
+@implementation CCSpriteFrameCache
+
+#pragma mark CCSpriteFrameCache - Alloc, Init & Dealloc
+
+static CCSpriteFrameCache *sharedSpriteFrameCache_=nil;
+
++ (CCSpriteFrameCache *)sharedSpriteFrameCache
+{
+       if (!sharedSpriteFrameCache_)
+               sharedSpriteFrameCache_ = [[CCSpriteFrameCache alloc] init];
+               
+       return sharedSpriteFrameCache_;
+}
+
++(id)alloc
+{
+       NSAssert(sharedSpriteFrameCache_ == nil, @"Attempted to allocate a second instance of a singleton.");
+       return [super alloc];
+}
+
++(void)purgeSharedSpriteFrameCache
+{
+       [sharedSpriteFrameCache_ release];
+       sharedSpriteFrameCache_ = nil;
+}
+
+-(id) init
+{
+       if( (self=[super init]) ) {
+               spriteFrames_ = [[NSMutableDictionary alloc] initWithCapacity: 100];
+               spriteFramesAliases_ = [[NSMutableDictionary alloc] initWithCapacity:10];
+       }
+       
+       return self;
+}
+
+- (NSString*) description
+{
+       return [NSString stringWithFormat:@"<%@ = %08X | num of sprite frames =  %i>", [self class], self, [spriteFrames_ count]];
+}
+
+-(void) dealloc
+{
+       CCLOGINFO(@"cocos2d: deallocing %@", self);
+       
+       [spriteFrames_ release];
+       [spriteFramesAliases_ release];
+       [super dealloc];
+}
+
+#pragma mark CCSpriteFrameCache - loading sprite frames
+
+-(void) addSpriteFramesWithDictionary:(NSDictionary*)dictionary texture:(CCTexture2D*)texture
+{
+       /*
+        Supported Zwoptex Formats:
+        ZWTCoordinatesFormatOptionXMLLegacy = 0, // Flash Version
+        ZWTCoordinatesFormatOptionXML1_0 = 1, // Desktop Version 0.0 - 0.4b
+        ZWTCoordinatesFormatOptionXML1_1 = 2, // Desktop Version 1.0.0 - 1.0.1
+        ZWTCoordinatesFormatOptionXML1_2 = 3, // Desktop Version 1.0.2+
+       */
+       NSDictionary *metadataDict = [dictionary objectForKey:@"metadata"];
+       NSDictionary *framesDict = [dictionary objectForKey:@"frames"];
+
+       int format = 0;
+       
+       // get the format
+       if(metadataDict != nil)
+               format = [[metadataDict objectForKey:@"format"] intValue];
+       
+       // check the format
+       NSAssert( format >= 0 && format <= 3, @"cocos2d: WARNING: format is not supported for CCSpriteFrameCache addSpriteFramesWithDictionary:texture:");
+       
+       
+       // add real frames
+       for(NSString *frameDictKey in framesDict) {
+               NSDictionary *frameDict = [framesDict objectForKey:frameDictKey];
+               CCSpriteFrame *spriteFrame;
+               if(format == 0) {
+                       float x = [[frameDict objectForKey:@"x"] floatValue];
+                       float y = [[frameDict objectForKey:@"y"] floatValue];
+                       float w = [[frameDict objectForKey:@"width"] floatValue];
+                       float h = [[frameDict objectForKey:@"height"] floatValue];
+                       float ox = [[frameDict objectForKey:@"offsetX"] floatValue];
+                       float oy = [[frameDict objectForKey:@"offsetY"] floatValue];
+                       int ow = [[frameDict objectForKey:@"originalWidth"] intValue];
+                       int oh = [[frameDict objectForKey:@"originalHeight"] intValue];
+                       // check ow/oh
+                       if(!ow || !oh)
+                               CCLOG(@"cocos2d: WARNING: originalWidth/Height not found on the CCSpriteFrame. AnchorPoint won't work as expected. Regenerate the .plist");
+                       
+                       // abs ow/oh
+                       ow = abs(ow);
+                       oh = abs(oh);
+                       // create frame
+                       
+                       spriteFrame = [[CCSpriteFrame alloc] initWithTexture:texture
+                                                                                                       rectInPixels:CGRectMake(x, y, w, h)
+                                                                                                                rotated:NO
+                                                                                                                 offset:CGPointMake(ox, oy)
+                                                                                                       originalSize:CGSizeMake(ow, oh)];
+               } else if(format == 1 || format == 2) {
+                       CGRect frame = CCRectFromString([frameDict objectForKey:@"frame"]);
+                       BOOL rotated = NO;
+                       
+                       // rotation
+                       if(format == 2)
+                               rotated = [[frameDict objectForKey:@"rotated"] boolValue];
+                       
+                       CGPoint offset = CCPointFromString([frameDict objectForKey:@"offset"]);
+                       CGSize sourceSize = CCSizeFromString([frameDict objectForKey:@"sourceSize"]);
+                       
+                       // create frame
+                       spriteFrame = [[CCSpriteFrame alloc] initWithTexture:texture
+                                                                                                       rectInPixels:frame
+                                                                                                                rotated:rotated
+                                                                                                                 offset:offset
+                                                                                                       originalSize:sourceSize];
+               } else if(format == 3) {
+                       // get values
+                       CGSize spriteSize = CCSizeFromString([frameDict objectForKey:@"spriteSize"]);
+                       CGPoint spriteOffset = CCPointFromString([frameDict objectForKey:@"spriteOffset"]);
+                       CGSize spriteSourceSize = CCSizeFromString([frameDict objectForKey:@"spriteSourceSize"]);
+                       CGRect textureRect = CCRectFromString([frameDict objectForKey:@"textureRect"]);
+                       BOOL textureRotated = [[frameDict objectForKey:@"textureRotated"] boolValue];
+                       
+                       // get aliases
+                       NSArray *aliases = [frameDict objectForKey:@"aliases"];
+                       for(NSString *alias in aliases) {
+                               if( [spriteFramesAliases_ objectForKey:alias] )
+                                       CCLOG(@"cocos2d: WARNING: an alias with name %@ already exists",alias);
+                               
+                               [spriteFramesAliases_ setObject:frameDictKey forKey:alias];
+                       }
+                       
+                       // create frame
+                       spriteFrame = [[CCSpriteFrame alloc] initWithTexture:texture 
+                                                                                                       rectInPixels:CGRectMake(textureRect.origin.x, textureRect.origin.y, spriteSize.width, spriteSize.height) 
+                                                                                                                rotated:textureRotated 
+                                                                                                                 offset:spriteOffset 
+                                                                                                       originalSize:spriteSourceSize];
+               }
+
+               // add sprite frame
+               [spriteFrames_ setObject:spriteFrame forKey:frameDictKey];
+               [spriteFrame release];
+       }
+}
+
+-(void) addSpriteFramesWithFile:(NSString*)plist texture:(CCTexture2D*)texture
+{
+       NSString *path = [CCFileUtils fullPathFromRelativePath:plist];
+       NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:path];
+
+       return [self addSpriteFramesWithDictionary:dict texture:texture];
+}
+
+-(void) addSpriteFramesWithFile:(NSString*)plist textureFile:(NSString*)textureFileName
+{
+       NSAssert( textureFileName, @"Invalid texture file name");
+       CCTexture2D *texture = [[CCTextureCache sharedTextureCache] addImage:textureFileName];
+       
+       if( texture )
+               [self addSpriteFramesWithFile:plist texture:texture];
+       else
+               CCLOG(@"cocos2d: CCSpriteFrameCache: couldn't load texture file. File not found: %@", textureFileName);
+}
+
+-(void) addSpriteFramesWithFile:(NSString*)plist
+{
+    NSString *path = [CCFileUtils fullPathFromRelativePath:plist];
+    NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:path];
+       
+    NSString *texturePath = nil;
+    NSDictionary *metadataDict = [dict objectForKey:@"metadata"];
+    if( metadataDict )
+        // try to read  texture file name from meta data
+        texturePath = [metadataDict objectForKey:@"textureFileName"];
+       
+       
+    if( texturePath )
+    {
+        // build texture path relative to plist file
+        NSString *textureBase = [plist stringByDeletingLastPathComponent];
+        texturePath = [textureBase stringByAppendingPathComponent:texturePath];
+    } else {
+        // build texture path by replacing file extension
+        texturePath = [plist stringByDeletingPathExtension];
+        texturePath = [texturePath stringByAppendingPathExtension:@"png"];
+               
+               CCLOG(@"cocos2d: CCSpriteFrameCache: Trying to use file '%@' as texture", texturePath); 
+    }
+       
+    CCTexture2D *texture = [[CCTextureCache sharedTextureCache] addImage:texturePath];
+       
+       if( texture )
+               [self addSpriteFramesWithDictionary:dict texture:texture];
+       
+       else
+               CCLOG(@"cocos2d: CCSpriteFrameCache: Couldn't load texture");
+}
+
+-(void) addSpriteFrame:(CCSpriteFrame*)frame name:(NSString*)frameName
+{
+       [spriteFrames_ setObject:frame forKey:frameName];
+}
+
+#pragma mark CCSpriteFrameCache - removing
+
+-(void) removeSpriteFrames
+{
+       [spriteFrames_ removeAllObjects];
+       [spriteFramesAliases_ removeAllObjects];
+}
+
+-(void) removeUnusedSpriteFrames
+{
+       NSArray *keys = [spriteFrames_ allKeys];
+       for( id key in keys ) {
+               id value = [spriteFrames_ objectForKey:key];            
+               if( [value retainCount] == 1 ) {
+                       CCLOG(@"cocos2d: CCSpriteFrameCache: removing unused frame: %@", key);
+                       [spriteFrames_ removeObjectForKey:key];
+               }
+       }       
+}
+
+-(void) removeSpriteFrameByName:(NSString*)name
+{
+       // explicit nil handling
+       if( ! name )
+               return;
+       
+       // Is this an alias ?
+       NSString *key = [spriteFramesAliases_ objectForKey:name];
+       
+       if( key ) {
+               [spriteFrames_ removeObjectForKey:key];
+               [spriteFramesAliases_ removeObjectForKey:name];
+
+       } else
+               [spriteFrames_ removeObjectForKey:name];
+}
+
+- (void) removeSpriteFramesFromFile:(NSString*) plist
+{
+       NSString *path = [CCFileUtils fullPathFromRelativePath:plist];
+       NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:path];
+       
+       [self removeSpriteFramesFromDictionary:dict];
+}
+
+- (void) removeSpriteFramesFromDictionary:(NSDictionary*) dictionary
+{
+       NSDictionary *framesDict = [dictionary objectForKey:@"frames"];
+       NSMutableArray *keysToRemove=[NSMutableArray array];
+       
+       for(NSString *frameDictKey in framesDict)
+       {
+               if ([spriteFrames_ objectForKey:frameDictKey]!=nil)
+                       [keysToRemove addObject:frameDictKey];
+       }
+       [spriteFrames_ removeObjectsForKeys:keysToRemove];
+}
+
+- (void) removeSpriteFramesFromTexture:(CCTexture2D*) texture
+{
+       NSMutableArray *keysToRemove=[NSMutableArray array];
+       
+       for (NSString *spriteFrameKey in spriteFrames_)
+       {
+               if ([[spriteFrames_ valueForKey:spriteFrameKey] texture] == texture) 
+                       [keysToRemove addObject:spriteFrameKey];
+               
+       }
+       [spriteFrames_ removeObjectsForKeys:keysToRemove];
+}
+
+#pragma mark CCSpriteFrameCache - getting
+
+-(CCSpriteFrame*) spriteFrameByName:(NSString*)name
+{
+       CCSpriteFrame *frame = [spriteFrames_ objectForKey:name];
+       if( ! frame ) {
+               // try alias dictionary
+               NSString *key = [spriteFramesAliases_ objectForKey:name];
+               frame = [spriteFrames_ objectForKey:key];
+               
+               if( ! frame )
+                       CCLOG(@"cocos2d: CCSpriteFrameCache: Frame '%@' not found", name);
+       }
+       
+       return frame;
+}
+
+#pragma mark CCSpriteFrameCache - sprite creation
+
+-(CCSprite*) createSpriteWithFrameName:(NSString*)name
+{
+       CCSpriteFrame *frame = [spriteFrames_ objectForKey:name];
+       return [CCSprite spriteWithSpriteFrame:frame];
+}
+@end
diff --git a/libs/cocos2d/CCTMXLayer.h b/libs/cocos2d/CCTMXLayer.h
new file mode 100644 (file)
index 0000000..79a397c
--- /dev/null
@@ -0,0 +1,152 @@
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2009-2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ *
+ * TMX Tiled Map support:
+ * http://www.mapeditor.org
+ *
+ */
+
+
+#import "CCAtlasNode.h"
+#import "CCSpriteBatchNode.h"
+
+
+@class CCTMXMapInfo;
+@class CCTMXLayerInfo;
+@class CCTMXTilesetInfo;
+
+/** CCTMXLayer represents the TMX layer.
+ It is a subclass of CCSpriteSheet. By default the tiles are rendered using a CCTextureAtlas.
+ If you mofify a tile on runtime, then, that tile will become a CCSprite, otherwise no CCSprite objects are created.
+ The benefits of using CCSprite objects as tiles are:
+ - tiles (CCSprite) can be rotated/scaled/moved with a nice API
+ If the layer contains a property named "cc_vertexz" with an integer (in can be positive or negative),
+ then all the tiles belonging to the layer will use that value as their OpenGL vertex Z for depth.
+
+ On the other hand, if the "cc_vertexz" property has the "automatic" value, then the tiles will use an automatic vertex Z value.
+ Also before drawing the tiles, GL_ALPHA_TEST will be enabled, and disabled after drawing them. The used alpha func will be:
+
+    glAlphaFunc( GL_GREATER, value )
+ "value" by default is 0, but you can change it from Tiled by adding the "cc_alpha_func" property to the layer.
+ The value 0 should work for most cases, but if you have tiles that are semi-transparent, then you might want to use a differnt
+ value, like 0.5.
+ For further information, please see the programming guide:
+       http://www.cocos2d-iphone.org/wiki/doku.php/prog_guide:tiled_maps
+ @since v0.8.1
+ */
+@interface CCTMXLayer : CCSpriteBatchNode
+{
+       CCTMXTilesetInfo        *tileset_;
+       NSString                        *layerName_;
+       CGSize                          layerSize_;
+       CGSize                          mapTileSize_;
+       uint32_t                        *tiles_;                        // GID are 32 bit
+       NSUInteger                      layerOrientation_;
+       NSMutableArray          *properties_;
+       
+       unsigned char           opacity_; // TMX Layer supports opacity
+       
+       NSUInteger                      minGID_;
+       NSUInteger                      maxGID_;
+       
+       // Only used when vertexZ is used
+       NSInteger                       vertexZvalue_;
+       BOOL                            useAutomaticVertexZ_;
+       float                           alphaFuncValue_;
+       
+       // used for optimization
+       CCSprite                *reusedTile_;
+       ccCArray                *atlasIndexArray_;
+}
+/** name of the layer */
+@property (nonatomic,readwrite,retain) NSString *layerName;
+/** size of the layer in tiles */
+@property (nonatomic,readwrite) CGSize layerSize;
+/** size of the map's tile (could be differnt from the tile's size) */
+@property (nonatomic,readwrite) CGSize mapTileSize;
+/** pointer to the map of tiles */
+@property (nonatomic,readwrite) uint32_t *tiles;
+/** Tilset information for the layer */
+@property (nonatomic,readwrite,retain) CCTMXTilesetInfo *tileset;
+/** Layer orientation, which is the same as the map orientation */
+@property (nonatomic,readwrite) NSUInteger layerOrientation;
+/** properties from the layer. They can be added using Tiled */
+@property (nonatomic,readwrite,retain) NSMutableArray *properties;
+
+/** creates a CCTMXLayer with an tileset info, a layer info and a map info */
++(id) layerWithTilesetInfo:(CCTMXTilesetInfo*)tilesetInfo layerInfo:(CCTMXLayerInfo*)layerInfo mapInfo:(CCTMXMapInfo*)mapInfo;
+/** initializes a CCTMXLayer with a tileset info, a layer info and a map info */
+-(id) initWithTilesetInfo:(CCTMXTilesetInfo*)tilesetInfo layerInfo:(CCTMXLayerInfo*)layerInfo mapInfo:(CCTMXMapInfo*)mapInfo;
+
+/** dealloc the map that contains the tile position from memory.
+ Unless you want to know at runtime the tiles positions, you can safely call this method.
+ If you are going to call [layer tileGIDAt:] then, don't release the map
+ */
+-(void) releaseMap;
+
+/** returns the tile (CCSprite) at a given a tile coordinate.
+ The returned CCSprite will be already added to the CCTMXLayer. Don't add it again.
+ The CCSprite can be treated like any other CCSprite: rotated, scaled, translated, opacity, color, etc.
+ You can remove either by calling:
+       - [layer removeChild:sprite cleanup:cleanup];
+       - or [layer removeTileAt:ccp(x,y)];
+ */
+-(CCSprite*) tileAt:(CGPoint)tileCoordinate;
+
+/** returns the tile gid at a given tile coordinate.
+ if it returns 0, it means that the tile is empty.
+ This method requires the the tile map has not been previously released (eg. don't call [layer releaseMap])
+ */
+-(uint32_t) tileGIDAt:(CGPoint)tileCoordinate;
+
+/** sets the tile gid (gid = tile global id) at a given tile coordinate.
+ The Tile GID can be obtained by using the method "tileGIDAt" or by using the TMX editor -> Tileset Mgr +1.
+ If a tile is already placed at that position, then it will be removed.
+ */
+-(void) setTileGID:(uint32_t)gid at:(CGPoint)tileCoordinate;
+
+/** removes a tile at given tile coordinate */
+-(void) removeTileAt:(CGPoint)tileCoordinate;
+
+/** returns the position in pixels of a given tile coordinate */
+-(CGPoint) positionAt:(CGPoint)tileCoordinate;
+
+/** return the value for the specific property name */
+-(id) propertyNamed:(NSString *)propertyName;
+
+/** Creates the tiles */
+-(void) setupTiles;
+
+/** CCTMXLayer doesn't support adding a CCSprite manually.
+ @warning addchild:z:tag: is not supported on CCTMXLayer. Instead of setTileGID:at:/tileAt:
+ */
+-(void) addChild: (CCNode*)node z:(NSInteger)z tag:(NSInteger)tag;
+@end
diff --git a/libs/cocos2d/CCTMXLayer.m b/libs/cocos2d/CCTMXLayer.m
new file mode 100644 (file)
index 0000000..ca045a8
--- /dev/null
@@ -0,0 +1,668 @@
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2009-2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ *
+ * TMX Tiled Map support:
+ * http://www.mapeditor.org
+ *
+ */
+
+#import "CCTMXLayer.h"
+#import "CCTMXTiledMap.h"
+#import "CCTMXXMLParser.h"
+#import "CCSprite.h"
+#import "CCSpriteBatchNode.h"
+#import "CCTextureCache.h"
+#import "Support/CGPointExtension.h"
+
+#pragma mark -
+#pragma mark CCSpriteBatchNode Extension
+
+/* IMPORTANT XXX IMPORTNAT:
+ * These 2 methods can't be part of CCTMXLayer since they call [super add...], and CCSpriteSheet#add SHALL not be called
+ */
+@implementation CCSpriteBatchNode (TMXTiledMapExtension)
+
+/* Adds a quad into the texture atlas but it won't be added into the children array.
+ This method should be called only when you are dealing with very big AtlasSrite and when most of the CCSprite won't be updated.
+ For example: a tile map (CCTMXMap) or a label with lots of characgers (BitmapFontAtlas)
+ */
+-(void) addQuadFromSprite:(CCSprite*)sprite quadIndex:(NSUInteger)index
+{
+       NSAssert( sprite != nil, @"Argument must be non-nil");
+       NSAssert( [sprite isKindOfClass:[CCSprite class]], @"CCSpriteSheet only supports CCSprites as children");
+       
+       
+       while(index >= textureAtlas_.capacity || textureAtlas_.capacity == textureAtlas_.totalQuads )
+               [self increaseAtlasCapacity];
+
+       //
+       // update the quad directly. Don't add the sprite to the scene graph
+       //
+
+       [sprite useBatchNode:self];
+       [sprite setAtlasIndex:index];
+
+       ccV3F_C4B_T2F_Quad quad = [sprite quad];
+       [textureAtlas_ insertQuad:&quad atIndex:index];
+       
+       // XXX: updateTransform will update the textureAtlas too using updateQuad.
+       // XXX: so, it should be AFTER the insertQuad
+       [sprite setDirty:YES];
+       [sprite updateTransform];
+}
+
+/* This is the opposite of "addQuadFromSprite.
+ It add the sprite to the children and descendants array, but it doesn't update add it to the texture atlas
+ */
+-(id) addSpriteWithoutQuad:(CCSprite*)child z:(NSUInteger)z tag:(NSInteger)aTag
+{
+       NSAssert( child != nil, @"Argument must be non-nil");
+       NSAssert( [child isKindOfClass:[CCSprite class]], @"CCSpriteSheet only supports CCSprites as children");
+       
+       // quad index is Z
+       [child setAtlasIndex:z];
+       
+       // XXX: optimize with a binary search
+       int i=0;
+       for( CCSprite *c in descendants_ ) {
+               if( c.atlasIndex >= z )
+                       break;
+               i++;
+       }
+       [descendants_ insertObject:child atIndex:i];
+       
+       
+       // IMPORTANT: Call super, and not self. Avoid adding it to the texture atlas array
+       [super addChild:child z:z tag:aTag];
+       return self;    
+}
+@end
+
+
+#pragma mark -
+#pragma mark CCTMXLayer
+
+@interface CCTMXLayer (Private)
+-(CGPoint) positionForIsoAt:(CGPoint)pos;
+-(CGPoint) positionForOrthoAt:(CGPoint)pos;
+-(CGPoint) positionForHexAt:(CGPoint)pos;
+
+-(CGPoint) calculateLayerOffset:(CGPoint)offset;
+
+/* optimization methos */
+-(CCSprite*) appendTileForGID:(uint32_t)gid at:(CGPoint)pos;
+-(CCSprite*) insertTileForGID:(uint32_t)gid at:(CGPoint)pos;
+-(CCSprite*) updateTileForGID:(uint32_t)gid at:(CGPoint)pos;
+
+/* The layer recognizes some special properties, like cc_vertez */
+-(void) parseInternalProperties;
+
+-(NSInteger) vertexZForPos:(CGPoint)pos;
+
+// adding quad from sprite
+-(void)addQuadFromSprite:(CCSprite*)sprite quadIndex:(NSUInteger)index;
+
+// adds an sprite without the quad
+-(id)addSpriteWithoutQuad:(CCSprite*)child z:(NSInteger)z tag:(NSInteger)aTag;
+
+// index
+-(NSUInteger) atlasIndexForExistantZ:(NSUInteger)z;
+-(NSUInteger) atlasIndexForNewZ:(NSUInteger)z;
+@end
+
+@implementation CCTMXLayer
+@synthesize layerSize = layerSize_, layerName = layerName_, tiles = tiles_;
+@synthesize tileset = tileset_;
+@synthesize layerOrientation = layerOrientation_;
+@synthesize mapTileSize = mapTileSize_;
+@synthesize properties = properties_;
+
+#pragma mark CCTMXLayer - init & alloc & dealloc
+
++(id) layerWithTilesetInfo:(CCTMXTilesetInfo*)tilesetInfo layerInfo:(CCTMXLayerInfo*)layerInfo mapInfo:(CCTMXMapInfo*)mapInfo
+{
+       return [[[self alloc] initWithTilesetInfo:tilesetInfo layerInfo:layerInfo mapInfo:mapInfo] autorelease];
+}
+
+-(id) initWithTilesetInfo:(CCTMXTilesetInfo*)tilesetInfo layerInfo:(CCTMXLayerInfo*)layerInfo mapInfo:(CCTMXMapInfo*)mapInfo
+{      
+       // XXX: is 35% a good estimate ?
+       CGSize size = layerInfo.layerSize;
+       float totalNumberOfTiles = size.width * size.height;
+       float capacity = totalNumberOfTiles * 0.35f + 1; // 35 percent is occupied ?
+       
+       CCTexture2D *tex = nil;
+       if( tilesetInfo )
+               tex = [[CCTextureCache sharedTextureCache] addImage:tilesetInfo.sourceImage];
+       
+       if((self = [super initWithTexture:tex capacity:capacity])) {            
+
+               // layerInfo
+               self.layerName = layerInfo.name;
+               layerSize_ = layerInfo.layerSize;
+               tiles_ = layerInfo.tiles;
+               minGID_ = layerInfo.minGID;
+               maxGID_ = layerInfo.maxGID;
+               opacity_ = layerInfo.opacity;
+               self.properties = [NSMutableDictionary dictionaryWithDictionary:layerInfo.properties];
+
+               // tilesetInfo
+               self.tileset = tilesetInfo;
+               
+               // mapInfo
+               mapTileSize_ = mapInfo.tileSize;
+               layerOrientation_ = mapInfo.orientation;
+               
+               // offset (after layer orientation is set);
+               CGPoint offset = [self calculateLayerOffset:layerInfo.offset];
+               [self setPositionInPixels:offset];
+               
+               atlasIndexArray_ = ccCArrayNew(totalNumberOfTiles);
+               
+               [self setContentSizeInPixels: CGSizeMake( layerSize_.width * mapTileSize_.width, layerSize_.height * mapTileSize_.height )];
+               
+               useAutomaticVertexZ_= NO;
+               vertexZvalue_ = 0;
+               alphaFuncValue_ = 0;
+
+       }
+       return self;
+}
+
+- (void) dealloc
+{
+       [layerName_ release];
+       [tileset_ release];
+       [reusedTile_ release];
+       [properties_ release];
+       
+       if( atlasIndexArray_ ) {
+               ccCArrayFree(atlasIndexArray_);
+               atlasIndexArray_ = NULL;
+       }
+       
+       if( tiles_ ) {
+               free(tiles_);
+               tiles_ = NULL;
+       }
+       
+       [super dealloc];
+}
+
+-(void) releaseMap
+{
+       if( tiles_) {
+               free( tiles_);
+               tiles_ = NULL;
+       }
+       
+       if( atlasIndexArray_ ) {
+               ccCArrayFree(atlasIndexArray_);
+               atlasIndexArray_ = NULL;
+       }
+}
+
+#pragma mark CCTMXLayer - setup Tiles
+
+-(void) setupTiles
+{      
+       // Optimization: quick hack that sets the image size on the tileset
+       tileset_.imageSize = [textureAtlas_.texture contentSizeInPixels];
+       
+       // By default all the tiles are aliased
+       // pros:
+       //  - easier to render
+       // cons:
+       //  - difficult to scale / rotate / etc.
+       [textureAtlas_.texture setAliasTexParameters];
+       
+       CFByteOrder o = CFByteOrderGetCurrent();
+               
+       // Parse cocos2d properties
+       [self parseInternalProperties];
+       
+       for( NSUInteger y=0; y < layerSize_.height; y++ ) {
+               for( NSUInteger x=0; x < layerSize_.width; x++ ) {
+                       
+                       NSUInteger pos = x + layerSize_.width * y;
+                       uint32_t gid = tiles_[ pos ];
+                       
+                       // gid are stored in little endian.
+                       // if host is big endian, then swap
+                       if( o == CFByteOrderBigEndian )
+                               gid = CFSwapInt32( gid );
+                       
+                       // XXX: gid == 0 --> empty tile
+                       if( gid != 0 ) {
+                               [self appendTileForGID:gid at:ccp(x,y)];
+                               
+                               // Optimization: update min and max GID rendered by the layer
+                               minGID_ = MIN(gid, minGID_);
+                               maxGID_ = MAX(gid, maxGID_);
+                       }
+               }
+       }
+       
+       NSAssert( maxGID_ >= tileset_.firstGid &&
+                        minGID_ >= tileset_.firstGid, @"TMX: Only 1 tilset per layer is supported");   
+}
+
+#pragma mark CCTMXLayer - Properties
+
+-(id) propertyNamed:(NSString *)propertyName 
+{
+       return [properties_ valueForKey:propertyName];
+}
+
+-(void) parseInternalProperties
+{
+       // if cc_vertex=automatic, then tiles will be rendered using vertexz
+
+       NSString *vertexz = [self propertyNamed:@"cc_vertexz"];
+       if( vertexz ) {
+               if( [vertexz isEqualToString:@"automatic"] )
+                       useAutomaticVertexZ_ = YES;
+               else
+                       vertexZvalue_ = [vertexz intValue];
+       }
+       
+       NSString *alphaFuncVal = [self propertyNamed:@"cc_alpha_func"];
+       alphaFuncValue_ = [alphaFuncVal floatValue];
+}
+
+#pragma mark CCTMXLayer - obtaining tiles/gids
+
+-(CCSprite*) tileAt:(CGPoint)pos
+{
+       NSAssert( pos.x < layerSize_.width && pos.y < layerSize_.height && pos.x >=0 && pos.y >=0, @"TMXLayer: invalid position");
+       NSAssert( tiles_ && atlasIndexArray_, @"TMXLayer: the tiles map has been released");
+       
+       CCSprite *tile = nil;
+       uint32_t gid = [self tileGIDAt:pos];
+       
+       // if GID == 0, then no tile is present
+       if( gid ) {
+               int z = pos.x + pos.y * layerSize_.width;
+               tile = (CCSprite*) [self getChildByTag:z];
+               
+               // tile not created yet. create it
+               if( ! tile ) {
+                       CGRect rect = [tileset_ rectForGID:gid];                        
+                       tile = [[CCSprite alloc] initWithBatchNode:self rectInPixels:rect];
+                       [tile setPositionInPixels: [self positionAt:pos]];
+                       [tile setVertexZ: [self vertexZForPos:pos]];
+                       tile.anchorPoint = CGPointZero;
+                       [tile setOpacity:opacity_];
+                       
+                       NSUInteger indexForZ = [self atlasIndexForExistantZ:z];
+                       [self addSpriteWithoutQuad:tile z:indexForZ tag:z];
+                       [tile release];
+               }
+       }
+       return tile;
+}
+
+-(uint32_t) tileGIDAt:(CGPoint)pos
+{
+       NSAssert( pos.x < layerSize_.width && pos.y < layerSize_.height && pos.x >=0 && pos.y >=0, @"TMXLayer: invalid position");
+       NSAssert( tiles_ && atlasIndexArray_, @"TMXLayer: the tiles map has been released");
+       
+       NSInteger idx = pos.x + pos.y * layerSize_.width;
+       return tiles_[ idx ];
+}
+
+#pragma mark CCTMXLayer - adding helper methods
+
+-(CCSprite*) insertTileForGID:(uint32_t)gid at:(CGPoint)pos
+{
+       CGRect rect = [tileset_ rectForGID:gid];
+       
+       NSInteger z = pos.x + pos.y * layerSize_.width;
+       
+       if( ! reusedTile_ )
+               reusedTile_ = [[CCSprite alloc] initWithBatchNode:self rectInPixels:rect];
+       else
+               [reusedTile_ initWithBatchNode:self rectInPixels:rect];
+       
+       [reusedTile_ setPositionInPixels: [self positionAt:pos]];
+       [reusedTile_ setVertexZ: [self vertexZForPos:pos]];
+       reusedTile_.anchorPoint = CGPointZero;
+       [reusedTile_ setOpacity:opacity_];
+       
+       // get atlas index
+       NSUInteger indexForZ = [self atlasIndexForNewZ:z];
+       
+       // Optimization: add the quad without adding a child
+       [self addQuadFromSprite:reusedTile_ quadIndex:indexForZ];
+       
+       // insert it into the local atlasindex array
+       ccCArrayInsertValueAtIndex(atlasIndexArray_, (void*)z, indexForZ);
+       
+       // update possible children
+       CCSprite *sprite;
+       CCARRAY_FOREACH(children_, sprite) {
+               NSUInteger ai = [sprite atlasIndex];
+               if( ai >= indexForZ)
+                       [sprite setAtlasIndex: ai+1];
+       }
+       
+       tiles_[z] = gid;
+       
+       return reusedTile_;
+}
+
+-(CCSprite*) updateTileForGID:(uint32_t)gid at:(CGPoint)pos
+{
+       CGRect rect = [tileset_ rectForGID:gid];
+       
+       int z = pos.x + pos.y * layerSize_.width;
+       
+       if( ! reusedTile_ )
+               reusedTile_ = [[CCSprite alloc] initWithBatchNode:self rectInPixels:rect];
+       else
+               [reusedTile_ initWithBatchNode:self rectInPixels:rect];
+       
+       [reusedTile_ setPositionInPixels: [self positionAt:pos]];
+       [reusedTile_ setVertexZ: [self vertexZForPos:pos]];
+       reusedTile_.anchorPoint = CGPointZero;
+       [reusedTile_ setOpacity:opacity_];
+       
+       // get atlas index
+       NSUInteger indexForZ = [self atlasIndexForExistantZ:z];
+
+       [reusedTile_ setAtlasIndex:indexForZ];
+       [reusedTile_ setDirty:YES];
+       [reusedTile_ updateTransform];
+       tiles_[z] = gid;
+       
+       return reusedTile_;
+}
+
+
+// used only when parsing the map. useless after the map was parsed
+// since lot's of assumptions are no longer true
+-(CCSprite*) appendTileForGID:(uint32_t)gid at:(CGPoint)pos
+{
+       CGRect rect = [tileset_ rectForGID:gid];
+       
+       NSInteger z = pos.x + pos.y * layerSize_.width;
+       
+       if( ! reusedTile_ )
+               reusedTile_ = [[CCSprite alloc] initWithBatchNode:self rectInPixels:rect];
+       else
+               [reusedTile_ initWithBatchNode:self rectInPixels:rect];
+       
+       [reusedTile_ setPositionInPixels: [self positionAt:pos]];
+       [reusedTile_ setVertexZ: [self vertexZForPos:pos]];
+       reusedTile_.anchorPoint = CGPointZero;
+       [reusedTile_ setOpacity:opacity_];
+       
+       // optimization:
+       // The difference between appendTileForGID and insertTileforGID is that append is faster, since
+       // it appends the tile at the end of the texture atlas
+       NSUInteger indexForZ = atlasIndexArray_->num;
+
+
+       // don't add it using the "standard" way.
+       [self addQuadFromSprite:reusedTile_ quadIndex:indexForZ];
+       
+       
+       // append should be after addQuadFromSprite since it modifies the quantity values
+       ccCArrayInsertValueAtIndex(atlasIndexArray_, (void*)z, indexForZ);
+       
+       return reusedTile_;
+}
+
+#pragma mark CCTMXLayer - atlasIndex and Z
+
+int compareInts (const void * a, const void * b)
+{
+       return ( *(int*)a - *(int*)b );
+}
+
+-(NSUInteger) atlasIndexForExistantZ:(NSUInteger)z
+{
+       NSInteger key = z;
+       NSInteger *item = bsearch((void*)&key, (void*)&atlasIndexArray_->arr[0], atlasIndexArray_->num, sizeof(void*), compareInts);
+       
+       NSAssert( item, @"TMX atlas index not found. Shall not happen");
+
+       NSUInteger index = ((NSInteger)item - (NSInteger)atlasIndexArray_->arr) / sizeof(void*);
+       return index;
+}
+
+-(NSUInteger)atlasIndexForNewZ:(NSUInteger)z
+{
+       // XXX: This can be improved with a sort of binary search
+       NSUInteger i = 0;
+       for(i = 0; i< atlasIndexArray_->num; i++) {
+               NSUInteger val = (NSUInteger) atlasIndexArray_->arr[i];
+               if( z < val )
+                       break;
+       }       
+       return i;
+}
+
+#pragma mark CCTMXLayer - adding / remove tiles
+
+-(void) setTileGID:(uint32_t)gid at:(CGPoint)pos
+{
+       NSAssert( pos.x < layerSize_.width && pos.y < layerSize_.height && pos.x >=0 && pos.y >=0, @"TMXLayer: invalid position");
+       NSAssert( tiles_ && atlasIndexArray_, @"TMXLayer: the tiles map has been released");
+       NSAssert( gid == 0 || gid >= tileset_.firstGid, @"TMXLayer: invalid gid" );
+
+       uint32_t currentGID = [self tileGIDAt:pos];
+       
+       if( currentGID != gid ) {
+               
+               // setting gid=0 is equal to remove the tile
+               if( gid == 0 )
+                       [self removeTileAt:pos];
+
+               // empty tile. create a new one
+               else if( currentGID == 0 )
+                       [self insertTileForGID:gid at:pos];
+
+               // modifying an existing tile with a non-empty tile
+               else {
+
+                       NSUInteger z = pos.x + pos.y * layerSize_.width;
+                       id sprite = [self getChildByTag:z];
+                       if( sprite ) {
+                               CGRect rect = [tileset_ rectForGID:gid];
+                               [sprite setTextureRectInPixels:rect rotated:NO untrimmedSize:rect.size];
+                               tiles_[z] = gid;
+                       } else
+                               [self updateTileForGID:gid at:pos];
+               }
+       }
+}
+
+-(void) addChild: (CCNode*)node z:(NSInteger)z tag:(NSInteger)tag
+{
+       NSAssert(NO, @"addChild: is not supported on CCTMXLayer. Instead use setTileGID:at:/tileAt:");
+}
+
+-(void) removeChild:(CCSprite*)sprite cleanup:(BOOL)cleanup
+{
+       // allows removing nil objects
+       if( ! sprite )
+               return;
+
+       NSAssert( [children_ containsObject:sprite], @"Tile does not belong to TMXLayer");
+       
+       NSUInteger atlasIndex = [sprite atlasIndex];
+       NSUInteger zz = (NSUInteger) atlasIndexArray_->arr[atlasIndex];
+       tiles_[zz] = 0;
+       ccCArrayRemoveValueAtIndex(atlasIndexArray_, atlasIndex);
+       [super removeChild:sprite cleanup:cleanup];
+}
+
+-(void) removeTileAt:(CGPoint)pos
+{
+       NSAssert( pos.x < layerSize_.width && pos.y < layerSize_.height && pos.x >=0 && pos.y >=0, @"TMXLayer: invalid position");
+       NSAssert( tiles_ && atlasIndexArray_, @"TMXLayer: the tiles map has been released");
+
+       uint32_t gid = [self tileGIDAt:pos];
+       
+       if( gid ) {
+               
+               NSUInteger z = pos.x + pos.y * layerSize_.width;
+               NSUInteger atlasIndex = [self atlasIndexForExistantZ:z];
+               
+               // remove tile from GID map
+               tiles_[z] = 0;
+
+               // remove tile from atlas position array
+               ccCArrayRemoveValueAtIndex(atlasIndexArray_, atlasIndex);
+               
+               // remove it from sprites and/or texture atlas
+               id sprite = [self getChildByTag:z];
+               if( sprite )
+                       [super removeChild:sprite cleanup:YES];
+               else {
+                       [textureAtlas_ removeQuadAtIndex:atlasIndex];
+
+                       // update possible children
+                       CCARRAY_FOREACH(children_, sprite) {
+                               NSUInteger ai = [sprite atlasIndex];
+                               if( ai >= atlasIndex) {
+                                       [sprite setAtlasIndex: ai-1];
+                               }
+                       }
+               }
+       }
+}
+
+#pragma mark CCTMXLayer - obtaining positions, offset
+
+-(CGPoint) calculateLayerOffset:(CGPoint)pos
+{
+       CGPoint ret = CGPointZero;
+       switch( layerOrientation_ ) {
+               case CCTMXOrientationOrtho:
+                       ret = ccp( pos.x * mapTileSize_.width, -pos.y *mapTileSize_.height);
+                       break;
+               case CCTMXOrientationIso:
+                       ret = ccp( (mapTileSize_.width /2) * (pos.x - pos.y),
+                                         (mapTileSize_.height /2 ) * (-pos.x - pos.y) );
+                       break;
+               case CCTMXOrientationHex:
+                       NSAssert(CGPointEqualToPoint(pos, CGPointZero), @"offset for hexagonal map not implemented yet");
+                       break;
+       }
+       return ret;     
+}
+
+-(CGPoint) positionAt:(CGPoint)pos
+{
+       CGPoint ret = CGPointZero;
+       switch( layerOrientation_ ) {
+               case CCTMXOrientationOrtho:
+                       ret = [self positionForOrthoAt:pos];
+                       break;
+               case CCTMXOrientationIso:
+                       ret = [self positionForIsoAt:pos];
+                       break;
+               case CCTMXOrientationHex:
+                       ret = [self positionForHexAt:pos];
+                       break;
+       }
+       return ret;
+}
+
+-(CGPoint) positionForOrthoAt:(CGPoint)pos
+{
+       CGPoint xy = {
+               pos.x * mapTileSize_.width,
+               (layerSize_.height - pos.y - 1) * mapTileSize_.height,
+       };
+       return xy;
+}
+
+-(CGPoint) positionForIsoAt:(CGPoint)pos
+{
+       CGPoint xy = {
+               mapTileSize_.width /2 * ( layerSize_.width + pos.x - pos.y - 1),
+               mapTileSize_.height /2 * (( layerSize_.height * 2 - pos.x - pos.y) - 2),
+       };
+       return xy;
+}
+
+-(CGPoint) positionForHexAt:(CGPoint)pos
+{
+       float diffY = 0;
+       if( (int)pos.x % 2 == 1 )
+               diffY = -mapTileSize_.height/2 ;
+       
+       CGPoint xy = {
+               pos.x * mapTileSize_.width*3/4,
+               (layerSize_.height - pos.y - 1) * mapTileSize_.height + diffY
+       };
+       return xy;
+}
+
+-(NSInteger) vertexZForPos:(CGPoint)pos
+{
+       NSInteger ret = 0;
+       NSUInteger maxVal = 0;
+       if( useAutomaticVertexZ_ ) {
+               switch( layerOrientation_ ) {
+                       case CCTMXOrientationIso:
+                               maxVal = layerSize_.width + layerSize_.height;
+                               ret = -(maxVal - (pos.x + pos.y));
+                               break;
+                       case CCTMXOrientationOrtho:
+                               ret = -(layerSize_.height-pos.y);
+                               break;
+                       case CCTMXOrientationHex:
+                               NSAssert(NO,@"TMX Hexa zOrder not supported");
+                               break;
+                       default:
+                               NSAssert(NO,@"TMX invalid value");
+                               break;
+               }
+       } else
+               ret = vertexZvalue_;
+       
+       return ret;
+}
+
+#pragma mark CCTMXLayer - draw
+
+-(void) draw
+{
+       if( useAutomaticVertexZ_ ) {
+               glEnable(GL_ALPHA_TEST);
+               glAlphaFunc(GL_GREATER, alphaFuncValue_);
+       }
+       
+       [super draw];
+       
+       if( useAutomaticVertexZ_ )
+               glDisable(GL_ALPHA_TEST);
+}
+@end
+
diff --git a/libs/cocos2d/CCTMXObjectGroup.h b/libs/cocos2d/CCTMXObjectGroup.h
new file mode 100644 (file)
index 0000000..02feadf
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2010 Neophit
+ * 
+ * Copyright (c) 2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ *
+ * TMX Tiled Map support:
+ * http://www.mapeditor.org
+ *
+ */
+
+#import "CCNode.h"
+
+
+@class CCTMXObjectGroup;
+
+
+/** CCTMXObjectGroup represents the TMX object group.
+@since v0.99.0
+*/
+@interface CCTMXObjectGroup : NSObject
+{
+       NSString                        *groupName_;
+       CGPoint                         positionOffset_;
+       NSMutableArray          *objects_;
+       NSMutableDictionary     *properties_;
+}
+
+/** name of the group */
+@property (nonatomic,readwrite,retain) NSString *groupName;
+/** offset position of child objects */
+@property (nonatomic,readwrite,assign) CGPoint positionOffset;
+/** array of the objects */
+@property (nonatomic,readwrite,retain) NSMutableArray *objects;
+/** list of properties stored in a dictionary */
+@property (nonatomic,readwrite,retain) NSMutableDictionary *properties;
+
+/** return the value for the specific property name */
+-(id) propertyNamed:(NSString *)propertyName;
+
+/** return the dictionary for the specific object name.
+ It will return the 1st object found on the array for the given name.
+ */
+-(NSMutableDictionary*) objectNamed:(NSString *)objectName;
+
+@end
diff --git a/libs/cocos2d/CCTMXObjectGroup.m b/libs/cocos2d/CCTMXObjectGroup.m
new file mode 100644 (file)
index 0000000..648cda4
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2010 Neophit
+ * 
+ * Copyright (c) 2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ *
+ * TMX Tiled Map support:
+ * http://www.mapeditor.org
+ *
+ */
+
+#import "CCTMXObjectGroup.h"
+#import "CCTMXXMLParser.h"
+#import "ccMacros.h"
+#import "Support/CGPointExtension.h"
+
+
+#pragma mark -
+#pragma mark TMXObjectGroup
+
+@implementation CCTMXObjectGroup
+
+@synthesize groupName = groupName_;
+@synthesize objects = objects_;
+@synthesize positionOffset = positionOffset_;
+@synthesize properties = properties_;
+
+-(id) init
+{
+       if (( self=[super init] )) {
+               self.groupName = nil;
+               self.positionOffset = CGPointZero;
+               self.objects = [NSMutableArray arrayWithCapacity:10];
+               self.properties = [NSMutableDictionary dictionaryWithCapacity:5];
+       }
+       return self;
+}
+
+-(void) dealloc
+{
+       CCLOGINFO( @"cocos2d: deallocing %@", self );
+               
+       [groupName_ release];
+       [objects_ release];
+       [properties_ release];
+       [super dealloc];
+}
+
+-(NSMutableDictionary*) objectNamed:(NSString *)objectName
+{
+       for( id object in objects_ ) {
+               if( [[object valueForKey:@"name"] isEqual:objectName] )
+                       return object;
+               }
+
+       // object not found
+       return nil;
+}
+
+-(id) propertyNamed:(NSString *)propertyName 
+{
+       return [properties_ valueForKey:propertyName];
+}
+
+@end
diff --git a/libs/cocos2d/CCTMXTiledMap.h b/libs/cocos2d/CCTMXTiledMap.h
new file mode 100644 (file)
index 0000000..da2f933
--- /dev/null
@@ -0,0 +1,145 @@
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2009-2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ *
+ * TMX Tiled Map support:
+ * http://www.mapeditor.org
+ *
+ */
+
+#import "CCNode.h"
+
+
+@class CCTMXLayer;
+@class CCTMXObjectGroup;
+
+/** Possible oritentations of the TMX map */
+enum
+{
+       /** Orthogonal orientation */
+       CCTMXOrientationOrtho,
+       
+       /** Hexagonal orientation */
+       CCTMXOrientationHex,
+       
+       /** Isometric orientation */
+       CCTMXOrientationIso,
+};
+
+/** CCTMXTiledMap knows how to parse and render a TMX map.
+ It adds support for the TMX tiled map format used by http://www.mapeditor.org
+ It supports isometric, hexagonal and orthogonal tiles.
+ It also supports object groups, objects, and properties.
+
+ Features:
+ - Each tile will be treated as an CCSprite
+ - The sprites are created on demand. They will be created only when you call "[layer tileAt:]"
+ - Each tile can be rotated / moved / scaled / tinted / "opacitied", since each tile is a CCSprite
+ - Tiles can be added/removed in runtime
+ - The z-order of the tiles can be modified in runtime
+ - Each tile has an anchorPoint of (0,0)
+ - The anchorPoint of the TMXTileMap is (0,0)
+ - The TMX layers will be added as a child
+ - The TMX layers will be aliased by default
+ - The tileset image will be loaded using the CCTextureCache
+ - Each tile will have a unique tag
+ - Each tile will have a unique z value. top-left: z=1, bottom-right: z=max z
+ - Each object group will be treated as an NSMutableArray
+ - Object class which will contain all the properties in a dictionary
+ - Properties can be assigned to the Map, Layer, Object Group, and Object
+ Limitations:
+ - It only supports one tileset per layer.
+ - Embeded images are not supported
+ - It only supports the XML format (the JSON format is not supported)
+ Technical description:
+   Each layer is created using an CCTMXLayer (subclass of CCSpriteSheet). If you have 5 layers, then 5 CCTMXLayer will be created,
+   unless the layer visibility is off. In that case, the layer won't be created at all.
+   You can obtain the layers (CCTMXLayer objects) at runtime by:
+  - [map getChildByTag: tag_number];  // 0=1st layer, 1=2nd layer, 2=3rd layer, etc...
+  - [map layerNamed: name_of_the_layer];
+
+   Each object group is created using a CCTMXObjectGroup which is a subclass of NSMutableArray.
+   You can obtain the object groups at runtime by:
+   - [map objectGroupNamed: name_of_the_object_group];
+  
+   Each object is a CCTMXObject.
+
+   Each property is stored as a key-value pair in an NSMutableDictionary.
+   You can obtain the properties at runtime by:
+               [map propertyNamed: name_of_the_property];
+               [layer propertyNamed: name_of_the_property];
+               [objectGroup propertyNamed: name_of_the_property];
+               [object propertyNamed: name_of_the_property];
+
+ @since v0.8.1
+ */
+@interface CCTMXTiledMap : CCNode
+{
+       CGSize                          mapSize_;
+       CGSize                          tileSize_;
+       int                                     mapOrientation_;
+       NSMutableArray          *objectGroups_;
+       NSMutableDictionary     *properties_;
+       NSMutableDictionary     *tileProperties_;
+}
+
+/** the map's size property measured in tiles */
+@property (nonatomic,readonly) CGSize mapSize;
+/** the tiles's size property measured in pixels */
+@property (nonatomic,readonly) CGSize tileSize;
+/** map orientation */
+@property (nonatomic,readonly) int mapOrientation;
+/** object groups */
+@property (nonatomic,readwrite,retain) NSMutableArray *objectGroups;
+/** properties */
+@property (nonatomic,readwrite,retain) NSMutableDictionary *properties;
+
+/** creates a TMX Tiled Map with a TMX file.*/
++(id) tiledMapWithTMXFile:(NSString*)tmxFile;
+
+/** initializes a TMX Tiled Map with a TMX file */
+-(id) initWithTMXFile:(NSString*)tmxFile;
+
+/** return the TMXLayer for the specific layer */
+-(CCTMXLayer*) layerNamed:(NSString *)layerName;
+
+/** return the TMXObjectGroup for the secific group */
+-(CCTMXObjectGroup*) objectGroupNamed:(NSString *)groupName;
+
+/** return the TMXObjectGroup for the secific group
+ @deprecated Use map#objectGroupNamed instead
+ */
+-(CCTMXObjectGroup*) groupNamed:(NSString *)groupName DEPRECATED_ATTRIBUTE;
+
+/** return the value for the specific property name */
+-(id) propertyNamed:(NSString *)propertyName;
+
+/** return properties dictionary for tile GID */
+-(NSDictionary*)propertiesForGID:(unsigned int)GID;
+@end
+
diff --git a/libs/cocos2d/CCTMXTiledMap.m b/libs/cocos2d/CCTMXTiledMap.m
new file mode 100644 (file)
index 0000000..7f86c6a
--- /dev/null
@@ -0,0 +1,201 @@
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2009-2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ *
+ * TMX Tiled Map support:
+ * http://www.mapeditor.org
+ *
+ */
+
+#import "CCTMXTiledMap.h"
+#import "CCTMXXMLParser.h"
+#import "CCTMXLayer.h"
+#import "CCTMXObjectGroup.h"
+#import "CCSprite.h"
+#import "CCTextureCache.h"
+#import "Support/CGPointExtension.h"
+
+
+#pragma mark -
+#pragma mark CCTMXTiledMap
+
+@interface CCTMXTiledMap (Private)
+-(id) parseLayer:(CCTMXLayerInfo*)layer map:(CCTMXMapInfo*)mapInfo;
+-(CCTMXTilesetInfo*) tilesetForLayer:(CCTMXLayerInfo*)layerInfo map:(CCTMXMapInfo*)mapInfo;
+@end
+
+@implementation CCTMXTiledMap
+@synthesize mapSize = mapSize_;
+@synthesize tileSize = tileSize_;
+@synthesize mapOrientation = mapOrientation_;
+@synthesize objectGroups = objectGroups_;
+@synthesize properties = properties_;
+
++(id) tiledMapWithTMXFile:(NSString*)tmxFile
+{
+       return [[[self alloc] initWithTMXFile:tmxFile] autorelease];
+}
+
+-(id) initWithTMXFile:(NSString*)tmxFile
+{
+       NSAssert(tmxFile != nil, @"TMXTiledMap: tmx file should not bi nil");
+
+       if ((self=[super init])) {
+               
+               [self setContentSize:CGSizeZero];
+
+               CCTMXMapInfo *mapInfo = [CCTMXMapInfo formatWithTMXFile:tmxFile];
+               
+               NSAssert( [mapInfo.tilesets count] != 0, @"TMXTiledMap: Map not found. Please check the filename.");
+               
+               mapSize_ = mapInfo.mapSize;
+               tileSize_ = mapInfo.tileSize;
+               mapOrientation_ = mapInfo.orientation;
+               objectGroups_ = [mapInfo.objectGroups retain];
+               properties_ = [mapInfo.properties retain];
+               tileProperties_ = [mapInfo.tileProperties retain];
+                               
+               int idx=0;
+
+               for( CCTMXLayerInfo *layerInfo in mapInfo.layers ) {
+                       
+                       if( layerInfo.visible ) {
+                               CCNode *child = [self parseLayer:layerInfo map:mapInfo];
+                               [self addChild:child z:idx tag:idx];
+                               
+                               // update content size with the max size
+                               CGSize childSize = [child contentSize];
+                               CGSize currentSize = [self contentSize];
+                               currentSize.width = MAX( currentSize.width, childSize.width );
+                               currentSize.height = MAX( currentSize.height, childSize.height );
+                               [self setContentSize:currentSize];
+       
+                               idx++;
+                       }                       
+               }               
+       }
+
+       return self;
+}
+
+-(void) dealloc
+{
+       [objectGroups_ release];
+       [properties_ release];
+       [tileProperties_ release];
+       [super dealloc];
+}
+
+// private
+-(id) parseLayer:(CCTMXLayerInfo*)layerInfo map:(CCTMXMapInfo*)mapInfo
+{
+       CCTMXTilesetInfo *tileset = [self tilesetForLayer:layerInfo map:mapInfo];
+       CCTMXLayer *layer = [CCTMXLayer layerWithTilesetInfo:tileset layerInfo:layerInfo mapInfo:mapInfo];
+
+       // tell the layerinfo to release the ownership of the tiles map.
+       layerInfo.ownTiles = NO;
+
+       [layer setupTiles];
+       
+       return layer;
+}
+
+-(CCTMXTilesetInfo*) tilesetForLayer:(CCTMXLayerInfo*)layerInfo map:(CCTMXMapInfo*)mapInfo
+{
+       CFByteOrder o = CFByteOrderGetCurrent();
+       
+       CGSize size = layerInfo.layerSize;
+
+       id iter = [mapInfo.tilesets reverseObjectEnumerator];
+       for( CCTMXTilesetInfo* tileset in iter) {
+               for( unsigned int y = 0; y < size.height; y++ ) {
+                       for( unsigned int x = 0; x < size.width; x++ ) {
+                               
+                               unsigned int pos = x + size.width * y;
+                               unsigned int gid = layerInfo.tiles[ pos ];
+                               
+                               // gid are stored in little endian.
+                               // if host is big endian, then swap
+                               if( o == CFByteOrderBigEndian )
+                                       gid = CFSwapInt32( gid );
+                               
+                               // XXX: gid == 0 --> empty tile
+                               if( gid != 0 ) {
+                                       
+                                       // Optimization: quick return
+                                       // if the layer is invalid (more than 1 tileset per layer) an assert will be thrown later
+                                       if( gid >= tileset.firstGid )
+                                               return tileset;
+                               }
+                       }
+               }               
+       }
+       
+       // If all the tiles are 0, return empty tileset
+       CCLOG(@"cocos2d: Warning: TMX Layer '%@' has no tiles", layerInfo.name);
+       return nil;
+}
+
+
+// public
+
+-(CCTMXLayer*) layerNamed:(NSString *)layerName 
+{
+       CCTMXLayer *layer;
+       CCARRAY_FOREACH(children_, layer) {
+               if([layer isKindOfClass:[CCTMXLayer class]])
+                       if([layer.layerName isEqual:layerName])
+                               return layer;
+       }
+       
+       // layer not found
+       return nil;
+}
+
+-(CCTMXObjectGroup*) objectGroupNamed:(NSString *)groupName 
+{
+       for( CCTMXObjectGroup *objectGroup in objectGroups_ ) {
+               if( [objectGroup.groupName isEqual:groupName] )
+                       return objectGroup;
+       }
+       
+       // objectGroup not found
+       return nil;
+}
+
+// XXX deprecated
+-(CCTMXObjectGroup*) groupNamed:(NSString *)groupName 
+{
+       return [self objectGroupNamed:groupName];
+}
+
+-(id) propertyNamed:(NSString *)propertyName 
+{
+       return [properties_ valueForKey:propertyName];
+}
+-(NSDictionary*)propertiesForGID:(unsigned int)GID{
+       return [tileProperties_ objectForKey:[NSNumber numberWithInt:GID]];
+}
+@end
+
diff --git a/libs/cocos2d/CCTMXXMLParser.h b/libs/cocos2d/CCTMXXMLParser.h
new file mode 100644 (file)
index 0000000..18d7a8a
--- /dev/null
@@ -0,0 +1,202 @@
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2009-2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ *
+ * TMX Tiled Map support:
+ * http://www.mapeditor.org
+ *
+ */
+
+/*
+ * Internal TMX parser
+ *
+ * IMPORTANT: These classed should not be documented using doxygen strings
+ * since the user should not use them.
+ *
+ */
+
+#import <Availability.h>
+#import <Foundation/Foundation.h>
+
+enum {
+       TMXLayerAttribNone = 1 << 0,
+       TMXLayerAttribBase64 = 1 << 1,
+       TMXLayerAttribGzip = 1 << 2,
+       TMXLayerAttribZlib = 1 << 3,
+};
+
+enum {
+       TMXPropertyNone,
+       TMXPropertyMap,
+       TMXPropertyLayer,
+       TMXPropertyObjectGroup,
+       TMXPropertyObject,
+       TMXPropertyTile
+};
+
+/* CCTMXLayerInfo contains the information about the layers like:
+ - Layer name
+ - Layer size
+ - Layer opacity at creation time (it can be modified at runtime)
+ - Whether the layer is visible (if it's not visible, then the CocosNode won't be created)
+ This information is obtained from the TMX file.
+ */
+@interface CCTMXLayerInfo : NSObject
+{
+       NSString                        *name_;
+       CGSize                          layerSize_;
+       unsigned int            *tiles_;
+       BOOL                            visible_;
+       unsigned char           opacity_;
+       BOOL                            ownTiles_;
+       unsigned int            minGID_;
+       unsigned int            maxGID_;
+       NSMutableDictionary     *properties_;
+       CGPoint                         offset_;
+}
+
+@property (nonatomic,readwrite,retain) NSString *name;
+@property (nonatomic,readwrite)                        CGSize layerSize;
+@property (nonatomic,readwrite)                        unsigned int *tiles;
+@property (nonatomic,readwrite)                        BOOL visible;
+@property (nonatomic,readwrite)                        unsigned char opacity;
+@property (nonatomic,readwrite)                        BOOL ownTiles;
+@property (nonatomic,readwrite)                        unsigned int minGID;
+@property (nonatomic,readwrite)                        unsigned int maxGID;
+@property (nonatomic,readwrite,retain) NSMutableDictionary *properties;
+@property (nonatomic,readwrite)                        CGPoint offset;
+@end
+
+/* CCTMXTilesetInfo contains the information about the tilesets like:
+ - Tileset name
+ - Tilset spacing
+ - Tileset margin
+ - size of the tiles
+ - Image used for the tiles
+ - Image size
+ This information is obtained from the TMX file. 
+ */
+@interface CCTMXTilesetInfo : NSObject
+{
+       NSString                *name_;
+       unsigned int    firstGid_;
+       CGSize                  tileSize_;
+       unsigned int    spacing_;
+       unsigned int    margin_;
+       
+       // filename containing the tiles (should be spritesheet / texture atlas)
+       NSString        *sourceImage_;
+       
+       // size in pixels of the image
+       CGSize          imageSize_;
+}
+@property (nonatomic,readwrite,retain) NSString *name;
+@property (nonatomic,readwrite,assign) unsigned int firstGid;
+@property (nonatomic,readwrite,assign) CGSize tileSize;
+@property (nonatomic,readwrite,assign) unsigned int spacing;
+@property (nonatomic,readwrite,assign) unsigned int margin;
+@property (nonatomic,readwrite,retain) NSString *sourceImage;
+@property (nonatomic,readwrite,assign) CGSize imageSize;
+
+-(CGRect) rectForGID:(unsigned int)gid;
+@end
+
+/* CCTMXMapInfo contains the information about the map like:
+ - Map orientation (hexagonal, isometric or orthogonal)
+ - Tile size
+ - Map size
+ And it also contains:
+ - Layers (an array of TMXLayerInfo objects)
+ - Tilesets (an array of TMXTilesetInfo objects)
+ - ObjectGroups (an array of TMXObjectGroupInfo objects)
+ This information is obtained from the TMX file.
+ */
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+#if defined(__IPHONE_4_0)
+@interface CCTMXMapInfo : NSObject <NSXMLParserDelegate>
+#else
+@interface CCTMXMapInfo : NSObject
+#endif
+
+#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED)
+@interface CCTMXMapInfo : NSObject <NSXMLParserDelegate>
+#endif
+{      
+       NSMutableString *currentString;
+    BOOL                               storingCharacters;      
+       int                                     layerAttribs;
+       int                                     parentElement;
+       unsigned int            parentGID_;
+
+       
+       // tmx filename
+       NSString *filename_;
+
+       // map orientation
+       int     orientation_;   
+       
+       // map width & height
+       CGSize  mapSize_;
+       
+       // tiles width & height
+       CGSize  tileSize_;
+       
+       // Layers
+       NSMutableArray *layers_;
+       
+       // tilesets
+       NSMutableArray *tilesets_;
+               
+       // ObjectGroups
+       NSMutableArray *objectGroups_;
+       
+       // properties
+       NSMutableDictionary *properties_;
+       
+       // tile properties
+       NSMutableDictionary *tileProperties_;
+}
+
+@property (nonatomic,readwrite,assign) int orientation;
+@property (nonatomic,readwrite,assign) CGSize mapSize;
+@property (nonatomic,readwrite,assign) CGSize tileSize;
+@property (nonatomic,readwrite,retain) NSMutableArray *layers;
+@property (nonatomic,readwrite,retain) NSMutableArray *tilesets;
+@property (nonatomic,readwrite,retain) NSString *filename;
+@property (nonatomic,readwrite,retain) NSMutableArray *objectGroups;
+@property (nonatomic,readwrite,retain) NSMutableDictionary *properties;
+@property (nonatomic,readwrite,retain) NSMutableDictionary *tileProperties;
+
+/** creates a TMX Format with a tmx file */
++(id) formatWithTMXFile:(NSString*)tmxFile;
+/** initializes a TMX format witha  tmx file */
+-(id) initWithTMXFile:(NSString*)tmxFile;
+@end
+
diff --git a/libs/cocos2d/CCTMXXMLParser.m b/libs/cocos2d/CCTMXXMLParser.m
new file mode 100644 (file)
index 0000000..8e6b080
--- /dev/null
@@ -0,0 +1,455 @@
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2009-2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ *
+ * TMX Tiled Map support:
+ * http://www.mapeditor.org
+ *
+ */
+
+
+#import <Foundation/Foundation.h>
+#include <zlib.h>
+
+#import "ccMacros.h"
+#import "Support/CGPointExtension.h"
+#import "CCTMXXMLParser.h"
+#import "CCTMXTiledMap.h"
+#import "CCTMXObjectGroup.h"
+#import "Support/base64.h"
+#import "Support/ZipUtils.h"
+#import "Support/CCFileUtils.h"
+
+#pragma mark -
+#pragma mark TMXLayerInfo
+
+
+@implementation CCTMXLayerInfo
+
+@synthesize name = name_, layerSize = layerSize_, tiles = tiles_, visible = visible_, opacity = opacity_, ownTiles = ownTiles_, minGID = minGID_, maxGID = maxGID_, properties = properties_;
+@synthesize offset = offset_;
+-(id) init
+{
+       if( (self=[super init])) {
+               ownTiles_ = YES;
+               minGID_ = 100000;
+               maxGID_ = 0;
+               self.name = nil;
+               tiles_ = NULL;
+               offset_ = CGPointZero;
+               self.properties = [NSMutableDictionary dictionaryWithCapacity:5];
+       }
+       return self;
+}
+- (void) dealloc
+{
+       CCLOGINFO(@"cocos2d: deallocing %@",self);
+       
+       [name_ release];
+       [properties_ release];
+
+       if( ownTiles_ && tiles_ ) {
+               free( tiles_ );
+               tiles_ = NULL;
+       }
+       [super dealloc];
+}
+
+@end
+
+#pragma mark -
+#pragma mark TMXTilesetInfo
+@implementation CCTMXTilesetInfo
+
+@synthesize name = name_, firstGid = firstGid_, tileSize = tileSize_, spacing = spacing_, margin = margin_, sourceImage = sourceImage_, imageSize = imageSize_;
+
+- (void) dealloc
+{
+       CCLOGINFO(@"cocos2d: deallocing %@", self);
+       [sourceImage_ release];
+       [name_ release];
+       [super dealloc];
+}
+
+-(CGRect) rectForGID:(unsigned int)gid
+{
+       CGRect rect;
+       rect.size = tileSize_;
+       
+       gid = gid - firstGid_;
+       
+       int max_x = (imageSize_.width - margin_*2 + spacing_) / (tileSize_.width + spacing_);
+       //      int max_y = (imageSize.height - margin*2 + spacing) / (tileSize.height + spacing);
+       
+       rect.origin.x = (gid % max_x) * (tileSize_.width + spacing_) + margin_;
+       rect.origin.y = (gid / max_x) * (tileSize_.height + spacing_) + margin_;
+       
+       return rect;
+}
+@end
+
+#pragma mark -
+#pragma mark CCTMXMapInfo
+
+@interface CCTMXMapInfo (Private)
+/* initalises parsing of an XML file, either a tmx (Map) file or tsx (Tileset) file */
+-(void) parseXMLFile:(NSString *)xmlFilename;
+@end
+
+
+@implementation CCTMXMapInfo
+
+@synthesize orientation = orientation_, mapSize = mapSize_, layers = layers_, tilesets = tilesets_, tileSize = tileSize_, filename = filename_, objectGroups = objectGroups_, properties = properties_;
+@synthesize tileProperties = tileProperties_;
+
++(id) formatWithTMXFile:(NSString*)tmxFile
+{
+       return [[[self alloc] initWithTMXFile:tmxFile] autorelease];
+}
+
+-(id) initWithTMXFile:(NSString*)tmxFile
+{
+       if( (self=[super init])) {
+               
+               self.tilesets = [NSMutableArray arrayWithCapacity:4];
+               self.layers = [NSMutableArray arrayWithCapacity:4];
+               self.filename = tmxFile;
+               self.objectGroups = [NSMutableArray arrayWithCapacity:4];
+               self.properties = [NSMutableDictionary dictionaryWithCapacity:5];
+               self.tileProperties = [NSMutableDictionary dictionaryWithCapacity:5];
+       
+               // tmp vars
+               currentString = [[NSMutableString alloc] initWithCapacity:1024];
+               storingCharacters = NO;
+               layerAttribs = TMXLayerAttribNone;
+               parentElement = TMXPropertyNone;
+               
+               [self parseXMLFile:filename_];          
+       }
+       return self;
+}
+- (void) dealloc
+{
+       CCLOGINFO(@"cocos2d: deallocing %@", self);
+       [tilesets_ release];
+       [layers_ release];
+       [filename_ release];
+       [currentString release];
+       [objectGroups_ release];
+       [properties_ release];
+       [tileProperties_ release];
+       [super dealloc];
+}
+
+- (void) parseXMLFile:(NSString *)xmlFilename
+{
+       NSURL *url = [NSURL fileURLWithPath:[CCFileUtils fullPathFromRelativePath:xmlFilename] ];
+       NSXMLParser *parser = [[NSXMLParser alloc] initWithContentsOfURL:url];
+
+       // we'll do the parsing
+       [parser setDelegate:self];
+       [parser setShouldProcessNamespaces:NO];
+       [parser setShouldReportNamespacePrefixes:NO];
+       [parser setShouldResolveExternalEntities:NO];
+       [parser parse];
+
+       NSAssert1( ! [parser parserError], @"Error parsing file: %@.", xmlFilename );
+
+       [parser release];
+}
+
+// the XML parser calls here with all the elements
+-(void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict
+{      
+       if([elementName isEqualToString:@"map"]) {
+               NSString *version = [attributeDict valueForKey:@"version"];
+               if( ! [version isEqualToString:@"1.0"] )
+                       CCLOG(@"cocos2d: TMXFormat: Unsupported TMX version: %@", version);
+               NSString *orientationStr = [attributeDict valueForKey:@"orientation"];
+               if( [orientationStr isEqualToString:@"orthogonal"])
+                       orientation_ = CCTMXOrientationOrtho;
+               else if ( [orientationStr isEqualToString:@"isometric"])
+                       orientation_ = CCTMXOrientationIso;
+               else if( [orientationStr isEqualToString:@"hexagonal"])
+                       orientation_ = CCTMXOrientationHex;
+               else
+                       CCLOG(@"cocos2d: TMXFomat: Unsupported orientation: %@", orientation_);
+
+               mapSize_.width = [[attributeDict valueForKey:@"width"] intValue];
+               mapSize_.height = [[attributeDict valueForKey:@"height"] intValue];
+               tileSize_.width = [[attributeDict valueForKey:@"tilewidth"] intValue];
+               tileSize_.height = [[attributeDict valueForKey:@"tileheight"] intValue];
+
+               // The parent element is now "map"
+               parentElement = TMXPropertyMap;
+       } else if([elementName isEqualToString:@"tileset"]) {
+               
+               // If this is an external tileset then start parsing that
+               NSString *externalTilesetFilename = [attributeDict valueForKey:@"source"];
+               if (externalTilesetFilename) {
+                               // Tileset file will be relative to the map file. So we need to convert it to an absolute path
+                               NSString *dir = [filename_ stringByDeletingLastPathComponent];  // Directory of map file
+                               externalTilesetFilename = [dir stringByAppendingPathComponent:externalTilesetFilename]; // Append path to tileset file
+                               
+                               [self parseXMLFile:externalTilesetFilename];
+               } else {
+                               
+                       CCTMXTilesetInfo *tileset = [CCTMXTilesetInfo new];
+                       tileset.name = [attributeDict valueForKey:@"name"];
+                       tileset.firstGid = [[attributeDict valueForKey:@"firstgid"] intValue];
+                       tileset.spacing = [[attributeDict valueForKey:@"spacing"] intValue];
+                       tileset.margin = [[attributeDict valueForKey:@"margin"] intValue];
+                       CGSize s;
+                       s.width = [[attributeDict valueForKey:@"tilewidth"] intValue];
+                       s.height = [[attributeDict valueForKey:@"tileheight"] intValue];
+                       tileset.tileSize = s;
+                       
+                       [tilesets_ addObject:tileset];
+                       [tileset release];
+               }
+
+       }else if([elementName isEqualToString:@"tile"]){
+               CCTMXTilesetInfo* info = [tilesets_ lastObject];
+               NSMutableDictionary* dict = [NSMutableDictionary dictionaryWithCapacity:3];
+               parentGID_ =  [info firstGid] + [[attributeDict valueForKey:@"id"] intValue];
+               [tileProperties_ setObject:dict forKey:[NSNumber numberWithInt:parentGID_]];
+               
+               parentElement = TMXPropertyTile;
+               
+       }else if([elementName isEqualToString:@"layer"]) {
+               CCTMXLayerInfo *layer = [CCTMXLayerInfo new];
+               layer.name = [attributeDict valueForKey:@"name"];
+               
+               CGSize s;
+               s.width = [[attributeDict valueForKey:@"width"] intValue];
+               s.height = [[attributeDict valueForKey:@"height"] intValue];
+               layer.layerSize = s;
+               
+               layer.visible = ![[attributeDict valueForKey:@"visible"] isEqualToString:@"0"];
+               
+               if( [attributeDict valueForKey:@"opacity"] )
+                       layer.opacity = 255 * [[attributeDict valueForKey:@"opacity"] floatValue];
+               else
+                       layer.opacity = 255;
+               
+               int x = [[attributeDict valueForKey:@"x"] intValue];
+               int y = [[attributeDict valueForKey:@"y"] intValue];
+               layer.offset = ccp(x,y);
+               
+               [layers_ addObject:layer];
+               [layer release];
+               
+               // The parent element is now "layer"
+               parentElement = TMXPropertyLayer;
+       
+       } else if([elementName isEqualToString:@"objectgroup"]) {
+               
+               CCTMXObjectGroup *objectGroup = [[CCTMXObjectGroup alloc] init];
+               objectGroup.groupName = [attributeDict valueForKey:@"name"];
+               CGPoint positionOffset;
+               positionOffset.x = [[attributeDict valueForKey:@"x"] intValue] * tileSize_.width;
+               positionOffset.y = [[attributeDict valueForKey:@"y"] intValue] * tileSize_.height;
+               objectGroup.positionOffset = positionOffset;
+               
+               [objectGroups_ addObject:objectGroup];
+               [objectGroup release];
+               
+               // The parent element is now "objectgroup"
+               parentElement = TMXPropertyObjectGroup;
+                       
+       } else if([elementName isEqualToString:@"image"]) {
+
+               CCTMXTilesetInfo *tileset = [tilesets_ lastObject];
+               
+               // build full path
+               NSString *imagename = [attributeDict valueForKey:@"source"];            
+               NSString *path = [filename_ stringByDeletingLastPathComponent];         
+               tileset.sourceImage = [path stringByAppendingPathComponent:imagename];
+
+       } else if([elementName isEqualToString:@"data"]) {
+               NSString *encoding = [attributeDict valueForKey:@"encoding"];
+               NSString *compression = [attributeDict valueForKey:@"compression"];
+               
+               if( [encoding isEqualToString:@"base64"] ) {
+                       layerAttribs |= TMXLayerAttribBase64;
+                       storingCharacters = YES;
+                       
+                       if( [compression isEqualToString:@"gzip"] )
+                               layerAttribs |= TMXLayerAttribGzip;
+
+                       else if( [compression isEqualToString:@"zlib"] )
+                               layerAttribs |= TMXLayerAttribZlib;
+
+                       NSAssert( !compression || [compression isEqualToString:@"gzip"] || [compression isEqualToString:@"zlib"], @"TMX: unsupported compression method" );
+               }
+               
+               NSAssert( layerAttribs != TMXLayerAttribNone, @"TMX tile map: Only base64 and/or gzip/zlib maps are supported" );
+               
+       } else if([elementName isEqualToString:@"object"]) {
+       
+               CCTMXObjectGroup *objectGroup = [objectGroups_ lastObject];
+               
+               // The value for "type" was blank or not a valid class name
+               // Create an instance of TMXObjectInfo to store the object and its properties
+               NSMutableDictionary *dict = [[NSMutableDictionary alloc] initWithCapacity:5];
+               
+               // Set the name of the object to the value for "name"
+               [dict setValue:[attributeDict valueForKey:@"name"] forKey:@"name"];
+               
+               // Assign all the attributes as key/name pairs in the properties dictionary
+               [dict setValue:[attributeDict valueForKey:@"type"] forKey:@"type"];
+               int x = [[attributeDict valueForKey:@"x"] intValue] + objectGroup.positionOffset.x;
+               [dict setValue:[NSNumber numberWithInt:x] forKey:@"x"];
+               int y = [[attributeDict valueForKey:@"y"] intValue] + objectGroup.positionOffset.y;
+               // Correct y position. (Tiled uses Flipped, cocos2d uses Standard)
+               y = (mapSize_.height * tileSize_.height) - y - [[attributeDict valueForKey:@"height"] intValue];
+               [dict setValue:[NSNumber numberWithInt:y] forKey:@"y"];
+               [dict setValue:[attributeDict valueForKey:@"width"] forKey:@"width"];
+               [dict setValue:[attributeDict valueForKey:@"height"] forKey:@"height"];
+               
+               // Add the object to the objectGroup
+               [[objectGroup objects] addObject:dict];
+               [dict release];
+               
+               // The parent element is now "object"
+               parentElement = TMXPropertyObject;
+               
+       } else if([elementName isEqualToString:@"property"]) {
+       
+               if ( parentElement == TMXPropertyNone ) {
+               
+                       CCLOG( @"TMX tile map: Parent element is unsupported. Cannot add property named '%@' with value '%@'",
+                       [attributeDict valueForKey:@"name"], [attributeDict valueForKey:@"value"] );
+                       
+               } else if ( parentElement == TMXPropertyMap ) {
+               
+                       // The parent element is the map
+                       [properties_ setValue:[attributeDict valueForKey:@"value"] forKey:[attributeDict valueForKey:@"name"]];
+                       
+               } else if ( parentElement == TMXPropertyLayer ) {
+               
+                       // The parent element is the last layer
+                       CCTMXLayerInfo *layer = [layers_ lastObject];
+                       // Add the property to the layer
+                       [[layer properties] setValue:[attributeDict valueForKey:@"value"] forKey:[attributeDict valueForKey:@"name"]];
+                       
+               } else if ( parentElement == TMXPropertyObjectGroup ) {
+                       
+                       // The parent element is the last object group
+                       CCTMXObjectGroup *objectGroup = [objectGroups_ lastObject];
+                       [[objectGroup properties] setValue:[attributeDict valueForKey:@"value"] forKey:[attributeDict valueForKey:@"name"]];
+                       
+               } else if ( parentElement == TMXPropertyObject ) {
+                       
+                       // The parent element is the last object
+                       CCTMXObjectGroup *objectGroup = [objectGroups_ lastObject];
+                       NSMutableDictionary *dict = [[objectGroup objects] lastObject];
+                       
+                       NSString *propertyName = [attributeDict valueForKey:@"name"];
+                       NSString *propertyValue = [attributeDict valueForKey:@"value"];
+
+                       [dict setValue:propertyValue forKey:propertyName];
+               } else if ( parentElement == TMXPropertyTile ) {
+                       
+                       NSMutableDictionary* dict = [tileProperties_ objectForKey:[NSNumber numberWithInt:parentGID_]];
+                       NSString *propertyName = [attributeDict valueForKey:@"name"];
+                       NSString *propertyValue = [attributeDict valueForKey:@"value"];
+                       [dict setObject:propertyValue forKey:propertyName];
+                       
+               }
+       }
+}
+
+- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
+{
+       int len = 0;
+
+       if([elementName isEqualToString:@"data"] && layerAttribs&TMXLayerAttribBase64) {
+               storingCharacters = NO;
+               
+               CCTMXLayerInfo *layer = [layers_ lastObject];
+               
+               unsigned char *buffer;
+               len = base64Decode((unsigned char*)[currentString UTF8String], (unsigned int) [currentString length], &buffer);
+               if( ! buffer ) {
+                       CCLOG(@"cocos2d: TiledMap: decode data error");
+                       return;
+               }
+               
+               if( layerAttribs & (TMXLayerAttribGzip | TMXLayerAttribZlib) ) {
+                       unsigned char *deflated;
+                       CGSize s = [layer layerSize];
+                       int sizeHint = s.width * s.height * sizeof(uint32_t);
+
+                       int inflatedLen = ccInflateMemoryWithHint(buffer, len, &deflated, sizeHint);
+                       NSAssert( inflatedLen == sizeHint, @"CCTMXXMLParser: Hint failed!");
+                       
+                       inflatedLen = (int)&inflatedLen; // XXX: to avoid warings in compiler
+
+                       free( buffer );
+                       
+                       if( ! deflated ) {
+                               CCLOG(@"cocos2d: TiledMap: inflate data error");
+                               return;
+                       }
+                       
+                       layer.tiles = (unsigned int*) deflated;
+               } else
+                       layer.tiles = (unsigned int*) buffer;
+               
+               [currentString setString:@""];
+                       
+       } else if ([elementName isEqualToString:@"map"]) {
+               // The map element has ended
+               parentElement = TMXPropertyNone;
+               
+       }       else if ([elementName isEqualToString:@"layer"]) {
+               // The layer element has ended
+               parentElement = TMXPropertyNone;
+               
+       } else if ([elementName isEqualToString:@"objectgroup"]) {
+               // The objectgroup element has ended
+               parentElement = TMXPropertyNone;
+               
+       } else if ([elementName isEqualToString:@"object"]) {
+               // The object element has ended
+               parentElement = TMXPropertyNone;
+       }
+}
+
+- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
+{
+    if (storingCharacters)
+               [currentString appendString:string];
+}
+
+
+//
+// the level did not load, file not found, etc.
+//
+-(void)parser:(NSXMLParser *)parser parseErrorOccurred:(NSError *)parseError{
+       CCLOG(@"cocos2d: Error on XML Parse: %@", [parseError localizedDescription] );
+}
+
+@end
diff --git a/libs/cocos2d/CCTexture2D.h b/libs/cocos2d/CCTexture2D.h
new file mode 100644 (file)
index 0000000..ddb671c
--- /dev/null
@@ -0,0 +1,328 @@
+/*
+
+===== IMPORTANT =====
+
+This is sample code demonstrating API, technology or techniques in development.
+Although this sample code has been reviewed for technical accuracy, it is not
+final. Apple is supplying this information to help you plan for the adoption of
+the technologies and programming interfaces described herein. This information
+is subject to change, and software implemented based on this sample code should
+be tested with final operating system software and final documentation. Newer
+versions of this sample code may be provided with future seeds of the API or
+technology. For information about updates to this and other developer
+documentation, view the New & Updated sidebars in subsequent documentation
+seeds.
+
+=====================
+
+File: Texture2D.h
+Abstract: Creates OpenGL 2D textures from images or text.
+
+Version: 1.6
+
+Disclaimer: IMPORTANT:  This Apple software is supplied to you by Apple Inc.
+("Apple") in consideration of your agreement to the following terms, and your
+use, installation, modification or redistribution of this Apple software
+constitutes acceptance of these terms.  If you do not agree with these terms,
+please do not use, install, modify or redistribute this Apple software.
+
+In consideration of your agreement to abide by the following terms, and subject
+to these terms, Apple grants you a personal, non-exclusive license, under
+Apple's copyrights in this original Apple software (the "Apple Software"), to
+use, reproduce, modify and redistribute the Apple Software, with or without
+modifications, in source and/or binary forms; provided that if you redistribute
+the Apple Software in its entirety and without modifications, you must retain
+this notice and the following text and disclaimers in all such redistributions
+of the Apple Software.
+Neither the name, trademarks, service marks or logos of Apple Inc. may be used
+to endorse or promote products derived from the Apple Software without specific
+prior written permission from Apple.  Except as expressly stated in this notice,
+no other rights or licenses, express or implied, are granted by Apple herein,
+including but not limited to any patent rights that may be infringed by your
+derivative works or by other works in which the Apple Software may be
+incorporated.
+
+The Apple Software is provided by Apple on an "AS IS" basis.  APPLE MAKES NO
+WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED
+WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN
+COMBINATION WITH YOUR PRODUCTS.
+
+IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR
+DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF
+CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF
+APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+Copyright (C) 2008 Apple Inc. All Rights Reserved.
+
+*/
+
+#import <Availability.h>
+
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+#import <UIKit/UIKit.h>                        // for UIImage
+#endif
+
+#import <Foundation/Foundation.h> //   for NSObject
+
+#import "Platforms/CCGL.h" // OpenGL stuff
+#import "Platforms/CCNS.h" // Next-Step stuff
+
+//CONSTANTS:
+
+/** @typedef CCTexture2DPixelFormat
+ Possible texture pixel formats
+ */
+typedef enum {
+       kCCTexture2DPixelFormat_Automatic = 0,
+       //! 32-bit texture: RGBA8888
+       kCCTexture2DPixelFormat_RGBA8888,
+       //! 16-bit texture without Alpha channel
+       kCCTexture2DPixelFormat_RGB565,
+       //! 8-bit textures used as masks
+       kCCTexture2DPixelFormat_A8,
+       //! 8-bit intensity texture
+       kCCTexture2DPixelFormat_I8,
+       //! 16-bit textures used as masks
+       kCCTexture2DPixelFormat_AI88,
+       //! 16-bit textures: RGBA4444
+       kCCTexture2DPixelFormat_RGBA4444,
+       //! 16-bit textures: RGB5A1
+       kCCTexture2DPixelFormat_RGB5A1, 
+       //! 4-bit PVRTC-compressed texture: PVRTC4
+       kCCTexture2DPixelFormat_PVRTC4,
+       //! 2-bit PVRTC-compressed texture: PVRTC2
+       kCCTexture2DPixelFormat_PVRTC2,
+
+       //! Default texture format: RGBA8888
+       kCCTexture2DPixelFormat_Default = kCCTexture2DPixelFormat_RGBA8888,
+
+       // backward compatibility stuff
+       kTexture2DPixelFormat_Automatic = kCCTexture2DPixelFormat_Automatic,
+       kTexture2DPixelFormat_RGBA8888 = kCCTexture2DPixelFormat_RGBA8888,
+       kTexture2DPixelFormat_RGB565 = kCCTexture2DPixelFormat_RGB565,
+       kTexture2DPixelFormat_A8 = kCCTexture2DPixelFormat_A8,
+       kTexture2DPixelFormat_RGBA4444 = kCCTexture2DPixelFormat_RGBA4444,
+       kTexture2DPixelFormat_RGB5A1 = kCCTexture2DPixelFormat_RGB5A1,
+       kTexture2DPixelFormat_Default = kCCTexture2DPixelFormat_Default
+       
+} CCTexture2DPixelFormat;
+
+//CLASS INTERFACES:
+
+/** CCTexture2D class.
+ * This class allows to easily create OpenGL 2D textures from images, text or raw data.
+ * The created CCTexture2D object will always have power-of-two dimensions. 
+ * Depending on how you create the CCTexture2D object, the actual image area of the texture might be smaller than the texture dimensions i.e. "contentSize" != (pixelsWide, pixelsHigh) and (maxS, maxT) != (1.0, 1.0).
+ * Be aware that the content of the generated textures will be upside-down!
+ */
+@interface CCTexture2D : NSObject
+{
+       GLuint                                          name_;
+       CGSize                                          size_;
+       NSUInteger                                      width_,
+                                                               height_;
+       CCTexture2DPixelFormat          format_;
+       GLfloat                                         maxS_,
+                                                               maxT_;
+       BOOL                                            hasPremultipliedAlpha_;
+}
+/** Intializes with a texture2d with data */
+- (id) initWithData:(const void*)data pixelFormat:(CCTexture2DPixelFormat)pixelFormat pixelsWide:(NSUInteger)width pixelsHigh:(NSUInteger)height contentSize:(CGSize)size;
+
+/** These functions are needed to create mutable textures */
+- (void) releaseData:(void*)data;
+- (void*) keepData:(void*)data length:(NSUInteger)length;
+
+/** pixel format of the texture */
+@property(nonatomic,readonly) CCTexture2DPixelFormat pixelFormat;
+/** width in pixels */
+@property(nonatomic,readonly) NSUInteger pixelsWide;
+/** hight in pixels */
+@property(nonatomic,readonly) NSUInteger pixelsHigh;
+
+/** texture name */
+@property(nonatomic,readonly) GLuint name;
+
+/** returns content size of the texture in pixels */
+@property(nonatomic,readonly, nonatomic) CGSize contentSizeInPixels;
+
+/** texture max S */
+@property(nonatomic,readwrite) GLfloat maxS;
+/** texture max T */
+@property(nonatomic,readwrite) GLfloat maxT;
+/** whether or not the texture has their Alpha premultiplied */
+@property(nonatomic,readonly) BOOL hasPremultipliedAlpha;
+
+/** returns the content size of the texture in points */
+-(CGSize) contentSize;
+@end
+
+/**
+Drawing extensions to make it easy to draw basic quads using a CCTexture2D object.
+These functions require GL_TEXTURE_2D and both GL_VERTEX_ARRAY and GL_TEXTURE_COORD_ARRAY client states to be enabled.
+*/
+@interface CCTexture2D (Drawing)
+/** draws a texture at a given point */
+- (void) drawAtPoint:(CGPoint)point;
+/** draws a texture inside a rect */
+- (void) drawInRect:(CGRect)rect;
+@end
+
+/**
+Extensions to make it easy to create a CCTexture2D object from an image file.
+Note that RGBA type textures will have their alpha premultiplied - use the blending mode (GL_ONE, GL_ONE_MINUS_SRC_ALPHA).
+*/
+@interface CCTexture2D (Image)
+/** Initializes a texture from a UIImage object */
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+- (id) initWithImage:(UIImage *)uiImage;
+#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED)
+- (id) initWithImage:(CGImageRef)cgImage;
+#endif
+@end
+
+/**
+Extensions to make it easy to create a CCTexture2D object from a string of text.
+Note that the generated textures are of type A8 - use the blending mode (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA).
+*/
+@interface CCTexture2D (Text)
+/** Initializes a texture from a string with dimensions, alignment, line break mode, font name and font size
+ Supported lineBreakModes:
+       - iOS: all UILineBreakMode supported modes
+       - Mac: Only NSLineBreakByWordWrapping is supported.
+ @since v1.0
+ */
+- (id) initWithString:(NSString*)string dimensions:(CGSize)dimensions alignment:(CCTextAlignment)alignment lineBreakMode:(CCLineBreakMode)lineBreakMode fontName:(NSString*)name fontSize:(CGFloat)size;
+/** Initializes a texture from a string with dimensions, alignment, font name and font size */
+- (id) initWithString:(NSString*)string dimensions:(CGSize)dimensions alignment:(CCTextAlignment)alignment fontName:(NSString*)name fontSize:(CGFloat)size;
+/** Initializes a texture from a string with font name and font size */
+- (id) initWithString:(NSString*)string fontName:(NSString*)name fontSize:(CGFloat)size;
+@end
+
+
+/**
+ Extensions to make it easy to create a CCTexture2D object from a PVRTC file
+ Note that the generated textures don't have their alpha premultiplied - use the blending mode (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA).
+ */
+@interface CCTexture2D (PVRSupport)
+/** Initializes a texture from a PVR Texture Compressed (PVRTC) buffer
+ *
+ * IMPORTANT: This method is only defined on iOS. It is not supported on the Mac version.
+ */
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+-(id) initWithPVRTCData: (const void*)data level:(int)level bpp:(int)bpp hasAlpha:(BOOL)hasAlpha length:(int)length pixelFormat:(CCTexture2DPixelFormat)pixelFormat;
+#endif // __IPHONE_OS_VERSION_MAX_ALLOWED
+/** Initializes a texture from a PVR file.
+ Supported PVR formats:
+ - BGRA 8888
+ - RGBA 8888
+ - RGBA 4444
+ - RGBA 5551
+ - RBG 565
+ - A 8
+ - I 8
+ - AI 8
+ - PVRTC 2BPP
+ - PVRTC 4BPP
+ By default PVR images are treated as if they alpha channel is NOT premultiplied. You can override this behavior with this class method:
+ - PVRImagesHavePremultipliedAlpha:(BOOL)haveAlphaPremultiplied;
+ IMPORTANT: This method is only defined on iOS. It is not supported on the Mac version.
+ */
+-(id) initWithPVRFile: (NSString*) file;
+
+/** treats (or not) PVR files as if they have alpha premultiplied.
+ Since it is impossible to know at runtime if the PVR images have the alpha channel premultiplied, it is
+ possible load them as if they have (or not) the alpha channel premultiplied.
+ By default it is disabled.
+ @since v0.99.5
+ */
++(void) PVRImagesHavePremultipliedAlpha:(BOOL)haveAlphaPremultiplied;
+@end
+
+/**
+ Extension to set the Min / Mag filter
+ */
+typedef struct _ccTexParams {
+       GLuint  minFilter;
+       GLuint  magFilter;
+       GLuint  wrapS;
+       GLuint  wrapT;
+} ccTexParams;
+
+@interface CCTexture2D (GLFilter)
+/** sets the min filter, mag filter, wrap s and wrap t texture parameters.
+ If the texture size is NPOT (non power of 2), then in can only use GL_CLAMP_TO_EDGE in GL_TEXTURE_WRAP_{S,T}.
+ @since v0.8
+ */
+-(void) setTexParameters: (ccTexParams*) texParams;
+
+/** sets antialias texture parameters:
+  - GL_TEXTURE_MIN_FILTER = GL_LINEAR
+  - GL_TEXTURE_MAG_FILTER = GL_LINEAR
+
+ @since v0.8
+ */
+- (void) setAntiAliasTexParameters;
+
+/** sets alias texture parameters:
+  - GL_TEXTURE_MIN_FILTER = GL_NEAREST
+  - GL_TEXTURE_MAG_FILTER = GL_NEAREST
+ @since v0.8
+ */
+- (void) setAliasTexParameters;
+
+
+/** Generates mipmap images for the texture.
+ It only works if the texture size is POT (power of 2).
+ @since v0.99.0
+ */
+-(void) generateMipmap;
+
+
+@end
+
+@interface CCTexture2D (PixelFormat)
+/** sets the default pixel format for UIImages that contains alpha channel.
+ If the UIImage contains alpha channel, then the options are:
+       - generate 32-bit textures: kCCTexture2DPixelFormat_RGBA8888 (default one)
+       - generate 16-bit textures: kCCTexture2DPixelFormat_RGBA4444
+       - generate 16-bit textures: kCCTexture2DPixelFormat_RGB5A1
+       - generate 16-bit textures: kCCTexture2DPixelFormat_RGB565
+       - generate 8-bit textures: kCCTexture2DPixelFormat_A8 (only use it if you use just 1 color)
+
+ How does it work ?
+   - If the image is an RGBA (with Alpha) then the default pixel format will be used (it can be a 8-bit, 16-bit or 32-bit texture)
+   - If the image is an RGB (without Alpha) then an RGB565 texture will be used (16-bit texture)
+ This parameter is not valid for PVR images.
+ @since v0.8
+ */
++(void) setDefaultAlphaPixelFormat:(CCTexture2DPixelFormat)format;
+
+/** returns the alpha pixel format
+ @since v0.8
+ */
++(CCTexture2DPixelFormat) defaultAlphaPixelFormat;
+
+/** returns the bits-per-pixel of the in-memory OpenGL texture
+ @since v1.0
+ */
+-(NSInteger) bitsPerPixelForFormat;
+@end
+
+
+
+
+
diff --git a/libs/cocos2d/CCTexture2D.m b/libs/cocos2d/CCTexture2D.m
new file mode 100644 (file)
index 0000000..6d5546a
--- /dev/null
@@ -0,0 +1,814 @@
+/*
+
+===== IMPORTANT =====
+
+This is sample code demonstrating API, technology or techniques in development.
+Although this sample code has been reviewed for technical accuracy, it is not
+final. Apple is supplying this information to help you plan for the adoption of
+the technologies and programming interfaces described herein. This information
+is subject to change, and software implemented based on this sample code should
+be tested with final operating system software and final documentation. Newer
+versions of this sample code may be provided with future seeds of the API or
+technology. For information about updates to this and other developer
+documentation, view the New & Updated sidebars in subsequent documentationd
+seeds.
+
+=====================
+
+File: Texture2D.m
+Abstract: Creates OpenGL 2D textures from images or text.
+
+Version: 1.6
+
+Disclaimer: IMPORTANT:  This Apple software is supplied to you by Apple Inc.
+("Apple") in consideration of your agreement to the following terms, and your
+use, installation, modification or redistribution of this Apple software
+constitutes acceptance of these terms.  If you do not agree with these terms,
+please do not use, install, modify or redistribute this Apple software.
+
+In consideration of your agreement to abide by the following terms, and subject
+to these terms, Apple grants you a personal, non-exclusive license, under
+Apple's copyrights in this original Apple software (the "Apple Software"), to
+use, reproduce, modify and redistribute the Apple Software, with or without
+modifications, in source and/or binary forms; provided that if you redistribute
+the Apple Software in its entirety and without modifications, you must retain
+this notice and the following text and disclaimers in all such redistributions
+of the Apple Software.
+Neither the name, trademarks, service marks or logos of Apple Inc. may be used
+to endorse or promote products derived from the Apple Software without specific
+prior written permission from Apple.  Except as expressly stated in this notice,
+no other rights or licenses, express or implied, are granted by Apple herein,
+including but not limited to any patent rights that may be infringed by your
+derivative works or by other works in which the Apple Software may be
+incorporated.
+
+The Apple Software is provided by Apple on an "AS IS" basis.  APPLE MAKES NO
+WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED
+WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN
+COMBINATION WITH YOUR PRODUCTS.
+
+IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR
+DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF
+CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF
+APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+Copyright (C) 2008 Apple Inc. All Rights Reserved.
+
+*/
+
+/*
+ * Support for RGBA_4_4_4_4 and RGBA_5_5_5_1 was copied from:
+ * https://devforums.apple.com/message/37855#37855 by a1studmuffin
+ */
+
+
+#import <Availability.h>
+
+#import "Platforms/CCGL.h"
+#import "Platforms/CCNS.h"
+
+
+#import "CCTexture2D.h"
+#import "ccConfig.h"
+#import "ccMacros.h"
+#import "CCConfiguration.h"
+#import "Support/ccUtils.h"
+#import "CCTexturePVR.h"
+
+#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && CC_FONT_LABEL_SUPPORT
+// FontLabel support
+#import "FontManager.h"
+#import "FontLabelStringDrawing.h"
+#endif// CC_FONT_LABEL_SUPPORT
+
+
+// For Labels use 16-bit textures on iPhone 3GS / iPads since A8 textures are very slow
+#if (defined(__ARM_NEON__) || TARGET_IPHONE_SIMULATOR) && CC_USE_LA88_LABELS_ON_NEON_ARCH
+#define USE_TEXT_WITH_A8_TEXTURES 0
+
+#else
+#define USE_TEXT_WITH_A8_TEXTURES 1
+#endif
+
+//CLASS IMPLEMENTATIONS:
+
+
+// If the image has alpha, you can create RGBA8 (32-bit) or RGBA4 (16-bit) or RGB5A1 (16-bit)
+// Default is: RGBA8888 (32-bit textures)
+static CCTexture2DPixelFormat defaultAlphaPixelFormat_ = kCCTexture2DPixelFormat_Default;
+
+#pragma mark -
+#pragma mark CCTexture2D - Main
+
+@implementation CCTexture2D
+
+@synthesize contentSizeInPixels = size_, pixelFormat = format_, pixelsWide = width_, pixelsHigh = height_, name = name_, maxS = maxS_, maxT = maxT_;
+@synthesize hasPremultipliedAlpha = hasPremultipliedAlpha_;
+
+- (id) initWithData:(const void*)data pixelFormat:(CCTexture2DPixelFormat)pixelFormat pixelsWide:(NSUInteger)width pixelsHigh:(NSUInteger)height contentSize:(CGSize)size
+{
+       if((self = [super init])) {
+               glGenTextures(1, &name_);
+               glBindTexture(GL_TEXTURE_2D, name_);
+
+               [self setAntiAliasTexParameters];
+               
+               // Specify OpenGL texture image
+               
+               switch(pixelFormat)
+               {
+                       case kCCTexture2DPixelFormat_RGBA8888:
+                               glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei) width, (GLsizei) height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
+                               break;
+                       case kCCTexture2DPixelFormat_RGBA4444:
+                               glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei) width, (GLsizei) height, 0, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, data);
+                               break;
+                       case kCCTexture2DPixelFormat_RGB5A1:
+                               glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei) width, (GLsizei) height, 0, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1, data);
+                               break;
+                       case kCCTexture2DPixelFormat_RGB565:
+                               glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, (GLsizei) width, (GLsizei) height, 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, data);
+                               break;
+                       case kCCTexture2DPixelFormat_AI88:
+                               glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE_ALPHA, (GLsizei) width, (GLsizei) height, 0, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, data);
+                               break;
+                       case kCCTexture2DPixelFormat_A8:
+                               glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, (GLsizei) width, (GLsizei) height, 0, GL_ALPHA, GL_UNSIGNED_BYTE, data);
+                               break;
+                       default:
+                               [NSException raise:NSInternalInconsistencyException format:@""];
+                               
+               }
+
+               size_ = size;
+               width_ = width;
+               height_ = height;
+               format_ = pixelFormat;
+               maxS_ = size.width / (float)width;
+               maxT_ = size.height / (float)height;
+
+               hasPremultipliedAlpha_ = NO;
+       }                                       
+       return self;
+}
+
+- (void) releaseData:(void*)data
+{
+       //Free data
+       free(data);
+}
+
+- (void*) keepData:(void*)data length:(NSUInteger)length
+{
+       //The texture data mustn't be saved becuase it isn't a mutable texture.
+       return data;
+}
+
+- (void) dealloc
+{
+       CCLOGINFO(@"cocos2d: deallocing %@", self);
+       if(name_)
+               glDeleteTextures(1, &name_);
+       
+       [super dealloc];
+}
+
+- (NSString*) description
+{
+       return [NSString stringWithFormat:@"<%@ = %08X | Name = %i | Dimensions = %ix%i | Coordinates = (%.2f, %.2f)>", [self class], self, name_, width_, height_, maxS_, maxT_];
+}
+
+-(CGSize) contentSize
+{
+       CGSize ret;
+       ret.width = size_.width / CC_CONTENT_SCALE_FACTOR();
+       ret.height = size_.height / CC_CONTENT_SCALE_FACTOR();
+       
+       return ret;
+}
+@end
+
+#pragma mark -
+#pragma mark CCTexture2D - Image
+
+@implementation CCTexture2D (Image)
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+- (id) initWithImage:(UIImage *)uiImage
+#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED)
+- (id) initWithImage:(CGImageRef)CGImage
+#endif
+{
+       NSUInteger                              POTWide, POTHigh;
+       CGContextRef                    context = nil;
+       void*                                   data = nil;;
+       CGColorSpaceRef                 colorSpace;
+       void*                                   tempData;
+       unsigned int*                   inPixel32;
+       unsigned short*                 outPixel16;
+       BOOL                                    hasAlpha;
+       CGImageAlphaInfo                info;
+       CGSize                                  imageSize;
+       CCTexture2DPixelFormat  pixelFormat;
+       
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+       CGImageRef      CGImage = uiImage.CGImage;
+#endif
+       
+       if(CGImage == NULL) {
+               CCLOG(@"cocos2d: CCTexture2D. Can't create Texture. UIImage is nil");
+               [self release];
+               return nil;
+       }
+       
+       CCConfiguration *conf = [CCConfiguration sharedConfiguration];
+
+#if CC_TEXTURE_NPOT_SUPPORT
+       if( [conf supportsNPOT] ) {
+               POTWide = CGImageGetWidth(CGImage);
+               POTHigh = CGImageGetHeight(CGImage);
+
+       } else 
+#endif
+       {
+               POTWide = ccNextPOT(CGImageGetWidth(CGImage));
+               POTHigh = ccNextPOT(CGImageGetHeight(CGImage));
+       }
+               
+       NSUInteger maxTextureSize = [conf maxTextureSize];
+       if( POTHigh > maxTextureSize || POTWide > maxTextureSize ) {
+               CCLOG(@"cocos2d: WARNING: Image (%lu x %lu) is bigger than the supported %ld x %ld",
+                         (long)POTWide, (long)POTHigh,
+                         (long)maxTextureSize, (long)maxTextureSize);
+               [self release];
+               return nil;
+       }
+       
+       info = CGImageGetAlphaInfo(CGImage);
+       hasAlpha = ((info == kCGImageAlphaPremultipliedLast) || (info == kCGImageAlphaPremultipliedFirst) || (info == kCGImageAlphaLast) || (info == kCGImageAlphaFirst) ? YES : NO);
+       
+       size_t bpp = CGImageGetBitsPerComponent(CGImage);
+       colorSpace = CGImageGetColorSpace(CGImage);
+
+       if(colorSpace) {
+               if(hasAlpha || bpp >= 8)
+                       pixelFormat = defaultAlphaPixelFormat_;
+               else {
+                       CCLOG(@"cocos2d: CCTexture2D: Using RGB565 texture since image has no alpha");
+                       pixelFormat = kCCTexture2DPixelFormat_RGB565;
+               }
+       } else {
+               // NOTE: No colorspace means a mask image
+               CCLOG(@"cocos2d: CCTexture2D: Using A8 texture since image is a mask");
+               pixelFormat = kCCTexture2DPixelFormat_A8;
+       }
+       
+       imageSize = CGSizeMake(CGImageGetWidth(CGImage), CGImageGetHeight(CGImage));
+
+       // Create the bitmap graphics context
+       
+       switch(pixelFormat) {          
+               case kCCTexture2DPixelFormat_RGBA8888:
+               case kCCTexture2DPixelFormat_RGBA4444:
+               case kCCTexture2DPixelFormat_RGB5A1:
+                       colorSpace = CGColorSpaceCreateDeviceRGB();
+                       data = malloc(POTHigh * POTWide * 4);
+                       info = hasAlpha ? kCGImageAlphaPremultipliedLast : kCGImageAlphaNoneSkipLast; 
+//                     info = kCGImageAlphaPremultipliedLast;  // issue #886. This patch breaks BMP images.
+                       context = CGBitmapContextCreate(data, POTWide, POTHigh, 8, 4 * POTWide, colorSpace, info | kCGBitmapByteOrder32Big);                            
+                       CGColorSpaceRelease(colorSpace);
+                       break;
+
+               case kCCTexture2DPixelFormat_RGB565:
+                       colorSpace = CGColorSpaceCreateDeviceRGB();
+                       data = malloc(POTHigh * POTWide * 4);
+                       info = kCGImageAlphaNoneSkipLast;
+                       context = CGBitmapContextCreate(data, POTWide, POTHigh, 8, 4 * POTWide, colorSpace, info | kCGBitmapByteOrder32Big);
+                       CGColorSpaceRelease(colorSpace);
+                       break;
+               case kCCTexture2DPixelFormat_A8:
+                       data = malloc(POTHigh * POTWide);
+                       info = kCGImageAlphaOnly; 
+                       context = CGBitmapContextCreate(data, POTWide, POTHigh, 8, POTWide, NULL, info);
+                       break;                    
+               default:
+                       [NSException raise:NSInternalInconsistencyException format:@"Invalid pixel format"];
+       }
+       
+       
+       CGContextClearRect(context, CGRectMake(0, 0, POTWide, POTHigh));
+       CGContextTranslateCTM(context, 0, POTHigh - imageSize.height);
+       CGContextDrawImage(context, CGRectMake(0, 0, CGImageGetWidth(CGImage), CGImageGetHeight(CGImage)), CGImage);
+       
+       // Repack the pixel data into the right format
+       
+       if(pixelFormat == kCCTexture2DPixelFormat_RGB565) {
+               //Convert "RRRRRRRRRGGGGGGGGBBBBBBBBAAAAAAAA" to "RRRRRGGGGGGBBBBB"
+               tempData = malloc(POTHigh * POTWide * 2);
+               inPixel32 = (unsigned int*)data;
+               outPixel16 = (unsigned short*)tempData;
+               for(unsigned int i = 0; i < POTWide * POTHigh; ++i, ++inPixel32)
+                       *outPixel16++ = ((((*inPixel32 >> 0) & 0xFF) >> 3) << 11) | ((((*inPixel32 >> 8) & 0xFF) >> 2) << 5) | ((((*inPixel32 >> 16) & 0xFF) >> 3) << 0);
+               free(data);
+               data = tempData;
+               
+       }
+       else if (pixelFormat == kCCTexture2DPixelFormat_RGBA4444) {
+               //Convert "RRRRRRRRRGGGGGGGGBBBBBBBBAAAAAAAA" to "RRRRGGGGBBBBAAAA"
+               tempData = malloc(POTHigh * POTWide * 2);
+               inPixel32 = (unsigned int*)data;
+               outPixel16 = (unsigned short*)tempData;
+               for(unsigned int i = 0; i < POTWide * POTHigh; ++i, ++inPixel32)
+                       *outPixel16++ = 
+                       ((((*inPixel32 >> 0) & 0xFF) >> 4) << 12) | // R
+                       ((((*inPixel32 >> 8) & 0xFF) >> 4) << 8) | // G
+                       ((((*inPixel32 >> 16) & 0xFF) >> 4) << 4) | // B
+                       ((((*inPixel32 >> 24) & 0xFF) >> 4) << 0); // A
+               
+               
+               free(data);
+               data = tempData;
+               
+       }
+       else if (pixelFormat == kCCTexture2DPixelFormat_RGB5A1) {
+               //Convert "RRRRRRRRRGGGGGGGGBBBBBBBBAAAAAAAA" to "RRRRRGGGGGBBBBBA"
+               tempData = malloc(POTHigh * POTWide * 2);
+               inPixel32 = (unsigned int*)data;
+               outPixel16 = (unsigned short*)tempData;
+               for(unsigned int i = 0; i < POTWide * POTHigh; ++i, ++inPixel32)
+                       *outPixel16++ = 
+                       ((((*inPixel32 >> 0) & 0xFF) >> 3) << 11) | // R
+                       ((((*inPixel32 >> 8) & 0xFF) >> 3) << 6) | // G
+                       ((((*inPixel32 >> 16) & 0xFF) >> 3) << 1) | // B
+                       ((((*inPixel32 >> 24) & 0xFF) >> 7) << 0); // A
+               
+               
+               free(data);
+               data = tempData;
+       }
+       self = [self initWithData:data pixelFormat:pixelFormat pixelsWide:POTWide pixelsHigh:POTHigh contentSize:imageSize];
+       
+       // should be after calling super init
+       hasPremultipliedAlpha_ = (info == kCGImageAlphaPremultipliedLast || info == kCGImageAlphaPremultipliedFirst);
+       
+       CGContextRelease(context);
+       [self releaseData:data];
+       
+       return self;
+}
+@end
+
+#pragma mark -
+#pragma mark CCTexture2D - Text
+
+@implementation CCTexture2D (Text)
+
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+
+- (id) initWithString:(NSString*)string dimensions:(CGSize)dimensions alignment:(CCTextAlignment)alignment lineBreakMode:(CCLineBreakMode)lineBreakMode font:(id)uifont
+{
+       NSAssert( uifont, @"Invalid font");
+       
+       NSUInteger POTWide = ccNextPOT(dimensions.width);
+       NSUInteger POTHigh = ccNextPOT(dimensions.height);
+       unsigned char*                  data;
+       
+       CGContextRef                    context;
+       CGColorSpaceRef                 colorSpace;
+
+#if USE_TEXT_WITH_A8_TEXTURES
+       data = calloc(POTHigh, POTWide);
+#else
+       data = calloc(POTHigh, POTWide * 2);
+#endif
+
+       colorSpace = CGColorSpaceCreateDeviceGray();
+       context = CGBitmapContextCreate(data, POTWide, POTHigh, 8, POTWide, colorSpace, kCGImageAlphaNone);
+       CGColorSpaceRelease(colorSpace);
+       
+       if( ! context ) {
+               free(data);
+               [self release];
+               return nil;
+       }
+       
+       CGContextSetGrayFillColor(context, 1.0f, 1.0f);
+       CGContextTranslateCTM(context, 0.0f, POTHigh);
+       CGContextScaleCTM(context, 1.0f, -1.0f); //NOTE: NSString draws in UIKit referential i.e. renders upside-down compared to CGBitmapContext referential
+       
+       UIGraphicsPushContext(context);
+
+       // normal fonts
+       if( [uifont isKindOfClass:[UIFont class] ] )
+               [string drawInRect:CGRectMake(0, 0, dimensions.width, dimensions.height) withFont:uifont lineBreakMode:lineBreakMode alignment:alignment];
+       
+#if CC_FONT_LABEL_SUPPORT
+       else // ZFont class 
+               [string drawInRect:CGRectMake(0, 0, dimensions.width, dimensions.height) withZFont:uifont lineBreakMode:lineBreakMode alignment:alignment];
+#endif
+       
+       UIGraphicsPopContext();
+       
+#if USE_TEXT_WITH_A8_TEXTURES
+       self = [self initWithData:data pixelFormat:kCCTexture2DPixelFormat_A8 pixelsWide:POTWide pixelsHigh:POTHigh contentSize:dimensions];
+
+#else // ! USE_TEXT_WITH_A8_TEXTURES
+       NSUInteger textureSize = POTWide*POTHigh;
+       unsigned short *la88_data = (unsigned short*)data;
+       for(int i = textureSize-1; i>=0; i--) //Convert A8 to AI88
+               la88_data[i] = (data[i] << 8) | 0xff;
+       
+       self = [self initWithData:data pixelFormat:kCCTexture2DPixelFormat_AI88 pixelsWide:POTWide pixelsHigh:POTHigh contentSize:dimensions];
+#endif // ! USE_TEXT_WITH_A8_TEXTURES
+
+       CGContextRelease(context);
+       [self releaseData:data];
+                       
+       return self;
+}
+
+
+                                
+#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED)
+
+- (id) initWithString:(NSString*)string dimensions:(CGSize)dimensions alignment:(CCTextAlignment)alignment attributedString:(NSAttributedString*)stringWithAttributes
+{                              
+       NSAssert( stringWithAttributes, @"Invalid stringWithAttributes");
+
+       NSUInteger POTWide = ccNextPOT(dimensions.width);
+       NSUInteger POTHigh = ccNextPOT(dimensions.height);
+       unsigned char*                  data;
+       
+       NSSize realDimensions = [stringWithAttributes size];
+
+       //Alignment
+       float xPadding = 0;
+       
+       // Mac crashes if the width or height is 0
+       if( realDimensions.width > 0 && realDimensions.height > 0 ) {
+               switch (alignment) {
+                       case CCTextAlignmentLeft: xPadding = 0; break;
+                       case CCTextAlignmentCenter: xPadding = (dimensions.width-realDimensions.width)/2.0f; break;
+                       case CCTextAlignmentRight: xPadding = dimensions.width-realDimensions.width; break;
+                       default: break;
+               }
+               
+               //Disable antialias
+               [[NSGraphicsContext currentContext] setShouldAntialias:NO];     
+               
+               NSImage *image = [[NSImage alloc] initWithSize:NSMakeSize(POTWide, POTHigh)];
+               [image lockFocus];      
+               
+               [stringWithAttributes drawAtPoint:NSMakePoint(xPadding, POTHigh-dimensions.height)]; // draw at offset position 
+               
+               NSBitmapImageRep *bitmap = [[NSBitmapImageRep alloc] initWithFocusedViewRect:NSMakeRect (0.0f, 0.0f, POTWide, POTHigh)];
+               [image unlockFocus];
+               
+               data = (unsigned char*) [bitmap bitmapData];  //Use the same buffer to improve the performance.
+               
+               NSUInteger textureSize = POTWide*POTHigh;
+               for(int i = 0; i<textureSize; i++) //Convert RGBA8888 to A8
+                       data[i] = data[i*4+3];
+               
+               data = [self keepData:data length:textureSize];
+               self = [self initWithData:data pixelFormat:kCCTexture2DPixelFormat_A8 pixelsWide:POTWide pixelsHigh:POTHigh contentSize:dimensions];
+               
+               [bitmap release];
+               [image release]; 
+                       
+       } else {
+               [self release];
+               return nil;
+       }
+       
+       return self;
+}
+#endif // __MAC_OS_X_VERSION_MAX_ALLOWED
+
+- (id) initWithString:(NSString*)string fontName:(NSString*)name fontSize:(CGFloat)size
+{
+    CGSize dim;
+
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+       id font;
+       font = [UIFont fontWithName:name size:size];
+       if( font )
+               dim = [string sizeWithFont:font];
+
+#if CC_FONT_LABEL_SUPPORT
+       if( ! font ){
+               font = [[FontManager sharedManager] zFontWithName:name pointSize:size];
+               if (font)
+                       dim = [string sizeWithZFont:font];
+       }
+#endif // CC_FONT_LABEL_SUPPORT
+       
+       if( ! font ) {
+               CCLOG(@"cocos2d: Unable to load font %@", name);
+               [self release];
+               return nil;
+       }
+       
+       return [self initWithString:string dimensions:dim alignment:CCTextAlignmentCenter lineBreakMode:UILineBreakModeWordWrap font:font];
+       
+#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED)
+       {
+
+               NSAttributedString *stringWithAttributes =
+               [[[NSAttributedString alloc] initWithString:string
+                                                                                attributes:[NSDictionary dictionaryWithObject:[[NSFontManager sharedFontManager]
+                                                                                                                                                                               fontWithFamily:name
+                                                                                                                                                                               traits:NSUnboldFontMask | NSUnitalicFontMask
+                                                                                                                                                                               weight:0
+                                                                                                                                                                               size:size]
+                                                                                                                                                               forKey:NSFontAttributeName]
+                 ]
+                autorelease];
+       
+               dim = NSSizeToCGSize( [stringWithAttributes size] );
+                               
+               return [self initWithString:string dimensions:dim alignment:CCTextAlignmentCenter attributedString:stringWithAttributes];
+       }
+#endif // __MAC_OS_X_VERSION_MAX_ALLOWED
+    
+}
+
+- (id) initWithString:(NSString*)string dimensions:(CGSize)dimensions alignment:(CCTextAlignment)alignment fontName:(NSString*)name fontSize:(CGFloat)size
+{
+       return [self initWithString:string dimensions:dimensions alignment:alignment lineBreakMode:CCLineBreakModeWordWrap fontName:name fontSize:size];
+}
+
+- (id) initWithString:(NSString*)string dimensions:(CGSize)dimensions alignment:(CCTextAlignment)alignment lineBreakMode:(CCLineBreakMode)lineBreakMode fontName:(NSString*)name fontSize:(CGFloat)size
+{
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+       id                                              uifont = nil;
+       
+       uifont = [UIFont fontWithName:name size:size];
+       
+#if CC_FONT_LABEL_SUPPORT
+       if( ! uifont )
+               uifont = [[FontManager sharedManager] zFontWithName:name pointSize:size];
+#endif // CC_FONT_LABEL_SUPPORT
+       if( ! uifont ) {
+               CCLOG(@"cocos2d: Texture2d: Invalid Font: %@. Verify the .ttf name", name);
+               [self release];
+               return nil;
+       }
+       
+       return [self initWithString:string dimensions:dimensions alignment:alignment lineBreakMode:lineBreakMode font:uifont];
+       
+#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED)
+       
+       NSAssert( lineBreakMode == CCLineBreakModeWordWrap, @"CCTexture2D: unsupported line break mode for Mac OS X");
+
+       //String with attributes
+       NSAttributedString *stringWithAttributes =
+       [[[NSAttributedString alloc] initWithString:string
+                                                                        attributes:[NSDictionary dictionaryWithObject:[[NSFontManager sharedFontManager]
+                                                                                                                                                                       fontWithFamily:name
+                                                                                                                                                                       traits:NSUnboldFontMask | NSUnitalicFontMask
+                                                                                                                                                                       weight:0
+                                                                                                                                                                       size:size]
+                                                                                                                                                       forKey:NSFontAttributeName]
+         ]
+        autorelease];
+       
+       return [self initWithString:string dimensions:dimensions alignment:alignment attributedString:stringWithAttributes];
+       
+#endif // Mac
+}
+@end
+
+#pragma mark -
+#pragma mark CCTexture2D - PVRSupport
+
+@implementation CCTexture2D (PVRSupport)
+
+// By default PVR images are treated as if they don't have the alpha channel premultiplied
+static BOOL PVRHaveAlphaPremultiplied_ = NO;
+
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+-(id) initWithPVRTCData: (const void*)data level:(int)level bpp:(int)bpp hasAlpha:(BOOL)hasAlpha length:(int)length pixelFormat:(CCTexture2DPixelFormat)pixelFormat
+{
+       //      GLint                                   saveName;
+       
+       if( ! [[CCConfiguration sharedConfiguration] supportsPVRTC] ) {
+               CCLOG(@"cocos2d: WARNING: PVRTC images is not supported");
+               [self release];
+               return nil;
+       }
+       
+       if((self = [super init])) {
+               glGenTextures(1, &name_);
+               glBindTexture(GL_TEXTURE_2D, name_);
+               
+               [self setAntiAliasTexParameters];
+               
+               GLenum format;
+               GLsizei size = length * length * bpp / 8;
+               if(hasAlpha)
+                       format = (bpp == 4) ? GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG : GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG;
+               else
+                       format = (bpp == 4) ? GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG : GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG;
+               
+               if(size < 32)
+                       size = 32;
+               
+               glCompressedTexImage2D(GL_TEXTURE_2D, level, format, length, length, 0, size, data);
+               
+               size_ = CGSizeMake(length, length);
+               width_ = length;
+               height_ = length;
+               maxS_ = 1.0f;
+               maxT_ = 1.0f;
+               hasPremultipliedAlpha_ = PVRHaveAlphaPremultiplied_;
+               format_ = pixelFormat;
+       }                                       
+       return self;
+}
+#endif // __IPHONE_OS_VERSION_MAX_ALLOWED
+
+-(id) initWithPVRFile: (NSString*) file
+{
+       if( (self = [super init]) ) {
+               CCTexturePVR *pvr = [[CCTexturePVR alloc] initWithContentsOfFile:file];
+               if( pvr ) {
+                       pvr.retainName = YES;   // don't dealloc texture on release
+                       
+                       name_ = pvr.name;       // texture id
+                       maxS_ = 1;                      // only POT texture are supported
+                       maxT_ = 1;
+                       width_ = pvr.width;
+                       height_ = pvr.height;
+                       size_ = CGSizeMake(width_, height_);
+                       hasPremultipliedAlpha_ = PVRHaveAlphaPremultiplied_;
+                       format_ = pvr.format;
+                       
+                       [pvr release];
+                       
+                       [self setAntiAliasTexParameters];
+               } else {
+                       
+                       CCLOG(@"cocos2d: Couldn't load PVR image: %@", file);
+                       [self release];
+                       return nil;
+               }
+       }
+       return self;
+}
+
++(void) PVRImagesHavePremultipliedAlpha:(BOOL)haveAlphaPremultiplied
+{
+       PVRHaveAlphaPremultiplied_ = haveAlphaPremultiplied;
+}
+@end
+
+#pragma mark -
+#pragma mark CCTexture2D - Drawing
+
+@implementation CCTexture2D (Drawing)
+
+- (void) drawAtPoint:(CGPoint)point 
+{
+       GLfloat         coordinates[] = { 0.0f, maxT_,
+                                                               maxS_,  maxT_,
+                                                               0.0f,   0.0f,
+                                                               maxS_,  0.0f };
+       GLfloat         width = (GLfloat)width_ * maxS_,
+                               height = (GLfloat)height_ * maxT_;
+
+       GLfloat         vertices[] = {  point.x,                        point.y,        0.0f,
+                                                               width + point.x,        point.y,        0.0f,
+                                                               point.x,                        height  + point.y,      0.0f,
+                                                               width + point.x,        height  + point.y,      0.0f };
+       
+       glBindTexture(GL_TEXTURE_2D, name_);
+       glVertexPointer(3, GL_FLOAT, 0, vertices);
+       glTexCoordPointer(2, GL_FLOAT, 0, coordinates);
+       glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
+}
+
+
+- (void) drawInRect:(CGRect)rect
+{
+       GLfloat  coordinates[] = {  0.0f,       maxT_,
+                                                               maxS_,  maxT_,
+                                                               0.0f,   0.0f,
+                                                               maxS_,  0.0f  };
+       GLfloat vertices[] = {  rect.origin.x,                                                  rect.origin.y,                                                  /*0.0f,*/
+                                                       rect.origin.x + rect.size.width,                rect.origin.y,                                                  /*0.0f,*/
+                                                       rect.origin.x,                                                  rect.origin.y + rect.size.height,               /*0.0f,*/
+                                                       rect.origin.x + rect.size.width,                rect.origin.y + rect.size.height,               /*0.0f*/ };
+       
+       glBindTexture(GL_TEXTURE_2D, name_);
+       glVertexPointer(2, GL_FLOAT, 0, vertices);
+       glTexCoordPointer(2, GL_FLOAT, 0, coordinates);
+       glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
+}
+
+@end
+
+
+#pragma mark -
+#pragma mark CCTexture2D - GLFilter
+
+//
+// Use to apply MIN/MAG filter
+//
+@implementation CCTexture2D (GLFilter)
+
+-(void) generateMipmap
+{
+       NSAssert( width_ == ccNextPOT(width_) && height_ == ccNextPOT(height_), @"Mimpap texture only works in POT textures");
+       glBindTexture( GL_TEXTURE_2D, name_ );
+       ccglGenerateMipmap(GL_TEXTURE_2D);
+}
+
+-(void) setTexParameters: (ccTexParams*) texParams
+{
+       NSAssert( (width_ == ccNextPOT(width_) && height_ == ccNextPOT(height_)) ||
+                        (texParams->wrapS == GL_CLAMP_TO_EDGE && texParams->wrapT == GL_CLAMP_TO_EDGE),
+                        @"GL_CLAMP_TO_EDGE should be used in NPOT textures");
+       glBindTexture( GL_TEXTURE_2D, name_ );
+       glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, texParams->minFilter );
+       glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, texParams->magFilter );
+       glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, texParams->wrapS );
+       glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, texParams->wrapT );
+}
+
+-(void) setAliasTexParameters
+{
+       ccTexParams texParams = { GL_NEAREST, GL_NEAREST, GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE };
+       [self setTexParameters: &texParams];
+}
+
+-(void) setAntiAliasTexParameters
+{
+       ccTexParams texParams = { GL_LINEAR, GL_LINEAR, GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE };
+       [self setTexParameters: &texParams];
+}
+@end
+
+
+#pragma mark -
+#pragma mark CCTexture2D - Pixel Format
+
+//
+// Texture options for images that contains alpha
+//
+@implementation CCTexture2D (PixelFormat)
++(void) setDefaultAlphaPixelFormat:(CCTexture2DPixelFormat)format
+{
+       defaultAlphaPixelFormat_ = format;
+}
+
++(CCTexture2DPixelFormat) defaultAlphaPixelFormat
+{
+       return defaultAlphaPixelFormat_;
+}
+
+-(NSInteger) bitsPerPixelForFormat
+{
+       NSInteger ret=-1;
+
+       switch (format_) {
+               case kCCTexture2DPixelFormat_RGBA8888:
+                       ret = 32;
+                       break;
+               case kCCTexture2DPixelFormat_RGB565:
+                       ret = 16;
+                       break;
+               case kCCTexture2DPixelFormat_A8:
+                       ret = 8;
+                       break;
+               case kCCTexture2DPixelFormat_RGBA4444:
+                       ret = 16;
+                       break;
+               case kCCTexture2DPixelFormat_RGB5A1:
+                       ret = 16;
+                       break;
+               case kCCTexture2DPixelFormat_PVRTC4:
+                       ret = 4;
+                       break;
+               case kCCTexture2DPixelFormat_PVRTC2:
+                       ret = 2;
+                       break;
+               case kCCTexture2DPixelFormat_I8:
+                       ret = 8;
+                       break;
+               case kCCTexture2DPixelFormat_AI88:
+                       ret = 16;
+                       break;
+               default:
+                       ret = -1;
+                       NSAssert1(NO , @"bitsPerPixelForFormat: %ld, unrecognised pixel format", (long)format_);
+                       CCLOG(@"bitsPerPixelForFormat: %ld, cannot give useful result", (long)format_);
+                       break;
+       }
+       return ret;
+}
+@end
+
diff --git a/libs/cocos2d/CCTextureAtlas.h b/libs/cocos2d/CCTextureAtlas.h
new file mode 100644 (file)
index 0000000..f70bb54
--- /dev/null
@@ -0,0 +1,147 @@
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2008-2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+#import "CCTexture2D.h"
+#import "ccTypes.h"
+#import "ccConfig.h"
+
+/** A class that implements a Texture Atlas.
+ Supported features:
+   * The atlas file can be a PVRTC, PNG or any other fomrat supported by Texture2D
+   * Quads can be udpated in runtime
+   * Quads can be added in runtime
+   * Quads can be removed in runtime
+   * Quads can be re-ordered in runtime
+   * The TextureAtlas capacity can be increased or decreased in runtime
+   * OpenGL component: V3F, C4B, T2F.
+ The quads are rendered using an OpenGL ES VBO.
+ To render the quads using an interleaved vertex array list, you should modify the ccConfig.h file 
+ */
+@interface CCTextureAtlas : NSObject
+{
+       NSUInteger                      totalQuads_;
+       NSUInteger                      capacity_;
+       ccV3F_C4B_T2F_Quad      *quads_;        // quads to be rendered
+       GLushort                        *indices_;
+       CCTexture2D                     *texture_;
+#if CC_USES_VBO
+       GLuint                          buffersVBO_[2]; //0: vertex  1: indices
+       BOOL                            dirty_; //indicates whether or not the array buffer of the VBO needs to be updated
+#endif // CC_USES_VBO
+}
+
+/** quantity of quads that are going to be drawn */
+@property (nonatomic,readonly) NSUInteger totalQuads;
+/** quantity of quads that can be stored with the current texture atlas size */
+@property (nonatomic,readonly) NSUInteger capacity;
+/** Texture of the texture atlas */
+@property (nonatomic,retain) CCTexture2D *texture;
+/** Quads that are going to be rendered */
+@property (nonatomic,readwrite) ccV3F_C4B_T2F_Quad *quads;
+
+/** creates a TextureAtlas with an filename and with an initial capacity for Quads.
+ * The TextureAtlas capacity can be increased in runtime.
+ */
++(id) textureAtlasWithFile:(NSString*)file capacity:(NSUInteger)capacity;
+
+/** initializes a TextureAtlas with a filename and with a certain capacity for Quads.
+ * The TextureAtlas capacity can be increased in runtime.
+ *
+ * WARNING: Do not reinitialize the TextureAtlas because it will leak memory (issue #706)
+ */
+-(id) initWithFile: (NSString*) file capacity:(NSUInteger)capacity;
+
+/** creates a TextureAtlas with a previously initialized Texture2D object, and
+ * with an initial capacity for n Quads. 
+ * The TextureAtlas capacity can be increased in runtime.
+ */
++(id) textureAtlasWithTexture:(CCTexture2D *)tex capacity:(NSUInteger)capacity;
+
+/** initializes a TextureAtlas with a previously initialized Texture2D object, and
+ * with an initial capacity for Quads. 
+ * The TextureAtlas capacity can be increased in runtime.
+ *
+ * WARNING: Do not reinitialize the TextureAtlas because it will leak memory (issue #706)
+ */
+-(id) initWithTexture:(CCTexture2D *)tex capacity:(NSUInteger)capacity;
+
+/** updates a Quad (texture, vertex and color) at a certain index
+ * index must be between 0 and the atlas capacity - 1
+ @since v0.8
+ */
+-(void) updateQuad:(ccV3F_C4B_T2F_Quad*)quad atIndex:(NSUInteger)index;
+
+/** Inserts a Quad (texture, vertex and color) at a certain index
+ index must be between 0 and the atlas capacity - 1
+ @since v0.8
+ */
+-(void) insertQuad:(ccV3F_C4B_T2F_Quad*)quad atIndex:(NSUInteger)index;
+
+/** Removes the quad that is located at a certain index and inserts it at a new index
+ This operation is faster than removing and inserting in a quad in 2 different steps
+ @since v0.7.2
+*/
+-(void) insertQuadFromIndex:(NSUInteger)fromIndex atIndex:(NSUInteger)newIndex;
+
+/** removes a quad at a given index number.
+ The capacity remains the same, but the total number of quads to be drawn is reduced in 1
+ @since v0.7.2
+ */
+-(void) removeQuadAtIndex:(NSUInteger) index;
+
+/** removes all Quads.
+ The TextureAtlas capacity remains untouched. No memory is freed.
+ The total number of quads to be drawn will be 0
+ @since v0.7.2
+ */
+-(void) removeAllQuads;
+
+/** resize the capacity of the CCTextureAtlas.
+ * The new capacity can be lower or higher than the current one
+ * It returns YES if the resize was successful.
+ * If it fails to resize the capacity it will return NO with a new capacity of 0.
+ */
+-(BOOL) resizeCapacity: (NSUInteger) n;
+
+
+/** draws n quads
+ * n can't be greater than the capacity of the Atlas
+ */
+-(void) drawNumberOfQuads: (NSUInteger) n;
+
+
+/** draws n quads from an index (offset).
+ n + start can't be greater than the capacity of the atlas
+ @since v1.0
+ */
+-(void) drawNumberOfQuads: (NSUInteger) n fromIndex: (NSUInteger) start;
+
+/** draws all the Atlas's Quads
+ */
+-(void) drawQuads;
+
+@end
diff --git a/libs/cocos2d/CCTextureAtlas.m b/libs/cocos2d/CCTextureAtlas.m
new file mode 100644 (file)
index 0000000..7c7df75
--- /dev/null
@@ -0,0 +1,369 @@
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2008-2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+
+// cocos2d
+#import "CCTextureAtlas.h"
+#import "ccMacros.h"
+#import "CCTexture2D.h"
+#import "CCTextureCache.h"
+
+
+@interface CCTextureAtlas (Private)
+-(void) initIndices;
+@end
+
+//According to some tests GL_TRIANGLE_STRIP is slower, MUCH slower. Probably I'm doing something very wrong
+
+@implementation CCTextureAtlas
+
+@synthesize totalQuads = totalQuads_, capacity = capacity_;
+@synthesize texture = texture_;
+@synthesize quads = quads_;
+
+#pragma mark TextureAtlas - alloc & init
+
++(id) textureAtlasWithFile:(NSString*) file capacity: (NSUInteger) n
+{
+       return [[[self alloc] initWithFile:file capacity:n] autorelease];
+}
+
++(id) textureAtlasWithTexture:(CCTexture2D *)tex capacity:(NSUInteger)n
+{
+       return [[[self alloc] initWithTexture:tex capacity:n] autorelease];
+}
+
+-(id) initWithFile:(NSString*)file capacity:(NSUInteger)n
+{
+       // retained in property
+       CCTexture2D *tex = [[CCTextureCache sharedTextureCache] addImage:file];
+       if( tex )
+               return [self initWithTexture:tex capacity:n];
+
+       // else
+       {
+               CCLOG(@"cocos2d: Could not open file: %@", file);
+               [self release];
+               return nil;
+       }
+}
+
+-(id) initWithTexture:(CCTexture2D*)tex capacity:(NSUInteger)n
+{
+       if( (self=[super init]) ) {
+
+               capacity_ = n;
+               totalQuads_ = 0;
+               
+               // retained in property
+               self.texture = tex;
+
+               // Re-initialization is not allowed
+               NSAssert(quads_==nil && indices_==nil, @"CCTextureAtlas re-initialization is not allowed");
+
+               quads_ = calloc( sizeof(quads_[0]) * capacity_, 1 );
+               indices_ = calloc( sizeof(indices_[0]) * capacity_ * 6, 1 );
+
+               if( ! ( quads_ && indices_) ) {
+                       CCLOG(@"cocos2d: CCTextureAtlas: not enough memory");
+                       if( quads_ )
+                               free(quads_);
+                       if( indices_ )
+                               free(indices_);
+                       return nil;
+               }
+
+#if CC_USES_VBO
+               // initial binding
+               glGenBuffers(2, &buffersVBO_[0]);       
+               dirty_ = YES;
+#endif // CC_USES_VBO
+
+               [self initIndices];
+       }
+
+       return self;
+}
+
+- (NSString*) description
+{
+       return [NSString stringWithFormat:@"<%@ = %08X | totalQuads =  %i>", [self class], self, totalQuads_];
+}
+
+-(void) dealloc
+{
+       CCLOGINFO(@"cocos2d: deallocing %@",self);
+
+       free(quads_);
+       free(indices_);
+
+#if CC_USES_VBO
+       glDeleteBuffers(2, buffersVBO_);
+#endif // CC_USES_VBO
+
+
+       [texture_ release];
+
+       [super dealloc];
+}
+
+-(void) initIndices
+{
+       for( NSUInteger i=0;i< capacity_;i++) {
+#if CC_TEXTURE_ATLAS_USE_TRIANGLE_STRIP
+               indices_[i*6+0] = i*4+0;
+               indices_[i*6+1] = i*4+0;
+               indices_[i*6+2] = i*4+2;                
+               indices_[i*6+3] = i*4+1;
+               indices_[i*6+4] = i*4+3;
+               indices_[i*6+5] = i*4+3;
+#else
+               indices_[i*6+0] = i*4+0;
+               indices_[i*6+1] = i*4+1;
+               indices_[i*6+2] = i*4+2;
+
+               // inverted index. issue #179
+               indices_[i*6+3] = i*4+3;
+               indices_[i*6+4] = i*4+2;
+               indices_[i*6+5] = i*4+1;                
+//             indices_[i*6+3] = i*4+2;
+//             indices_[i*6+4] = i*4+3;
+//             indices_[i*6+5] = i*4+1;        
+#endif 
+       }
+
+#if CC_USES_VBO
+       glBindBuffer(GL_ARRAY_BUFFER, buffersVBO_[0]);
+       glBufferData(GL_ARRAY_BUFFER, sizeof(quads_[0]) * capacity_, quads_, GL_DYNAMIC_DRAW);
+       glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffersVBO_[1]);
+       glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices_[0]) * capacity_ * 6, indices_, GL_STATIC_DRAW);
+       glBindBuffer(GL_ARRAY_BUFFER, 0);
+       glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+#endif // CC_USES_VBO
+}
+
+#pragma mark TextureAtlas - Update, Insert, Move & Remove
+
+-(void) updateQuad:(ccV3F_C4B_T2F_Quad*)quad atIndex:(NSUInteger) n
+{
+       NSAssert(n < capacity_, @"updateQuadWithTexture: Invalid index");
+
+       totalQuads_ =  MAX( n+1, totalQuads_);
+
+       quads_[n] = *quad;      
+
+#if CC_USES_VBO
+       dirty_ = YES;
+#endif
+}
+
+
+-(void) insertQuad:(ccV3F_C4B_T2F_Quad*)quad atIndex:(NSUInteger)index
+{
+       NSAssert(index < capacity_, @"insertQuadWithTexture: Invalid index");
+
+       totalQuads_++;
+       NSAssert( totalQuads_ <= capacity_, @"invalid totalQuads");
+
+       // issue #575. index can be > totalQuads
+       NSInteger remaining = (totalQuads_-1) - index;
+
+       // last object doesn't need to be moved
+       if( remaining > 0)
+               // tex coordinates
+               memmove( &quads_[index+1],&quads_[index], sizeof(quads_[0]) * remaining );
+
+       quads_[index] = *quad;
+
+#if CC_USES_VBO
+       dirty_ = YES;
+#endif
+}
+
+
+-(void) insertQuadFromIndex:(NSUInteger)oldIndex atIndex:(NSUInteger)newIndex
+{
+       NSAssert(newIndex < totalQuads_, @"insertQuadFromIndex:atIndex: Invalid index");
+       NSAssert(oldIndex < totalQuads_, @"insertQuadFromIndex:atIndex: Invalid index");
+
+       if( oldIndex == newIndex )
+               return;
+
+       NSUInteger howMany = labs( oldIndex - newIndex);
+       NSUInteger dst = oldIndex;
+       NSUInteger src = oldIndex + 1;
+       if( oldIndex > newIndex) {
+               dst = newIndex+1;
+               src = newIndex;
+       }
+
+       // tex coordinates
+       ccV3F_C4B_T2F_Quad quadsBackup = quads_[oldIndex];
+       memmove( &quads_[dst],&quads_[src], sizeof(quads_[0]) * howMany );
+       quads_[newIndex] = quadsBackup;
+
+#if CC_USES_VBO
+       dirty_ = YES;
+#endif
+}
+
+-(void) removeQuadAtIndex:(NSUInteger) index
+{
+       NSAssert(index < totalQuads_, @"removeQuadAtIndex: Invalid index");
+
+       NSUInteger remaining = (totalQuads_-1) - index;
+
+
+       // last object doesn't need to be moved
+       if( remaining )
+               // tex coordinates
+               memmove( &quads_[index],&quads_[index+1], sizeof(quads_[0]) * remaining );
+
+       totalQuads_--;
+
+#if CC_USES_VBO
+       dirty_ = YES;
+#endif
+}
+
+-(void) removeAllQuads
+{
+       totalQuads_ = 0;
+}
+
+#pragma mark TextureAtlas - Resize
+
+-(BOOL) resizeCapacity: (NSUInteger) newCapacity
+{
+       if( newCapacity == capacity_ )
+               return YES;
+
+       // update capacity and totolQuads
+       totalQuads_ = MIN(totalQuads_,newCapacity);
+       capacity_ = newCapacity;
+
+       void * tmpQuads = realloc( quads_, sizeof(quads_[0]) * capacity_ );
+       void * tmpIndices = realloc( indices_, sizeof(indices_[0]) * capacity_ * 6 );
+
+       if( ! ( tmpQuads && tmpIndices) ) {
+               CCLOG(@"cocos2d: CCTextureAtlas: not enough memory");
+               if( tmpQuads )
+                       free(tmpQuads);
+               else
+                       free(quads_);
+
+               if( tmpIndices )
+                       free(tmpIndices);
+               else
+                       free(indices_);
+
+               indices_ = nil;
+               quads_ = nil;
+               capacity_ = totalQuads_ = 0;
+               return NO;
+       }
+
+       quads_ = tmpQuads;
+       indices_ = tmpIndices;
+
+       [self initIndices];     
+
+#if CC_USES_VBO
+       dirty_ = YES;
+#endif 
+       return YES;
+}
+
+#pragma mark TextureAtlas - Drawing
+
+-(void) drawQuads
+{
+       [self drawNumberOfQuads: totalQuads_ fromIndex:0];
+}
+
+-(void) drawNumberOfQuads: (NSUInteger) n
+{      
+       [self drawNumberOfQuads:n fromIndex:0];
+}
+
+-(void) drawNumberOfQuads: (NSUInteger) n fromIndex: (NSUInteger) start
+{
+       // Default GL states: GL_TEXTURE_2D, GL_VERTEX_ARRAY, GL_COLOR_ARRAY, GL_TEXTURE_COORD_ARRAY
+       // Needed states: GL_TEXTURE_2D, GL_VERTEX_ARRAY, GL_COLOR_ARRAY, GL_TEXTURE_COORD_ARRAY
+       // Unneeded states: -
+
+       glBindTexture(GL_TEXTURE_2D, [texture_ name]);
+#define kQuadSize sizeof(quads_[0].bl)
+#if CC_USES_VBO
+       glBindBuffer(GL_ARRAY_BUFFER, buffersVBO_[0]);
+
+       // XXX: update is done in draw... perhaps it should be done in a timer
+       if (dirty_) {
+               glBufferSubData(GL_ARRAY_BUFFER, sizeof(quads_[0])*start, sizeof(quads_[0]) * n , &quads_[start] );
+               dirty_ = NO;
+       }
+
+       // vertices
+       glVertexPointer(3, GL_FLOAT, kQuadSize, (GLvoid*) offsetof( ccV3F_C4B_T2F, vertices));
+
+       // colors
+       glColorPointer(4, GL_UNSIGNED_BYTE, kQuadSize, (GLvoid*) offsetof( ccV3F_C4B_T2F, colors));
+
+       // tex coords
+       glTexCoordPointer(2, GL_FLOAT, kQuadSize, (GLvoid*) offsetof( ccV3F_C4B_T2F, texCoords));
+
+       glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffersVBO_[1]);
+#if CC_TEXTURE_ATLAS_USE_TRIANGLE_STRIP
+       glDrawElements(GL_TRIANGLE_STRIP, (GLsizei) n*6, GL_UNSIGNED_SHORT, (GLvoid*) (start*6*sizeof(indices_[0])) );
+#else
+       glDrawElements(GL_TRIANGLES, (GLsizei) n*6, GL_UNSIGNED_SHORT, (GLvoid*) (start*6*sizeof(indices_[0])) );
+#endif // CC_TEXTURE_ATLAS_USE_TRIANGLE_STRIP
+
+       glBindBuffer(GL_ARRAY_BUFFER, 0);
+       glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+#else // ! CC_USES_VBO
+
+       NSUInteger offset = (NSUInteger)quads_;
+       // vertex
+       NSUInteger diff = offsetof( ccV3F_C4B_T2F, vertices);
+       glVertexPointer(3, GL_FLOAT, kQuadSize, (GLvoid*) (offset + diff) );
+       // color
+       diff = offsetof( ccV3F_C4B_T2F, colors);
+       glColorPointer(4, GL_UNSIGNED_BYTE, kQuadSize, (GLvoid*)(offset + diff));
+
+       // tex coords
+       diff = offsetof( ccV3F_C4B_T2F, texCoords);
+       glTexCoordPointer(2, GL_FLOAT, kQuadSize, (GLvoid*)(offset + diff));
+
+#if CC_TEXTURE_ATLAS_USE_TRIANGLE_STRIP
+       glDrawElements(GL_TRIANGLE_STRIP, n*6, GL_UNSIGNED_SHORT, indices_ + start * 6 );
+#else
+       glDrawElements(GL_TRIANGLES, n*6, GL_UNSIGNED_SHORT, indices_ + start * 6 );
+#endif
+
+#endif // CC_USES_VBO
+}
+@end
diff --git a/libs/cocos2d/CCTextureCache.h b/libs/cocos2d/CCTextureCache.h
new file mode 100644 (file)
index 0000000..08323f1
--- /dev/null
@@ -0,0 +1,149 @@
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2008-2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+#import <Availability.h>
+
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+#import <CoreGraphics/CGImage.h>
+#endif
+
+#import <Foundation/Foundation.h>
+
+@class CCTexture2D;
+
+/** Singleton that handles the loading of textures
+ * Once the texture is loaded, the next time it will return
+ * a reference of the previously loaded texture reducing GPU & CPU memory
+ */
+@interface CCTextureCache : NSObject
+{
+       NSMutableDictionary *textures_;
+       NSLock                          *dictLock_;
+       NSLock                          *contextLock_;
+}
+
+/** Retruns ths shared instance of the cache */
++ (CCTextureCache *) sharedTextureCache;
+
+/** purges the cache. It releases the retained instance.
+ @since v0.99.0
+ */
++(void)purgeSharedTextureCache;
+
+
+/** Returns a Texture2D object given an file image
+ * If the file image was not previously loaded, it will create a new CCTexture2D
+ *  object and it will return it. It will use the filename as a key.
+ * Otherwise it will return a reference of a previosly loaded image.
+ * Supported image extensions: .png, .bmp, .tiff, .jpeg, .pvr, .gif
+ */
+-(CCTexture2D*) addImage: (NSString*) fileimage;
+
+/** Returns a Texture2D object given a file image
+ * If the file image was not previously loaded, it will create a new CCTexture2D object and it will return it.
+ * Otherwise it will load a texture in a new thread, and when the image is loaded, the callback will be called with the Texture2D as a parameter.
+ * The callback will be called from the main thread, so it is safe to create any cocos2d object from the callback.
+ * Supported image extensions: .png, .bmp, .tiff, .jpeg, .pvr, .gif
+ * @since v0.8
+ */
+-(void) addImageAsync:(NSString*) filename target:(id)target selector:(SEL)selector;
+
+/** Returns a Texture2D object given an CGImageRef image
+ * If the image was not previously loaded, it will create a new CCTexture2D object and it will return it.
+ * Otherwise it will return a reference of a previously loaded image
+ * The "key" parameter will be used as the "key" for the cache.
+ * If "key" is nil, then a new texture will be created each time.
+ * @since v0.8
+ */
+-(CCTexture2D*) addCGImage: (CGImageRef) image forKey: (NSString *)key;
+
+/** Returns an already created texture. Returns nil if the texture doesn't exist.
+ @since v0.99.5
+ */
+-(CCTexture2D *) textureForKey:(NSString *)key;
+
+/** Purges the dictionary of loaded textures.
+ * Call this method if you receive the "Memory Warning"
+ * In the short term: it will free some resources preventing your app from being killed
+ * In the medium term: it will allocate more resources
+ * In the long term: it will be the same
+ */
+-(void) removeAllTextures;
+
+/** Removes unused textures
+ * Textures that have a retain count of 1 will be deleted
+ * It is convinient to call this method after when starting a new Scene
+ * @since v0.8
+ */
+-(void) removeUnusedTextures;
+
+/** Deletes a texture from the cache given a texture
+ */
+-(void) removeTexture: (CCTexture2D*) tex;
+
+/** Deletes a texture from the cache given a its key name
+ @since v0.99.4
+ */
+-(void) removeTextureForKey: (NSString*) textureKeyName;
+
+@end
+
+
+@interface CCTextureCache (PVRSupport)
+
+/** Returns a Texture2D object given an PVRTC RAW filename
+ * If the file image was not previously loaded, it will create a new CCTexture2D
+ *  object and it will return it. Otherwise it will return a reference of a previosly loaded image
+ *
+ * It can only load square images: width == height, and it must be a power of 2 (128,256,512...)
+ * bpp can only be 2 or 4. 2 means more compression but lower quality.
+ * hasAlpha: whether or not the image contains alpha channel
+ *
+ * IMPORTANT: This method is only defined on iOS. It is not supported on the Mac version.
+ */
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+-(CCTexture2D*) addPVRTCImage:(NSString*)fileimage bpp:(int)bpp hasAlpha:(BOOL)alpha width:(int)w;
+#endif // __IPHONE_OS_VERSION_MAX_ALLOWED
+
+/** Returns a Texture2D object given an PVR filename.
+ * If the file image was not previously loaded, it will create a new CCTexture2D
+ *  object and it will return it. Otherwise it will return a reference of a previosly loaded image
+ *
+ */
+-(CCTexture2D*) addPVRImage:(NSString*) filename;
+
+@end
+
+
+@interface CCTextureCache (Debug)
+/** Output to CCLOG the current contents of this CCTextureCache
+ * This will attempt to calculate the size of each texture, and the total texture memory in use
+ *
+ * @since v1.0
+ */
+-(void) dumpCachedTextureInfo;
+
+@end
\ No newline at end of file
diff --git a/libs/cocos2d/CCTextureCache.m b/libs/cocos2d/CCTextureCache.m
new file mode 100644 (file)
index 0000000..23825eb
--- /dev/null
@@ -0,0 +1,498 @@
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2008-2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+#import <Availability.h>
+
+#import "Platforms/CCGL.h"
+#import "CCTextureCache.h"
+#import "CCTexture2D.h"
+#import "CCTexturePVR.h"
+#import "ccMacros.h"
+#import "CCConfiguration.h"
+#import "Support/CCFileUtils.h"
+#import "CCDirector.h"
+#import "ccConfig.h"
+
+// needed for CCCallFuncO in Mac-display_link version
+#import "CCActionManager.h"
+#import "CCActionInstant.h"
+
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+static EAGLContext *auxGLcontext = nil;
+#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED)
+static NSOpenGLContext *auxGLcontext = nil;
+#endif
+
+
+@interface CCAsyncObject : NSObject
+{
+       SEL                     selector_;
+       id                      target_;
+       id                      data_;
+}
+@property      (readwrite,assign)      SEL                     selector;
+@property      (readwrite,retain)      id                      target;
+@property      (readwrite,retain)      id                      data;
+@end
+
+@implementation CCAsyncObject
+@synthesize selector = selector_;
+@synthesize target = target_;
+@synthesize data = data_;
+- (void) dealloc
+{
+       CCLOGINFO(@"cocos2d: deallocing %@", self);
+       [target_ release];
+       [data_ release];
+       [super dealloc];
+}
+@end
+
+
+@implementation CCTextureCache
+
+#pragma mark TextureCache - Alloc, Init & Dealloc
+static CCTextureCache *sharedTextureCache;
+
++ (CCTextureCache *)sharedTextureCache
+{
+       if (!sharedTextureCache)
+               sharedTextureCache = [[CCTextureCache alloc] init];
+               
+       return sharedTextureCache;
+}
+
++(id)alloc
+{
+       NSAssert(sharedTextureCache == nil, @"Attempted to allocate a second instance of a singleton.");
+       return [super alloc];
+}
+
++(void)purgeSharedTextureCache
+{
+       [sharedTextureCache release];
+       sharedTextureCache = nil;
+}
+
+-(id) init
+{
+       if( (self=[super init]) ) {
+               textures_ = [[NSMutableDictionary dictionaryWithCapacity: 10] retain];
+               dictLock_ = [[NSLock alloc] init];
+               contextLock_ = [[NSLock alloc] init];
+       }
+
+       return self;
+}
+
+- (NSString*) description
+{
+       return [NSString stringWithFormat:@"<%@ = %08X | num of textures =  %i | keys: %@>",
+                       [self class],
+                       self,
+                       [textures_ count],
+                       [textures_ allKeys]
+                       ];
+                       
+}
+
+-(void) dealloc
+{
+       CCLOGINFO(@"cocos2d: deallocing %@", self);
+
+       [textures_ release];
+       [dictLock_ release];
+       [contextLock_ release];
+       [auxGLcontext release];
+       auxGLcontext = nil;
+       sharedTextureCache = nil;
+       [super dealloc];
+}
+
+#pragma mark TextureCache - Add Images
+
+-(void) addImageWithAsyncObject:(CCAsyncObject*)async
+{
+       NSAutoreleasePool *autoreleasepool = [[NSAutoreleasePool alloc] init];
+       
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+       // textures will be created on the main OpenGL context
+       // it seems that in SDK 2.2.x there can't be 2 threads creating textures at the same time
+       // the lock is used for this purpose: issue #472
+       [contextLock_ lock];
+       if( auxGLcontext == nil ) {
+               auxGLcontext = [[EAGLContext alloc]
+                                                          initWithAPI:kEAGLRenderingAPIOpenGLES1
+                                                          sharegroup:[[[[CCDirector sharedDirector] openGLView] context] sharegroup]];
+               
+               if( ! auxGLcontext )
+                       CCLOG(@"cocos2d: TextureCache: Could not create EAGL context");
+       }
+       
+       if( [EAGLContext setCurrentContext:auxGLcontext] ) {
+
+               // load / create the texture
+               CCTexture2D *tex = [self addImage:async.data];
+
+               // The callback will be executed on the main thread
+               [async.target performSelectorOnMainThread:async.selector withObject:tex waitUntilDone:NO];              
+               
+               [EAGLContext setCurrentContext:nil];
+       } else {
+               CCLOG(@"cocos2d: TetureCache: EAGLContext error");
+       }
+       [contextLock_ unlock];
+       
+       [autoreleasepool release];
+
+#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED)
+
+       [contextLock_ lock];
+       if( auxGLcontext == nil ) {
+
+               MacGLView *view = [[CCDirector sharedDirector] openGLView];
+               
+               NSOpenGLPixelFormat *pf = [view pixelFormat];
+               NSOpenGLContext *share = [view openGLContext];
+
+               auxGLcontext = [[NSOpenGLContext alloc] initWithFormat:pf shareContext:share];
+
+               if( ! auxGLcontext )
+                       CCLOG(@"cocos2d: TextureCache: Could not create NSOpenGLContext");
+       }
+       
+       [auxGLcontext makeCurrentContext];
+               
+       // load / create the texture
+       CCTexture2D *tex = [self addImage:async.data];
+       
+#if CC_DIRECTOR_MAC_USE_DISPLAY_LINK_THREAD
+       id action = [CCCallFuncO actionWithTarget:async.target selector:async.selector object:tex];
+       [[CCActionManager sharedManager] addAction:action target:async.target paused:NO];
+#else
+       // The callback will be executed on the main thread
+       [async.target performSelector:async.selector
+                                                onThread:[[CCDirector sharedDirector] runningThread]
+                                          withObject:tex
+                                       waitUntilDone:NO];
+#endif
+       
+       
+       [NSOpenGLContext clearCurrentContext];
+
+       [contextLock_ unlock];
+       
+       [autoreleasepool release];
+       
+#endif // __MAC_OS_X_VERSION_MAX_ALLOWED
+}
+
+-(void) addImageAsync: (NSString*)path target:(id)target selector:(SEL)selector
+{
+       NSAssert(path != nil, @"TextureCache: fileimage MUST not be nill");
+
+       // optimization
+       
+       CCTexture2D * tex;
+       
+       path = ccRemoveHDSuffixFromFile(path);
+       
+       if( (tex=[textures_ objectForKey: path] ) ) {
+               [target performSelector:selector withObject:tex];
+               return;
+       }
+
+       // schedule the load
+       
+       CCAsyncObject *asyncObject = [[CCAsyncObject alloc] init];
+       asyncObject.selector = selector;
+       asyncObject.target = target;
+       asyncObject.data = path;
+       
+       [NSThread detachNewThreadSelector:@selector(addImageWithAsyncObject:) toTarget:self withObject:asyncObject];
+       [asyncObject release];
+}
+
+-(CCTexture2D*) addImage: (NSString*) path
+{
+       NSAssert(path != nil, @"TextureCache: fileimage MUST not be nill");
+
+       CCTexture2D * tex = nil;
+
+       // MUTEX:
+       // Needed since addImageAsync calls this method from a different thread
+       [dictLock_ lock];
+       
+       // remove possible -HD suffix to prevent caching the same image twice (issue #1040)
+       path = ccRemoveHDSuffixFromFile( path );
+
+       tex=[textures_ objectForKey: path];
+       
+       if( ! tex ) {
+               
+               NSString *lowerCase = [path lowercaseString];
+               // all images are handled by UIImage except PVR extension that is handled by our own handler
+               
+               if ( [lowerCase hasSuffix:@".pvr"] || [lowerCase hasSuffix:@".pvr.gz"] || [lowerCase hasSuffix:@".pvr.ccz"] )
+                       tex = [self addPVRImage:path];
+
+               // Only iPhone
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+
+               // Issue #886: TEMPORARY FIX FOR TRANSPARENT JPEGS IN IOS4
+               else if ( ( [[CCConfiguration sharedConfiguration] OSVersion] >= kCCiOSVersion_4_0) &&
+                                 ( [lowerCase hasSuffix:@".jpg"] || [lowerCase hasSuffix:@".jpeg"] ) 
+                                ) {
+                       // convert jpg to png before loading the texture
+                       
+                       NSString *fullpath = [CCFileUtils fullPathFromRelativePath: path ];
+                                               
+                       UIImage *jpg = [[UIImage alloc] initWithContentsOfFile:fullpath];
+                       UIImage *png = [[UIImage alloc] initWithData:UIImagePNGRepresentation(jpg)];
+                       tex = [ [CCTexture2D alloc] initWithImage: png ];
+                       [png release];
+                       [jpg release];
+                       
+                       if( tex )
+                               [textures_ setObject: tex forKey:path];
+                       else
+                               CCLOG(@"cocos2d: Couldn't add image:%@ in CCTextureCache", path);
+                       
+                       // autorelease prevents possible crash in multithreaded environments
+                       [tex autorelease];
+               }
+               
+               else {
+                       
+                       // prevents overloading the autorelease pool
+                       NSString *fullpath = [CCFileUtils fullPathFromRelativePath: path ];
+
+                       UIImage *image = [ [UIImage alloc] initWithContentsOfFile: fullpath ];
+                       tex = [ [CCTexture2D alloc] initWithImage: image ];
+                       [image release];
+                       
+                       if( tex )
+                               [textures_ setObject: tex forKey:path];
+                       else
+                               CCLOG(@"cocos2d: Couldn't add image:%@ in CCTextureCache", path);
+                       
+                       // autorelease prevents possible crash in multithreaded environments
+                       [tex autorelease];                      
+               }
+
+               // Only in Mac
+#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED)
+               else {
+                       NSString *fullpath = [CCFileUtils fullPathFromRelativePath: path ];
+
+                       NSData *data = [[NSData alloc] initWithContentsOfFile:fullpath];
+                       NSBitmapImageRep *image = [[NSBitmapImageRep alloc] initWithData:data];
+                       tex = [ [CCTexture2D alloc] initWithImage:[image CGImage]];
+                       
+                       [data release];
+                       [image release];
+
+                       if( tex )
+                               [textures_ setObject: tex forKey:path];
+                       else
+                               CCLOG(@"cocos2d: Couldn't add image:%@ in CCTextureCache", path);
+                       
+                       // autorelease prevents possible crash in multithreaded environments
+                       [tex autorelease];                      
+               }
+#endif // __MAC_OS_X_VERSION_MAX_ALLOWED
+
+       }
+       
+       [dictLock_ unlock];
+       
+       return tex;
+}
+
+
+-(CCTexture2D*) addCGImage: (CGImageRef) imageref forKey: (NSString *)key
+{
+       NSAssert(imageref != nil, @"TextureCache: image MUST not be nill");
+       
+       CCTexture2D * tex = nil;
+       
+       // If key is nil, then create a new texture each time
+       if( key && (tex=[textures_ objectForKey: key] ) ) {
+               return tex;
+       }
+       
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+       // prevents overloading the autorelease pool
+       UIImage *image = [[UIImage alloc] initWithCGImage:imageref];
+       tex = [[CCTexture2D alloc] initWithImage: image];
+       [image release];
+
+#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED)
+       tex = [[CCTexture2D alloc] initWithImage: imageref];
+#endif
+       
+       if(tex && key)
+               [textures_ setObject: tex forKey:key];
+       else
+               CCLOG(@"cocos2d: Couldn't add CGImage in CCTextureCache");
+       
+       return [tex autorelease];
+}
+
+#pragma mark TextureCache - Remove
+
+-(void) removeAllTextures
+{
+       [textures_ removeAllObjects];
+}
+
+-(void) removeUnusedTextures
+{
+       NSArray *keys = [textures_ allKeys];
+       for( id key in keys ) {
+               id value = [textures_ objectForKey:key];                
+               if( [value retainCount] == 1 ) {
+                       CCLOG(@"cocos2d: CCTextureCache: removing unused texture: %@", key);
+                       [textures_ removeObjectForKey:key];
+               }
+       }
+}
+
+-(void) removeTexture: (CCTexture2D*) tex
+{
+       if( ! tex )
+               return;
+       
+       NSArray *keys = [textures_ allKeysForObject:tex];
+       
+       for( NSUInteger i = 0; i < [keys count]; i++ )
+               [textures_ removeObjectForKey:[keys objectAtIndex:i]];
+}
+
+-(void) removeTextureForKey:(NSString*)name
+{
+       if( ! name )
+               return;
+       
+       [textures_ removeObjectForKey:name];
+}
+
+#pragma mark TextureCache - Get
+- (CCTexture2D *)textureForKey:(NSString *)key
+{
+    return [textures_ objectForKey:key];    
+}
+
+@end
+
+
+@implementation CCTextureCache (PVRSupport)
+
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+-(CCTexture2D*) addPVRTCImage:(NSString*)path bpp:(int)bpp hasAlpha:(BOOL)alpha width:(int)w
+{
+       NSAssert(path != nil, @"TextureCache: fileimage MUST not be nill");
+       NSAssert( bpp==2 || bpp==4, @"TextureCache: bpp must be either 2 or 4");
+       
+       CCTexture2D * tex;
+       
+       // remove possible -HD suffix to prevent caching the same image twice (issue #1040)
+       path = ccRemoveHDSuffixFromFile( path );
+
+       if( (tex=[textures_ objectForKey: path] ) ) {
+               return tex;
+       }
+       
+       // Split up directory and filename
+       NSString *fullpath = [CCFileUtils fullPathFromRelativePath:path];
+       
+       NSData *nsdata = [[NSData alloc] initWithContentsOfFile:fullpath];
+       tex = [[CCTexture2D alloc] initWithPVRTCData:[nsdata bytes] level:0 bpp:bpp hasAlpha:alpha length:w pixelFormat:bpp==2?kCCTexture2DPixelFormat_PVRTC2:kCCTexture2DPixelFormat_PVRTC4];
+       if( tex )
+               [textures_ setObject: tex forKey:path];
+       else
+               CCLOG(@"cocos2d: Couldn't add PVRTCImage:%@ in CCTextureCache",path);
+       
+       [nsdata release];
+       
+       return [tex autorelease];
+}
+#endif // __IPHONE_OS_VERSION_MAX_ALLOWED
+
+-(CCTexture2D*) addPVRImage:(NSString*)path
+{
+       NSAssert(path != nil, @"TextureCache: fileimage MUST not be nill");
+       
+       CCTexture2D * tex;
+       
+       // remove possible -HD suffix to prevent caching the same image twice (issue #1040)
+       path = ccRemoveHDSuffixFromFile( path );
+
+       if( (tex=[textures_ objectForKey: path] ) ) {
+               return tex;
+       }
+       
+       // Split up directory and filename
+       NSString *fullpath = [CCFileUtils fullPathFromRelativePath:path];
+       
+       tex = [[CCTexture2D alloc] initWithPVRFile: fullpath];
+       if( tex )
+               [textures_ setObject: tex forKey:path];
+       else
+               CCLOG(@"cocos2d: Couldn't add PVRImage:%@ in CCTextureCache",path);     
+       
+       return [tex autorelease];
+}
+
+@end
+
+
+@implementation CCTextureCache (Debug)
+
+-(void) dumpCachedTextureInfo
+{
+       NSUInteger count = 0;
+       NSUInteger totalBytes = 0;
+       for (NSString* texKey in textures_) {
+               CCTexture2D* tex = [textures_ objectForKey:texKey];
+               NSUInteger bpp = [tex bitsPerPixelForFormat];
+               // Each texture takes up width * height * bytesPerPixel bytes.
+               NSUInteger bytes = tex.pixelsWide * tex.pixelsWide * bpp / 8;
+               totalBytes += bytes;
+               count++;
+               CCLOG( @"cocos2d: \"%@\" rc=%lu id=%lu %lu x %lu @ %ld bpp => %lu KB",
+                         texKey,
+                         (long)[tex retainCount],
+                         (long)tex.name,
+                         (long)tex.pixelsWide,
+                         (long)tex.pixelsHigh,
+                         (long)bpp,
+                         (long)bytes / 1024 );
+       }
+       CCLOG( @"cocos2d: CCTextureCache dumpDebugInfo: %ld textures, for %lu KB (%.2f MB)", (long)count, (long)totalBytes / 1024, totalBytes / (1024.0f*1024.0f));
+}
+
+@end
\ No newline at end of file
diff --git a/libs/cocos2d/CCTexturePVR.h b/libs/cocos2d/CCTexturePVR.h
new file mode 100644 (file)
index 0000000..66f8286
--- /dev/null
@@ -0,0 +1,127 @@
+/*
+
+File: PVRTexture.h
+Abstract: The PVRTexture class is responsible for loading .pvr files.
+
+Version: 1.0
+
+Disclaimer: IMPORTANT:  This Apple software is supplied to you by Apple Inc.
+("Apple") in consideration of your agreement to the following terms, and your
+use, installation, modification or redistribution of this Apple software
+constitutes acceptance of these terms.  If you do not agree with these terms,
+please do not use, install, modify or redistribute this Apple software.
+
+In consideration of your agreement to abide by the following terms, and subject
+to these terms, Apple grants you a personal, non-exclusive license, under
+Apple's copyrights in this original Apple software (the "Apple Software"), to
+use, reproduce, modify and redistribute the Apple Software, with or without
+modifications, in source and/or binary forms; provided that if you redistribute
+the Apple Software in its entirety and without modifications, you must retain
+this notice and the following text and disclaimers in all such redistributions
+of the Apple Software.
+Neither the name, trademarks, service marks or logos of Apple Inc. may be used
+to endorse or promote products derived from the Apple Software without specific
+prior written permission from Apple.  Except as expressly stated in this notice,
+no other rights or licenses, express or implied, are granted by Apple herein,
+including but not limited to any patent rights that may be infringed by your
+derivative works or by other works in which the Apple Software may be
+incorporated.
+
+The Apple Software is provided by Apple on an "AS IS" basis.  APPLE MAKES NO
+WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED
+WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN
+COMBINATION WITH YOUR PRODUCTS.
+
+IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR
+DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF
+CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF
+APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+Copyright (C) 2008 Apple Inc. All Rights Reserved.
+
+*/
+
+#import <Foundation/Foundation.h>
+
+#import "Platforms/CCGL.h"
+#import "CCTexture2D.h"
+
+
+#pragma mark -
+#pragma mark CCTexturePVR
+
+struct CCPVRMipmap {
+       unsigned char *address;
+       unsigned int len;
+};
+
+enum {
+       CC_PVRMIPMAP_MAX = 16,
+};
+
+/** CCTexturePVR
+ Object that loads PVR images.
+ Supported PVR formats:
+       - RGBA8888
+       - BGRA8888
+       - RGBA4444
+       - RGBA5551
+       - RGB565
+       - A8
+       - I8
+       - AI88
+       - PVRTC 4BPP
+       - PVRTC 2BPP
+
+ Limitations:
+       Pre-generated mipmaps, such as PVR textures with mipmap levels embedded in file,
+       are only supported if all individual sprites are of _square_ size. 
+       To use mipmaps with non-square textures, instead call CCTexture2D#generateMipmap on the sheet texture itself
+       (and to save space, save the PVR sprite sheet without mip maps included).
+ */
+@interface CCTexturePVR : NSObject
+{
+       struct CCPVRMipmap      mipmaps_[CC_PVRMIPMAP_MAX];     // pointer to mipmap images
+       int             numberOfMipmaps_;                                       // number of mipmap used
+       
+       unsigned int    tableFormatIndex_;
+       uint32_t width_, height_;
+       GLuint  name_;
+       BOOL hasAlpha_;
+       
+       // cocos2d integration
+       BOOL retainName_;
+       CCTexture2DPixelFormat format_;
+}
+
+/** initializes a CCTexturePVR with a path */
+- (id)initWithContentsOfFile:(NSString *)path;
+/** initializes a CCTexturePVR with an URL */
+- (id)initWithContentsOfURL:(NSURL *)url;
+/** creates and initializes a CCTexturePVR with a path */
++ (id)pvrTextureWithContentsOfFile:(NSString *)path;
+/** creates and initializes a CCTexturePVR with an URL */
++ (id)pvrTextureWithContentsOfURL:(NSURL *)url;
+
+/** texture id name */
+@property (nonatomic,readonly) GLuint name;
+/** texture width */
+@property (nonatomic,readonly) uint32_t width;
+/** texture height */
+@property (nonatomic,readonly) uint32_t height;
+/** whether or not the texture has alpha */
+@property (nonatomic,readonly) BOOL hasAlpha;
+
+// cocos2d integration
+@property (nonatomic,readwrite) BOOL retainName;
+@property (nonatomic,readonly) CCTexture2DPixelFormat format;
+
+@end
+
+
diff --git a/libs/cocos2d/CCTexturePVR.m b/libs/cocos2d/CCTexturePVR.m
new file mode 100644 (file)
index 0000000..5d9b0d6
--- /dev/null
@@ -0,0 +1,411 @@
+/*
+
+File: PVRTexture.m
+Abstract: The PVRTexture class is responsible for loading .pvr files.
+
+Version: 1.0
+
+Disclaimer: IMPORTANT:  This Apple software is supplied to you by Apple Inc.
+("Apple") in consideration of your agreement to the following terms, and your
+use, installation, modification or redistribution of this Apple software
+constitutes acceptance of these terms.  If you do not agree with these terms,
+please do not use, install, modify or redistribute this Apple software.
+
+In consideration of your agreement to abide by the following terms, and subject
+to these terms, Apple grants you a personal, non-exclusive license, under
+Apple's copyrights in this original Apple software (the "Apple Software"), to
+use, reproduce, modify and redistribute the Apple Software, with or without
+modifications, in source and/or binary forms; provided that if you redistribute
+the Apple Software in its entirety and without modifications, you must retain
+this notice and the following text and disclaimers in all such redistributions
+of the Apple Software.
+Neither the name, trademarks, service marks or logos of Apple Inc. may be used
+to endorse or promote products derived from the Apple Software without specific
+prior written permission from Apple.  Except as expressly stated in this notice,
+no other rights or licenses, express or implied, are granted by Apple herein,
+including but not limited to any patent rights that may be infringed by your
+derivative works or by other works in which the Apple Software may be
+incorporated.
+
+The Apple Software is provided by Apple on an "AS IS" basis.  APPLE MAKES NO
+WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED
+WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN
+COMBINATION WITH YOUR PRODUCTS.
+
+IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR
+DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF
+CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF
+APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+Copyright (C) 2008 Apple Inc. All Rights Reserved.
+
+*/
+
+/*
+ * Extended PVR formats for cocos2d project ( http://www.cocos2d-iphone.org )
+ *     - RGBA8888
+ *     - BGRA8888
+ *  - RGBA4444
+ *  - RGBA5551
+ *  - RGB565
+ *  - A8
+ *  - I8
+ *  - AI88
+ */
+
+#import <Availability.h>
+
+#import <zlib.h>
+
+#import "CCTexturePVR.h"
+#import "ccMacros.h"
+#import "CCConfiguration.h"
+#import "Support/ccUtils.h"
+#import "Support/CCFileUtils.h"
+#import "Support/ZipUtils.h"
+#import "Support/OpenGL_Internal.h"
+
+#pragma mark -
+#pragma mark CCTexturePVR
+
+#define PVR_TEXTURE_FLAG_TYPE_MASK     0xff
+#define PVR_TEXTURE_FLAG_FLIPPED_MASK 0x10000
+
+static char gPVRTexIdentifier[4] = "PVR!";
+
+enum
+{
+       kPVRTextureFlagTypeRGBA_4444= 0x10,
+       kPVRTextureFlagTypeRGBA_5551,
+       kPVRTextureFlagTypeRGBA_8888,
+       kPVRTextureFlagTypeRGB_565,
+       kPVRTextureFlagTypeRGB_555,                             // unsupported
+       kPVRTextureFlagTypeRGB_888,                             // unsupported
+       kPVRTextureFlagTypeI_8,
+       kPVRTextureFlagTypeAI_88,
+       kPVRTextureFlagTypePVRTC_2,
+       kPVRTextureFlagTypePVRTC_4,     
+       kPVRTextureFlagTypeBGRA_8888,
+       kPVRTextureFlagTypeA_8,
+};
+
+static const uint32_t tableFormats[][7] = {
+       
+       // - PVR texture format
+       // - OpenGL internal format
+       // - OpenGL format
+       // - OpenGL type
+       // - bpp
+       // - compressed
+       // - Cocos2d texture format constant
+       { kPVRTextureFlagTypeRGBA_4444, GL_RGBA,        GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4,                             16, NO, kCCTexture2DPixelFormat_RGBA4444        },
+       { kPVRTextureFlagTypeRGBA_5551, GL_RGBA,        GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1,                             16, NO, kCCTexture2DPixelFormat_RGB5A1          },
+       { kPVRTextureFlagTypeRGBA_8888, GL_RGBA,        GL_RGBA, GL_UNSIGNED_BYTE,                                              32, NO, kCCTexture2DPixelFormat_RGBA8888        },
+       { kPVRTextureFlagTypeRGB_565,   GL_RGB,         GL_RGB,  GL_UNSIGNED_SHORT_5_6_5,                               16, NO, kCCTexture2DPixelFormat_RGB565          },
+       { kPVRTextureFlagTypeA_8,               GL_ALPHA,       GL_ALPHA,       GL_UNSIGNED_BYTE,                                       8,      NO, kCCTexture2DPixelFormat_A8                  },
+       { kPVRTextureFlagTypeI_8,               GL_LUMINANCE,   GL_LUMINANCE,   GL_UNSIGNED_BYTE,                       8,      NO, kCCTexture2DPixelFormat_I8                  },
+       { kPVRTextureFlagTypeAI_88,             GL_LUMINANCE_ALPHA,     GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE,   16,     NO, kCCTexture2DPixelFormat_AI88                },
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+       { kPVRTextureFlagTypePVRTC_2,   GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG, -1, -1,                            2,      YES, kCCTexture2DPixelFormat_PVRTC2             },
+       { kPVRTextureFlagTypePVRTC_4,   GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG, -1, -1,                            4,      YES, kCCTexture2DPixelFormat_PVRTC4             },
+#endif // iphone only
+       { kPVRTextureFlagTypeBGRA_8888, GL_RGBA,        GL_BGRA, GL_UNSIGNED_BYTE,                                              32,     NO, kCCTexture2DPixelFormat_RGBA8888    },
+};
+#define MAX_TABLE_ELEMENTS (sizeof(tableFormats) / sizeof(tableFormats[0]))
+
+enum {
+       kCCInternalPVRTextureFormat,
+       kCCInternalOpenGLInternalFormat,
+       kCCInternalOpenGLFormat,
+       kCCInternalOpenGLType,
+       kCCInternalBPP,
+       kCCInternalCompressedImage,
+       kCCInternalCCTexture2DPixelFormat,
+};
+
+typedef struct _PVRTexHeader
+{
+       uint32_t headerLength;
+       uint32_t height;
+       uint32_t width;
+       uint32_t numMipmaps;
+       uint32_t flags;
+       uint32_t dataLength;
+       uint32_t bpp;
+       uint32_t bitmaskRed;
+       uint32_t bitmaskGreen;
+       uint32_t bitmaskBlue;
+       uint32_t bitmaskAlpha;
+       uint32_t pvrTag;
+       uint32_t numSurfs;
+} PVRTexHeader;
+
+
+@implementation CCTexturePVR
+
+@synthesize name = name_;
+@synthesize width = width_;
+@synthesize height = height_;
+@synthesize hasAlpha = hasAlpha_;
+
+// cocos2d integration
+@synthesize retainName = retainName_;
+@synthesize format = format_;
+
+
+- (BOOL)unpackPVRData:(unsigned char*)data PVRLen:(NSUInteger)len
+{
+       BOOL success = FALSE;
+       PVRTexHeader *header = NULL;
+       uint32_t flags, pvrTag;
+       uint32_t dataLength = 0, dataOffset = 0, dataSize = 0;
+       uint32_t blockSize = 0, widthBlocks = 0, heightBlocks = 0;
+       uint32_t width = 0, height = 0, bpp = 4;
+       uint8_t *bytes = NULL;
+       uint32_t formatFlags;
+       
+       header = (PVRTexHeader *)data;
+       
+       pvrTag = CFSwapInt32LittleToHost(header->pvrTag);
+
+       if ((uint32_t)gPVRTexIdentifier[0] != ((pvrTag >>  0) & 0xff) ||
+               (uint32_t)gPVRTexIdentifier[1] != ((pvrTag >>  8) & 0xff) ||
+               (uint32_t)gPVRTexIdentifier[2] != ((pvrTag >> 16) & 0xff) ||
+               (uint32_t)gPVRTexIdentifier[3] != ((pvrTag >> 24) & 0xff))
+       {
+               return FALSE;
+       }
+       
+       flags = CFSwapInt32LittleToHost(header->flags);
+       formatFlags = flags & PVR_TEXTURE_FLAG_TYPE_MASK;
+       BOOL flipped = flags & PVR_TEXTURE_FLAG_FLIPPED_MASK;
+       if( flipped )
+               CCLOG(@"cocos2d: WARNING: Image is flipped. Regenerate it using PVRTexTool");
+
+       if( header->width != ccNextPOT(header->width) || header->height != ccNextPOT(header->height) ) {
+               CCLOG(@"cocos2d: WARNING: PVR NPOT textures are not supported. Regenerate it.");
+               return FALSE;
+       }
+       
+       for( tableFormatIndex_=0; tableFormatIndex_ < (unsigned int)MAX_TABLE_ELEMENTS ; tableFormatIndex_++) {
+               if( tableFormats[tableFormatIndex_][kCCInternalPVRTextureFormat] == formatFlags ) {
+                       
+                       numberOfMipmaps_ = 0;
+                                       
+                       width_ = width = CFSwapInt32LittleToHost(header->width);
+                       height_ = height = CFSwapInt32LittleToHost(header->height);
+                       
+                       if (CFSwapInt32LittleToHost(header->bitmaskAlpha))
+                               hasAlpha_ = TRUE;
+                       else
+                               hasAlpha_ = FALSE;
+                       
+                       dataLength = CFSwapInt32LittleToHost(header->dataLength);
+                       bytes = ((uint8_t *)data) + sizeof(PVRTexHeader);
+                       format_ = tableFormats[tableFormatIndex_][kCCInternalCCTexture2DPixelFormat];
+                       bpp = tableFormats[tableFormatIndex_][kCCInternalBPP];
+                       
+                       // Calculate the data size for each texture level and respect the minimum number of blocks
+                       while (dataOffset < dataLength)
+                       {
+                               switch (formatFlags) {
+                                       case kPVRTextureFlagTypePVRTC_2:
+                                               blockSize = 8 * 4; // Pixel by pixel block size for 2bpp
+                                               widthBlocks = width / 8;
+                                               heightBlocks = height / 4;
+                                               break;
+                                       case kPVRTextureFlagTypePVRTC_4:
+                                               blockSize = 4 * 4; // Pixel by pixel block size for 4bpp
+                                               widthBlocks = width / 4;
+                                               heightBlocks = height / 4;
+                                               break;
+                                       case kPVRTextureFlagTypeBGRA_8888:
+                                               if( ! [[CCConfiguration sharedConfiguration] supportsBGRA8888] ) {
+                                                       CCLOG(@"cocos2d: TexturePVR. BGRA8888 not supported on this device");
+                                                       return FALSE;
+                                               }
+                                       default:
+                                               blockSize = 1;
+                                               widthBlocks = width;
+                                               heightBlocks = height;
+                                               break;
+                               }
+                               
+                               // Clamp to minimum number of blocks
+                               if (widthBlocks < 2)
+                                       widthBlocks = 2;
+                               if (heightBlocks < 2)
+                                       heightBlocks = 2;
+
+                               dataSize = widthBlocks * heightBlocks * ((blockSize  * bpp) / 8);
+                               float packetLength = (dataLength-dataOffset);
+                               packetLength = packetLength > dataSize ? dataSize : packetLength;
+                               
+                               mipmaps_[numberOfMipmaps_].address = bytes+dataOffset;
+                               mipmaps_[numberOfMipmaps_].len = packetLength;
+                               numberOfMipmaps_++;
+                               
+                               NSAssert( numberOfMipmaps_ < CC_PVRMIPMAP_MAX, @"TexturePVR: Maximum number of mimpaps reached. Increate the CC_PVRMIPMAP_MAX value");
+                               
+                               dataOffset += packetLength;
+                               
+                               width = MAX(width >> 1, 1);
+                               height = MAX(height >> 1, 1);
+                       }
+                                         
+                       success = TRUE;
+                       break;
+               }
+       }
+       
+       if( ! success )
+               CCLOG(@"cocos2d: WARNING: Unsupported PVR Pixel Format: 0x%2x. Re-encode it with a OpenGL pixel format variant", formatFlags);
+       
+       return success;
+}
+
+
+- (BOOL)createGLTexture
+{
+       GLsizei width = width_;
+       GLsizei height = height_;
+       GLenum err;
+       
+       if (numberOfMipmaps_ > 0)
+       {
+               if (name_ != 0)
+                       glDeleteTextures(1, &name_);
+               
+               glGenTextures(1, &name_);
+               glBindTexture(GL_TEXTURE_2D, name_);
+       }
+
+       CHECK_GL_ERROR(); // clean possible GL error
+
+       // Generate textures with mipmaps
+       for (GLint i=0; i < numberOfMipmaps_; i++)
+       {
+               GLenum internalFormat = tableFormats[tableFormatIndex_][kCCInternalOpenGLInternalFormat];
+               GLenum format = tableFormats[tableFormatIndex_][kCCInternalOpenGLFormat];
+               GLenum type = tableFormats[tableFormatIndex_][kCCInternalOpenGLType];
+               BOOL compressed = tableFormats[tableFormatIndex_][kCCInternalCompressedImage];
+               
+               if( compressed && ! [[CCConfiguration sharedConfiguration] supportsPVRTC] ) {
+                       CCLOG(@"cocos2d: WARNING: PVRTC images are not supported");
+                       return FALSE;
+               }                       
+               
+               unsigned char *data = mipmaps_[i].address;
+               unsigned int datalen = mipmaps_[i].len;
+               
+               if( compressed)
+                       glCompressedTexImage2D(GL_TEXTURE_2D, i, internalFormat, width, height, 0, datalen, data);
+               else 
+                       glTexImage2D(GL_TEXTURE_2D, i, internalFormat, width, height, 0, format, type, data);
+
+               if( i > 0 && (width != height || ccNextPOT(width) != width ) )
+                       CCLOG(@"cocos2d: TexturePVR. WARNING. Mipmap level %u is not squared. Texture won't render correctly. width=%u != height=%u", i, width, height);
+               
+               err = glGetError();
+               if (err != GL_NO_ERROR)
+               {
+                       CCLOG(@"cocos2d: TexturePVR: Error uploading compressed texture level: %u . glError: 0x%04X", i, err);
+                       return FALSE;
+               }
+               
+               width = MAX(width >> 1, 1);
+               height = MAX(height >> 1, 1);
+       }
+               
+       return TRUE;
+}
+
+
+- (id)initWithContentsOfFile:(NSString *)path
+{
+       if((self = [super init]))  
+       { 
+               unsigned char *pvrdata = NULL;
+               NSInteger pvrlen = 0;
+               NSString *lowerCase = [path lowercaseString];       
+               
+        if ( [lowerCase hasSuffix:@".ccz"]) 
+                       pvrlen = ccInflateCCZFile( [path UTF8String], &pvrdata );
+                       
+               else if( [lowerCase hasSuffix:@".gz"] )
+                       pvrlen = ccInflateGZipFile( [path UTF8String], &pvrdata );
+               
+               else
+                       pvrlen = ccLoadFileIntoMemory( [path UTF8String], &pvrdata );
+               
+               if( pvrlen < 0 ) {
+                       [self release];
+                       return nil;
+               }                       
+               
+
+        numberOfMipmaps_ = 0;
+        
+               name_ = 0;
+               width_ = height_ = 0;
+               tableFormatIndex_ = -1;
+               hasAlpha_ = FALSE;
+
+               retainName_ = NO; // cocos2d integration
+               
+               if( ! [self unpackPVRData:pvrdata PVRLen:pvrlen] || ![self createGLTexture]  ) {
+                       free(pvrdata);
+                       [self release];
+                       return nil;
+               }
+               
+               free(pvrdata);
+       }
+
+       return self;
+}
+
+- (id)initWithContentsOfURL:(NSURL *)url
+{
+       if (![url isFileURL])
+       {
+               CCLOG(@"cocos2d: CCPVRTexture: Only files are supported");
+               [self release];
+               return nil;
+       }
+       
+       return [self initWithContentsOfFile:[url path]];
+}
+
+
++ (id)pvrTextureWithContentsOfFile:(NSString *)path
+{
+       return [[[self alloc] initWithContentsOfFile:path] autorelease];
+}
+
+
++ (id)pvrTextureWithContentsOfURL:(NSURL *)url
+{
+       if (![url isFileURL])
+               return nil;
+       
+       return [CCTexturePVR pvrTextureWithContentsOfFile:[url path]];
+}
+
+
+- (void)dealloc
+{
+       CCLOGINFO( @"cocos2d: deallocing %@", self);
+       
+       if (name_ != 0 && ! retainName_ )
+               glDeleteTextures(1, &name_);
+       
+       [super dealloc];
+}
+
+@end
+
diff --git a/libs/cocos2d/CCTileMapAtlas.h b/libs/cocos2d/CCTileMapAtlas.h
new file mode 100644 (file)
index 0000000..102ae46
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2008-2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+#import "CCTextureAtlas.h"
+#import "CCAtlasNode.h"
+#import "Support/TGAlib.h"
+
+/** CCTileMapAtlas is a subclass of CCAtlasNode.
+ It knows how to render a map based of tiles.
+ The tiles must be in a .PNG format while the map must be a .TGA file.
+ For more information regarding the format, please see this post:
+ http://www.cocos2d-iphone.org/archives/27
+ All features from CCAtlasNode are valid in CCTileMapAtlas
+ IMPORTANT:
+ This class is deprecated. It is maintained for compatibility reasons only.
+ You SHOULD not use this class.
+ Instead, use the newer TMX file format: CCTMXTiledMap
+ */
+@interface CCTileMapAtlas : CCAtlasNode
+{
+       
+       /// info about the map file
+       tImageTGA               *tgaInfo;
+       
+       /// x,y to altas dicctionary
+       NSMutableDictionary     *posToAtlasIndex;
+       
+       /// numbers of tiles to render
+       int                             itemsToRender;
+}
+
+/** TileMap info */
+@property (nonatomic,readonly) tImageTGA *tgaInfo;
+
+/** creates a CCTileMap with a tile file (atlas) with a map file and the width and height of each tile in points.
+ The tile file will be loaded using the TextureMgr.
+ */
++(id) tileMapAtlasWithTileFile:(NSString*)tile mapFile:(NSString*)map tileWidth:(int)w tileHeight:(int)h;
+
+/** initializes a CCTileMap with a tile file (atlas) with a map file and the width and height of each tile in points.
+ The file will be loaded using the TextureMgr.
+ */
+-(id) initWithTileFile:(NSString*)tile mapFile:(NSString*)map tileWidth:(int)w tileHeight:(int)h;
+
+/** returns a tile from position x,y.
+ For the moment only channel R is used
+ */
+-(ccColor3B) tileAt: (ccGridSize) position;
+
+/** sets a tile at position x,y.
+ For the moment only channel R is used
+ */
+-(void) setTile:(ccColor3B)tile at:(ccGridSize)position;
+/** dealloc the map from memory */
+-(void) releaseMap;
+@end
diff --git a/libs/cocos2d/CCTileMapAtlas.m b/libs/cocos2d/CCTileMapAtlas.m
new file mode 100644 (file)
index 0000000..aef6fe0
--- /dev/null
@@ -0,0 +1,234 @@
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2008-2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+#import "ccConfig.h"
+#import "CCTileMapAtlas.h"
+#import "ccMacros.h"
+#import "Support/CCFileUtils.h"
+
+@interface CCTileMapAtlas (Private)
+-(void) loadTGAfile:(NSString*)file;
+-(void) calculateItemsToRender;
+-(void) updateAtlasValueAt:(ccGridSize)pos withValue:(ccColor3B)value withIndex:(NSUInteger)idx;
+@end
+
+
+@implementation CCTileMapAtlas
+
+@synthesize tgaInfo;
+
+#pragma mark CCTileMapAtlas - Creation & Init
++(id) tileMapAtlasWithTileFile:(NSString*)tile mapFile:(NSString*)map tileWidth:(int)w tileHeight:(int)h
+{
+       return [[[self alloc] initWithTileFile:tile mapFile:map tileWidth:w tileHeight:h] autorelease];
+}
+
+
+-(id) initWithTileFile:(NSString*)tile mapFile:(NSString*)map tileWidth:(int)w tileHeight:(int)h
+{
+       [self loadTGAfile: map];
+       [self calculateItemsToRender];
+
+       if( (self=[super initWithTileFile:tile tileWidth:w tileHeight:h itemsToRender: itemsToRender]) ) {
+
+               posToAtlasIndex = [[NSMutableDictionary dictionaryWithCapacity:itemsToRender] retain];
+
+               [self updateAtlasValues];
+               
+               [self setContentSize: CGSizeMake(tgaInfo->width*itemWidth_, tgaInfo->height*itemHeight_)];
+       }
+
+       return self;
+}
+
+-(void) dealloc
+{
+       if( tgaInfo )
+               tgaDestroy(tgaInfo);
+
+       [posToAtlasIndex release];
+
+       [super dealloc];
+}
+
+-(void) releaseMap
+{
+       if( tgaInfo )
+               tgaDestroy(tgaInfo);
+       
+       tgaInfo = nil;
+
+       [posToAtlasIndex release];
+       posToAtlasIndex = nil;
+}
+
+-(void) calculateItemsToRender
+{
+       NSAssert( tgaInfo != nil, @"tgaInfo must be non-nil");
+
+       itemsToRender = 0;
+       for(int x = 0;x < tgaInfo->width; x++ ) {
+               for(int y = 0; y < tgaInfo->height; y++ ) {
+                       ccColor3B *ptr = (ccColor3B*) tgaInfo->imageData;
+                       ccColor3B value = ptr[x + y * tgaInfo->width];
+                       if( value.r )
+                               itemsToRender++;
+               }
+       }
+}
+
+-(void) loadTGAfile:(NSString*)file
+{
+       NSAssert( file != nil, @"file must be non-nil");
+
+       NSString *path = [CCFileUtils fullPathFromRelativePath:file ];
+
+//     //Find the path of the file
+//     NSBundle *mainBndl = [CCDirector sharedDirector].loadingBundle;
+//     NSString *resourcePath = [mainBndl resourcePath];
+//     NSString * path = [resourcePath stringByAppendingPathComponent:file];
+       
+       tgaInfo = tgaLoad( [path UTF8String] );
+#if 1
+       if( tgaInfo->status != TGA_OK )
+               [NSException raise:@"TileMapAtlasLoadTGA" format:@"TileMapAtas cannot load TGA file"];
+       
+#endif
+}
+
+#pragma mark CCTileMapAtlas - Atlas generation / updates
+
+-(void) setTile:(ccColor3B) tile at:(ccGridSize) pos
+{
+       NSAssert( tgaInfo != nil, @"tgaInfo must not be nil");
+       NSAssert( posToAtlasIndex != nil, @"posToAtlasIndex must not be nil");
+       NSAssert( pos.x < tgaInfo->width, @"Invalid position.x");
+       NSAssert( pos.y < tgaInfo->height, @"Invalid position.x");
+       NSAssert( tile.r != 0, @"R component must be non 0");
+       
+       ccColor3B *ptr = (ccColor3B*) tgaInfo->imageData;
+       ccColor3B value = ptr[pos.x + pos.y * tgaInfo->width];
+       if( value.r == 0 )
+               CCLOG(@"cocos2d: Value.r must be non 0.");
+       else {
+               ptr[pos.x + pos.y * tgaInfo->width] = tile;
+               
+               // XXX: this method consumes a lot of memory
+               // XXX: a tree of something like that shall be impolemented
+               NSNumber *num = [posToAtlasIndex objectForKey: [NSString stringWithFormat:@"%d,%d", pos.x, pos.y]];
+               [self updateAtlasValueAt:pos withValue:tile withIndex: [num integerValue]];
+       }       
+}
+
+-(ccColor3B) tileAt:(ccGridSize) pos
+{
+       NSAssert( tgaInfo != nil, @"tgaInfo must not be nil");
+       NSAssert( pos.x < tgaInfo->width, @"Invalid position.x");
+       NSAssert( pos.y < tgaInfo->height, @"Invalid position.y");
+       
+       ccColor3B *ptr = (ccColor3B*) tgaInfo->imageData;
+       ccColor3B value = ptr[pos.x + pos.y * tgaInfo->width];
+       
+       return value;   
+}
+
+-(void) updateAtlasValueAt:(ccGridSize)pos withValue:(ccColor3B)value withIndex:(NSUInteger)idx
+{
+       ccV3F_C4B_T2F_Quad quad;
+
+       NSInteger x = pos.x;
+       NSInteger y = pos.y;
+       float row = (value.r % itemsPerRow_);
+       float col = (value.r / itemsPerRow_);
+       
+       float textureWide = [[textureAtlas_ texture] pixelsWide];
+       float textureHigh = [[textureAtlas_ texture] pixelsHigh];
+
+#if CC_FIX_ARTIFACTS_BY_STRECHING_TEXEL
+       float left              = (2*row*itemWidth_+1)/(2*textureWide);
+       float right             = left+(itemWidth_*2-2)/(2*textureWide);
+       float top               = (2*col*itemHeight_+1)/(2*textureHigh);
+       float bottom    = top+(itemHeight_*2-2)/(2*textureHigh);
+#else
+       float left              = (row*itemWidth_)/textureWide;
+       float right             = left+itemWidth_/textureWide;
+       float top               = (col*itemHeight_)/textureHigh;
+       float bottom    = top+itemHeight_/textureHigh;
+#endif
+       
+
+       quad.tl.texCoords.u = left;
+       quad.tl.texCoords.v = top;
+       quad.tr.texCoords.u = right;
+       quad.tr.texCoords.v = top;
+       quad.bl.texCoords.u = left;
+       quad.bl.texCoords.v = bottom;
+       quad.br.texCoords.u = right;
+       quad.br.texCoords.v = bottom;
+
+       quad.bl.vertices.x = (int) (x * itemWidth_);
+       quad.bl.vertices.y = (int) (y * itemHeight_);
+       quad.bl.vertices.z = 0.0f;
+       quad.br.vertices.x = (int)(x * itemWidth_ + itemWidth_);
+       quad.br.vertices.y = (int)(y * itemHeight_);
+       quad.br.vertices.z = 0.0f;
+       quad.tl.vertices.x = (int)(x * itemWidth_);
+       quad.tl.vertices.y = (int)(y * itemHeight_ + itemHeight_);
+       quad.tl.vertices.z = 0.0f;
+       quad.tr.vertices.x = (int)(x * itemWidth_ + itemWidth_);
+       quad.tr.vertices.y = (int)(y * itemHeight_ + itemHeight_);
+       quad.tr.vertices.z = 0.0f;
+       
+       [textureAtlas_ updateQuad:&quad atIndex:idx];
+}
+
+-(void) updateAtlasValues
+{
+       NSAssert( tgaInfo != nil, @"tgaInfo must be non-nil");
+
+       
+       int total = 0;
+
+       for(int x = 0;x < tgaInfo->width; x++ ) {
+               for(int y = 0; y < tgaInfo->height; y++ ) {
+                       if( total < itemsToRender ) {
+                               ccColor3B *ptr = (ccColor3B*) tgaInfo->imageData;
+                               ccColor3B value = ptr[x + y * tgaInfo->width];
+                               
+                               if( value.r != 0 ) {
+                                       [self updateAtlasValueAt:ccg(x,y) withValue:value withIndex:total];
+                                       
+                                       NSString *key = [NSString stringWithFormat:@"%d,%d", x,y];
+                                       NSNumber *num = [NSNumber numberWithInt:total];
+                                       [posToAtlasIndex setObject:num forKey:key];
+
+                                       total++;
+                               }
+                       }
+               }
+       }
+}
+@end
diff --git a/libs/cocos2d/CCTransition.h b/libs/cocos2d/CCTransition.h
new file mode 100644 (file)
index 0000000..e37d3e8
--- /dev/null
@@ -0,0 +1,296 @@
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2008-2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+
+#import "CCScene.h"
+@class CCActionInterval;
+@class CCNode;
+
+/** CCTransitionEaseScene can ease the actions of the scene protocol.
+ @since v0.8.2
+ */
+@protocol CCTransitionEaseScene <NSObject>
+/** returns the Ease action that will be performed on a linear action.
+ @since v0.8.2
+ */
+-(CCActionInterval*) easeActionWithAction:(CCActionInterval*)action;
+@end
+
+/** Orientation Type used by some transitions
+ */
+typedef enum {
+       /// An horizontal orientation where the Left is nearer
+       kOrientationLeftOver = 0,
+       /// An horizontal orientation where the Right is nearer
+       kOrientationRightOver = 1,
+       /// A vertical orientation where the Up is nearer
+       kOrientationUpOver = 0,
+       /// A vertical orientation where the Bottom is nearer
+       kOrientationDownOver = 1,
+} tOrientation;
+
+/** Base class for CCTransition scenes
+ */
+@interface CCTransitionScene : CCScene
+{
+       CCScene *inScene_;
+       CCScene *outScene_;
+       ccTime  duration_;
+       BOOL    inSceneOnTop_;
+       BOOL    sendCleanupToScene_;
+}
+/** creates a base transition with duration and incoming scene */
++(id) transitionWithDuration:(ccTime) t scene:(CCScene*)s;
+/** initializes a transition with duration and incoming scene */
+-(id) initWithDuration:(ccTime) t scene:(CCScene*)s;
+/** called after the transition finishes */
+-(void) finish;
+/** used by some transitions to hide the outter scene */
+-(void) hideOutShowIn;
+@end
+
+/** A CCTransition that supports orientation like.
+ * Possible orientation: LeftOver, RightOver, UpOver, DownOver
+ */
+@interface CCTransitionSceneOriented : CCTransitionScene
+{
+       tOrientation orientation;
+}
+/** creates a base transition with duration and incoming scene */
++(id) transitionWithDuration:(ccTime) t scene:(CCScene*)s orientation:(tOrientation)o;
+/** initializes a transition with duration and incoming scene */
+-(id) initWithDuration:(ccTime) t scene:(CCScene*)s orientation:(tOrientation)o;
+@end
+
+
+/** CCTransitionRotoZoom:
+ Rotate and zoom out the outgoing scene, and then rotate and zoom in the incoming 
+ */
+@interface CCTransitionRotoZoom : CCTransitionScene
+{}
+@end
+
+/** CCTransitionJumpZoom:
+ Zoom out and jump the outgoing scene, and then jump and zoom in the incoming 
+*/
+@interface CCTransitionJumpZoom : CCTransitionScene
+{}
+@end
+
+/** CCTransitionMoveInL:
+ Move in from to the left the incoming scene.
+*/
+@interface CCTransitionMoveInL : CCTransitionScene <CCTransitionEaseScene>
+{}
+/** initializes the scenes */
+-(void) initScenes;
+/** returns the action that will be performed */
+-(CCActionInterval*) action;
+@end
+
+/** CCTransitionMoveInR:
+ Move in from to the right the incoming scene.
+ */
+@interface CCTransitionMoveInR : CCTransitionMoveInL
+{}
+@end
+
+/** CCTransitionMoveInT:
+ Move in from to the top the incoming scene.
+ */
+@interface CCTransitionMoveInT : CCTransitionMoveInL 
+{}
+@end
+
+/** CCTransitionMoveInB:
+ Move in from to the bottom the incoming scene.
+ */
+@interface CCTransitionMoveInB : CCTransitionMoveInL
+{}
+@end
+
+/** CCTransitionSlideInL:
+ Slide in the incoming scene from the left border.
+ */
+@interface CCTransitionSlideInL : CCTransitionScene <CCTransitionEaseScene>
+{}
+/** initializes the scenes */
+-(void) initScenes;
+/** returns the action that will be performed by the incomming and outgoing scene */
+-(CCActionInterval*) action;
+@end
+
+/** CCTransitionSlideInR:
+ Slide in the incoming scene from the right border.
+ */
+@interface CCTransitionSlideInR : CCTransitionSlideInL 
+{}
+@end
+
+/** CCTransitionSlideInB:
+ Slide in the incoming scene from the bottom border.
+ */
+@interface CCTransitionSlideInB : CCTransitionSlideInL
+{}
+@end
+
+/** CCTransitionSlideInT:
+ Slide in the incoming scene from the top border.
+ */
+@interface CCTransitionSlideInT : CCTransitionSlideInL
+{}
+@end
+
+/**
+ Shrink the outgoing scene while grow the incoming scene
+ */
+@interface CCTransitionShrinkGrow : CCTransitionScene <CCTransitionEaseScene>
+{}
+@end
+
+/** CCTransitionFlipX:
+ Flips the screen horizontally.
+ The front face is the outgoing scene and the back face is the incoming scene.
+ */
+@interface CCTransitionFlipX : CCTransitionSceneOriented
+{}
+@end
+
+/** CCTransitionFlipY:
+ Flips the screen vertically.
+ The front face is the outgoing scene and the back face is the incoming scene.
+ */
+@interface CCTransitionFlipY : CCTransitionSceneOriented
+{}
+@end
+
+/** CCTransitionFlipAngular:
+ Flips the screen half horizontally and half vertically.
+ The front face is the outgoing scene and the back face is the incoming scene.
+ */
+@interface CCTransitionFlipAngular : CCTransitionSceneOriented
+{}
+@end
+
+/** CCTransitionZoomFlipX:
+ Flips the screen horizontally doing a zoom out/in
+ The front face is the outgoing scene and the back face is the incoming scene.
+ */
+@interface CCTransitionZoomFlipX : CCTransitionSceneOriented
+{}
+@end
+
+/** CCTransitionZoomFlipY:
+ Flips the screen vertically doing a little zooming out/in
+ The front face is the outgoing scene and the back face is the incoming scene.
+ */
+@interface CCTransitionZoomFlipY : CCTransitionSceneOriented
+{}
+@end
+
+/** CCTransitionZoomFlipAngular:
+ Flips the screen half horizontally and half vertically doing a little zooming out/in.
+ The front face is the outgoing scene and the back face is the incoming scene.
+ */
+@interface CCTransitionZoomFlipAngular : CCTransitionSceneOriented
+{}
+@end
+
+/** CCTransitionFade:
+ Fade out the outgoing scene and then fade in the incoming scene.'''
+ */
+@interface CCTransitionFade : CCTransitionScene
+{
+       ccColor4B       color;
+}
+/** creates the transition with a duration and with an RGB color
+ * Example: [FadeTransition transitionWithDuration:2 scene:s withColor:ccc3(255,0,0)]; // red color
+ */
++(id) transitionWithDuration:(ccTime)duration scene:(CCScene*)scene withColor:(ccColor3B)color;
+/** initializes the transition with a duration and with an RGB color */
+-(id) initWithDuration:(ccTime)duration scene:(CCScene*)scene withColor:(ccColor3B)color;
+@end
+
+
+/**
+ CCTransitionCrossFade:
+ Cross fades two scenes using the CCRenderTexture object.
+ */
+@class CCRenderTexture;
+@interface CCTransitionCrossFade : CCTransitionScene
+{}
+@end
+
+/** CCTransitionTurnOffTiles:
+ Turn off the tiles of the outgoing scene in random order
+ */
+@interface CCTransitionTurnOffTiles : CCTransitionScene <CCTransitionEaseScene>
+{}
+@end
+
+/** CCTransitionSplitCols:
+ The odd columns goes upwards while the even columns goes downwards.
+ */
+@interface CCTransitionSplitCols : CCTransitionScene <CCTransitionEaseScene>
+{}
+-(CCActionInterval*) action;
+@end
+
+/** CCTransitionSplitRows:
+ The odd rows goes to the left while the even rows goes to the right.
+ */
+@interface CCTransitionSplitRows : CCTransitionSplitCols
+{}
+@end
+
+/** CCTransitionFadeTR:
+ Fade the tiles of the outgoing scene from the left-bottom corner the to top-right corner.
+ */
+@interface CCTransitionFadeTR : CCTransitionScene <CCTransitionEaseScene>
+{}
+-(CCActionInterval*) actionWithSize:(ccGridSize) vector;
+@end
+
+/** CCTransitionFadeBL:
+ Fade the tiles of the outgoing scene from the top-right corner to the bottom-left corner.
+ */
+@interface CCTransitionFadeBL : CCTransitionFadeTR
+{}
+@end
+
+/** CCTransitionFadeUp:
+ * Fade the tiles of the outgoing scene from the bottom to the top.
+ */
+@interface CCTransitionFadeUp : CCTransitionFadeTR
+{}
+@end
+
+/** CCTransitionFadeDown:
+ * Fade the tiles of the outgoing scene from the top to the bottom.
+ */
+@interface CCTransitionFadeDown : CCTransitionFadeTR
+{}
+@end
diff --git a/libs/cocos2d/CCTransition.m b/libs/cocos2d/CCTransition.m
new file mode 100644 (file)
index 0000000..dc1487f
--- /dev/null
@@ -0,0 +1,1058 @@
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2008-2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+
+
+#import "CCTransition.h"
+#import "CCNode.h"
+#import "CCDirector.h"
+#import "CCActionInterval.h"
+#import "CCActionInstant.h"
+#import "CCActionCamera.h"
+#import "CCLayer.h"
+#import "CCCamera.h"
+#import "CCActionTiledGrid.h"
+#import "CCActionEase.h"
+#import "CCRenderTexture.h"
+#import "Support/CGPointExtension.h"
+
+#import <Availability.h>
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+#import "Platforms/iOS/CCTouchDispatcher.h"
+#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED)
+#import "Platforms/Mac/CCEventDispatcher.h"
+#endif
+
+enum {
+       kSceneFade = 0xFADEFADE,
+};
+
+@interface CCTransitionScene (Private)
+-(void) sceneOrder;
+- (void)setNewScene:(ccTime)dt;
+@end
+
+@implementation CCTransitionScene
++(id) transitionWithDuration:(ccTime) t scene:(CCScene*)s
+{
+       return [[[self alloc] initWithDuration:t scene:s] autorelease];
+}
+
+-(id) initWithDuration:(ccTime) t scene:(CCScene*)s
+{
+       NSAssert( s != nil, @"Argument scene must be non-nil");
+       
+       if( (self=[super init]) ) {
+       
+               duration_ = t;
+               
+               // retain
+               inScene_ = [s retain];
+               outScene_ = [[CCDirector sharedDirector] runningScene];
+               [outScene_ retain];
+               
+               NSAssert( inScene_ != outScene_, @"Incoming scene must be different from the outgoing scene" );
+
+               // disable events while transitions
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+               [[CCTouchDispatcher sharedDispatcher] setDispatchEvents: NO];
+#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED)
+               [[CCEventDispatcher sharedDispatcher] setDispatchEvents: NO];
+#endif
+
+               [self sceneOrder];
+       }
+       return self;
+}
+-(void) sceneOrder
+{
+       inSceneOnTop_ = YES;
+}
+
+-(void) draw
+{
+       if( inSceneOnTop_ ) {
+               [outScene_ visit];
+               [inScene_ visit];
+       } else {
+               [inScene_ visit];
+               [outScene_ visit];
+       }
+}
+
+-(void) finish
+{
+       /* clean up */  
+       [inScene_ setVisible:YES];
+       [inScene_ setPosition:ccp(0,0)];
+       [inScene_ setScale:1.0f];
+       [inScene_ setRotation:0.0f];
+       [inScene_.camera restore];
+       
+       [outScene_ setVisible:NO];
+       [outScene_ setPosition:ccp(0,0)];
+       [outScene_ setScale:1.0f];
+       [outScene_ setRotation:0.0f];
+       [outScene_.camera restore];
+       
+       [self schedule:@selector(setNewScene:) interval:0];
+}
+
+-(void) setNewScene: (ccTime) dt
+{      
+       [self unschedule:_cmd];
+       
+       CCDirector *director = [CCDirector sharedDirector];
+       
+       // Before replacing, save the "send cleanup to scene"
+       sendCleanupToScene_ = [director sendCleanupToScene];
+       
+       [director replaceScene: inScene_];
+
+       // enable events while transitions
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+       [[CCTouchDispatcher sharedDispatcher] setDispatchEvents: YES];
+#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED)
+       [[CCEventDispatcher sharedDispatcher] setDispatchEvents: YES];
+#endif
+       
+       // issue #267
+       [outScene_ setVisible:YES];     
+}
+
+-(void) hideOutShowIn
+{
+       [inScene_ setVisible:YES];
+       [outScene_ setVisible:NO];
+}
+
+// custom onEnter
+-(void) onEnter
+{
+       [super onEnter];
+       [inScene_ onEnter];
+       // outScene_ should not receive the onEnter callback
+}
+
+// custom onExit
+-(void) onExit
+{
+       [super onExit];
+       [outScene_ onExit];
+
+       // inScene_ should not receive the onExit callback
+       // only the onEnterTransitionDidFinish
+       [inScene_ onEnterTransitionDidFinish];
+}
+
+// custom cleanup
+-(void) cleanup
+{
+       [super cleanup];
+       
+       if( sendCleanupToScene_ )
+          [outScene_ cleanup];
+}
+
+-(void) dealloc
+{
+       [inScene_ release];
+       [outScene_ release];
+       [super dealloc];
+}
+@end
+
+//
+// Oriented Transition
+//
+@implementation CCTransitionSceneOriented
++(id) transitionWithDuration:(ccTime) t scene:(CCScene*)s orientation:(tOrientation)o
+{
+       return [[[self alloc] initWithDuration:t scene:s orientation:o] autorelease];
+}
+
+-(id) initWithDuration:(ccTime) t scene:(CCScene*)s orientation:(tOrientation)o
+{
+       if( (self=[super initWithDuration:t scene:s]) )
+               orientation = o;
+       return self;
+}
+@end
+
+
+//
+// RotoZoom
+//
+@implementation CCTransitionRotoZoom
+-(void) onEnter
+{
+       [super onEnter];
+       
+       [inScene_ setScale:0.001f];
+       [outScene_ setScale:1.0f];
+       
+       [inScene_ setAnchorPoint:ccp(0.5f, 0.5f)];
+       [outScene_ setAnchorPoint:ccp(0.5f, 0.5f)];
+       
+       CCActionInterval *rotozoom = [CCSequence actions: [CCSpawn actions:
+                                                                  [CCScaleBy actionWithDuration:duration_/2 scale:0.001f],
+                                                                  [CCRotateBy actionWithDuration:duration_/2 angle:360 *2],
+                                                                  nil],
+                                                               [CCDelayTime actionWithDuration:duration_/2],
+                                                       nil];
+       
+       
+       [outScene_ runAction: rotozoom];
+       [inScene_ runAction: [CCSequence actions:
+                                       [rotozoom reverse],
+                                       [CCCallFunc actionWithTarget:self selector:@selector(finish)],
+                                 nil]];
+}
+@end
+
+//
+// JumpZoom
+//
+@implementation CCTransitionJumpZoom
+-(void) onEnter
+{
+       [super onEnter];
+       CGSize s = [[CCDirector sharedDirector] winSize];
+       
+       [inScene_ setScale:0.5f];
+       [inScene_ setPosition:ccp( s.width,0 )];
+
+       [inScene_ setAnchorPoint:ccp(0.5f, 0.5f)];
+       [outScene_ setAnchorPoint:ccp(0.5f, 0.5f)];
+
+       CCActionInterval *jump = [CCJumpBy actionWithDuration:duration_/4 position:ccp(-s.width,0) height:s.width/4 jumps:2];
+       CCActionInterval *scaleIn = [CCScaleTo actionWithDuration:duration_/4 scale:1.0f];
+       CCActionInterval *scaleOut = [CCScaleTo actionWithDuration:duration_/4 scale:0.5f];
+       
+       CCActionInterval *jumpZoomOut = [CCSequence actions: scaleOut, jump, nil];
+       CCActionInterval *jumpZoomIn = [CCSequence actions: jump, scaleIn, nil];
+       
+       CCActionInterval *delay = [CCDelayTime actionWithDuration:duration_/2];
+       
+       [outScene_ runAction: jumpZoomOut];
+       [inScene_ runAction: [CCSequence actions: delay,
+                                                               jumpZoomIn,
+                                                               [CCCallFunc actionWithTarget:self selector:@selector(finish)],
+                                                               nil] ];
+}
+@end
+
+//
+// MoveInL
+//
+@implementation CCTransitionMoveInL
+-(void) onEnter
+{
+       [super onEnter];
+       
+       [self initScenes];
+       
+       CCActionInterval *a = [self action];
+
+       [inScene_ runAction: [CCSequence actions:
+                                                [self easeActionWithAction:a],
+                                                [CCCallFunc actionWithTarget:self selector:@selector(finish)],
+                                                nil]
+       ];
+                       
+}
+-(CCActionInterval*) action
+{
+       return [CCMoveTo actionWithDuration:duration_ position:ccp(0,0)];
+}
+
+-(CCActionInterval*) easeActionWithAction:(CCActionInterval*)action
+{
+       return [CCEaseOut actionWithAction:action rate:2.0f];
+//     return [EaseElasticOut actionWithAction:action period:0.4f];
+}
+
+-(void) initScenes
+{
+       CGSize s = [[CCDirector sharedDirector] winSize];
+       [inScene_ setPosition: ccp( -s.width,0) ];
+}
+@end
+
+//
+// MoveInR
+//
+@implementation CCTransitionMoveInR
+-(void) initScenes
+{
+       CGSize s = [[CCDirector sharedDirector] winSize];
+       [inScene_ setPosition: ccp( s.width,0) ];
+}
+@end
+
+//
+// MoveInT
+//
+@implementation CCTransitionMoveInT
+-(void) initScenes
+{
+       CGSize s = [[CCDirector sharedDirector] winSize];
+       [inScene_ setPosition: ccp( 0, s.height) ];
+}
+@end
+
+//
+// MoveInB
+//
+@implementation CCTransitionMoveInB
+-(void) initScenes
+{
+       CGSize s = [[CCDirector sharedDirector] winSize];
+       [inScene_ setPosition: ccp( 0, -s.height) ];
+}
+@end
+
+//
+// SlideInL
+//
+
+// The adjust factor is needed to prevent issue #442
+// One solution is to use DONT_RENDER_IN_SUBPIXELS images, but NO
+// The other issue is that in some transitions (and I don't know why)
+// the order should be reversed (In in top of Out or vice-versa).
+#define ADJUST_FACTOR 0.5f
+@implementation CCTransitionSlideInL
+-(void) onEnter
+{
+       [super onEnter];
+
+       [self initScenes];
+       
+       CCActionInterval *in = [self action];
+       CCActionInterval *out = [self action];
+
+       id inAction = [self easeActionWithAction:in];
+       id outAction = [CCSequence actions:
+                                       [self easeActionWithAction:out],
+                                       [CCCallFunc actionWithTarget:self selector:@selector(finish)],
+                                       nil];
+       
+       [inScene_ runAction: inAction];
+       [outScene_ runAction: outAction];
+}
+-(void) sceneOrder
+{
+       inSceneOnTop_ = NO;
+}
+-(void) initScenes
+{
+       CGSize s = [[CCDirector sharedDirector] winSize];
+       [inScene_ setPosition: ccp( -(s.width-ADJUST_FACTOR),0) ];
+}
+-(CCActionInterval*) action
+{
+       CGSize s = [[CCDirector sharedDirector] winSize];
+       return [CCMoveBy actionWithDuration:duration_ position:ccp(s.width-ADJUST_FACTOR,0)];
+}
+
+-(CCActionInterval*) easeActionWithAction:(CCActionInterval*)action
+{
+       return [CCEaseOut actionWithAction:action rate:2.0f];
+//     return [EaseElasticOut actionWithAction:action period:0.4f];
+}
+
+@end
+
+//
+// SlideInR
+//
+@implementation CCTransitionSlideInR
+-(void) sceneOrder
+{
+       inSceneOnTop_ = YES;
+}
+-(void) initScenes
+{
+       CGSize s = [[CCDirector sharedDirector] winSize];
+       [inScene_ setPosition: ccp( s.width-ADJUST_FACTOR,0) ];
+}
+
+-(CCActionInterval*) action
+{
+       CGSize s = [[CCDirector sharedDirector] winSize];
+       return [CCMoveBy actionWithDuration:duration_ position:ccp(-(s.width-ADJUST_FACTOR),0)];
+}
+
+@end
+
+//
+// SlideInT
+//
+@implementation CCTransitionSlideInT
+-(void) sceneOrder
+{
+       inSceneOnTop_ = NO;
+}
+-(void) initScenes
+{
+       CGSize s = [[CCDirector sharedDirector] winSize];
+       [inScene_ setPosition: ccp(0,s.height-ADJUST_FACTOR) ];
+}
+
+-(CCActionInterval*) action
+{
+       CGSize s = [[CCDirector sharedDirector] winSize];
+       return [CCMoveBy actionWithDuration:duration_ position:ccp(0,-(s.height-ADJUST_FACTOR))];
+}
+
+@end
+
+//
+// SlideInB
+//
+@implementation CCTransitionSlideInB
+-(void) sceneOrder
+{
+       inSceneOnTop_ = YES;
+}
+
+-(void) initScenes
+{
+       CGSize s = [[CCDirector sharedDirector] winSize];
+       [inScene_ setPosition: ccp(0,-(s.height-ADJUST_FACTOR)) ];
+}
+
+-(CCActionInterval*) action
+{
+       CGSize s = [[CCDirector sharedDirector] winSize];
+       return [CCMoveBy actionWithDuration:duration_ position:ccp(0,s.height-ADJUST_FACTOR)];
+}
+@end
+
+//
+// ShrinkGrow Transition
+//
+@implementation CCTransitionShrinkGrow
+-(void) onEnter
+{
+       [super onEnter];
+       
+       [inScene_ setScale:0.001f];
+       [outScene_ setScale:1.0f];
+
+       [inScene_ setAnchorPoint:ccp(2/3.0f,0.5f)];
+       [outScene_ setAnchorPoint:ccp(1/3.0f,0.5f)];    
+       
+       CCActionInterval *scaleOut = [CCScaleTo actionWithDuration:duration_ scale:0.01f];
+       CCActionInterval *scaleIn = [CCScaleTo actionWithDuration:duration_ scale:1.0f];
+
+       [inScene_ runAction: [self easeActionWithAction:scaleIn]];
+       [outScene_ runAction: [CCSequence actions:
+                                       [self easeActionWithAction:scaleOut],
+                                       [CCCallFunc actionWithTarget:self selector:@selector(finish)],
+                                       nil] ];
+}
+-(CCActionInterval*) easeActionWithAction:(CCActionInterval*)action
+{
+       return [CCEaseOut actionWithAction:action rate:2.0f];
+//     return [EaseElasticOut actionWithAction:action period:0.3f];
+}
+@end
+
+//
+// FlipX Transition
+//
+@implementation CCTransitionFlipX
+-(void) onEnter
+{
+       [super onEnter];
+       
+       CCActionInterval *inA, *outA;
+       [inScene_ setVisible: NO];
+
+       float inDeltaZ, inAngleZ;
+       float outDeltaZ, outAngleZ;
+
+       if( orientation == kOrientationRightOver ) {
+               inDeltaZ = 90;
+               inAngleZ = 270;
+               outDeltaZ = 90;
+               outAngleZ = 0;
+       } else {
+               inDeltaZ = -90;
+               inAngleZ = 90;
+               outDeltaZ = -90;
+               outAngleZ = 0;
+       }
+               
+       inA = [CCSequence actions:
+                  [CCDelayTime actionWithDuration:duration_/2],
+                  [CCShow action],
+                  [CCOrbitCamera actionWithDuration: duration_/2 radius: 1 deltaRadius:0 angleZ:inAngleZ deltaAngleZ:inDeltaZ angleX:0 deltaAngleX:0],
+                  [CCCallFunc actionWithTarget:self selector:@selector(finish)],
+                  nil ];
+       outA = [CCSequence actions:
+                       [CCOrbitCamera actionWithDuration: duration_/2 radius: 1 deltaRadius:0 angleZ:outAngleZ deltaAngleZ:outDeltaZ angleX:0 deltaAngleX:0],
+                       [CCHide action],
+                       [CCDelayTime actionWithDuration:duration_/2],                                                   
+                       nil ];
+       
+       [inScene_ runAction: inA];
+       [outScene_ runAction: outA];
+       
+}
+@end
+
+//
+// FlipY Transition
+//
+@implementation CCTransitionFlipY
+-(void) onEnter
+{
+       [super onEnter];
+       
+       CCActionInterval *inA, *outA;
+       [inScene_ setVisible: NO];
+
+       float inDeltaZ, inAngleZ;
+       float outDeltaZ, outAngleZ;
+
+       if( orientation == kOrientationUpOver ) {
+               inDeltaZ = 90;
+               inAngleZ = 270;
+               outDeltaZ = 90;
+               outAngleZ = 0;
+       } else {
+               inDeltaZ = -90;
+               inAngleZ = 90;
+               outDeltaZ = -90;
+               outAngleZ = 0;
+       }
+       inA = [CCSequence actions:
+                  [CCDelayTime actionWithDuration:duration_/2],
+                  [CCShow action],
+                  [CCOrbitCamera actionWithDuration: duration_/2 radius: 1 deltaRadius:0 angleZ:inAngleZ deltaAngleZ:inDeltaZ angleX:90 deltaAngleX:0],
+                  [CCCallFunc actionWithTarget:self selector:@selector(finish)],
+                  nil ];
+       outA = [CCSequence actions:
+                       [CCOrbitCamera actionWithDuration: duration_/2 radius: 1 deltaRadius:0 angleZ:outAngleZ deltaAngleZ:outDeltaZ angleX:90 deltaAngleX:0],
+                       [CCHide action],
+                       [CCDelayTime actionWithDuration:duration_/2],                                                   
+                       nil ];
+       
+       [inScene_ runAction: inA];
+       [outScene_ runAction: outA];
+       
+}
+@end
+
+//
+// FlipAngular Transition
+//
+@implementation CCTransitionFlipAngular
+-(void) onEnter
+{
+       [super onEnter];
+       
+       CCActionInterval *inA, *outA;
+       [inScene_ setVisible: NO];
+
+       float inDeltaZ, inAngleZ;
+       float outDeltaZ, outAngleZ;
+
+       if( orientation == kOrientationRightOver ) {
+               inDeltaZ = 90;
+               inAngleZ = 270;
+               outDeltaZ = 90;
+               outAngleZ = 0;
+       } else {
+               inDeltaZ = -90;
+               inAngleZ = 90;
+               outDeltaZ = -90;
+               outAngleZ = 0;
+       }
+       inA = [CCSequence actions:
+                          [CCDelayTime actionWithDuration:duration_/2],
+                          [CCShow action],
+                          [CCOrbitCamera actionWithDuration: duration_/2 radius: 1 deltaRadius:0 angleZ:inAngleZ deltaAngleZ:inDeltaZ angleX:-45 deltaAngleX:0],
+                          [CCCallFunc actionWithTarget:self selector:@selector(finish)],
+                          nil ];
+       outA = [CCSequence actions:
+                               [CCOrbitCamera actionWithDuration: duration_/2 radius: 1 deltaRadius:0 angleZ:outAngleZ deltaAngleZ:outDeltaZ angleX:45 deltaAngleX:0],
+                               [CCHide action],
+                               [CCDelayTime actionWithDuration:duration_/2],                                                   
+                               nil ];
+
+       [inScene_ runAction: inA];
+       [outScene_ runAction: outA];
+}
+@end
+
+//
+// ZoomFlipX Transition
+//
+@implementation CCTransitionZoomFlipX
+-(void) onEnter
+{
+       [super onEnter];
+       
+       CCActionInterval *inA, *outA;
+       [inScene_ setVisible: NO];
+       
+       float inDeltaZ, inAngleZ;
+       float outDeltaZ, outAngleZ;
+       
+       if( orientation == kOrientationRightOver ) {
+               inDeltaZ = 90;
+               inAngleZ = 270;
+               outDeltaZ = 90;
+               outAngleZ = 0;
+       } else {
+               inDeltaZ = -90;
+               inAngleZ = 90;
+               outDeltaZ = -90;
+               outAngleZ = 0;
+       }
+       inA = [CCSequence actions:
+                  [CCDelayTime actionWithDuration:duration_/2],
+                  [CCSpawn actions:
+                       [CCOrbitCamera actionWithDuration: duration_/2 radius: 1 deltaRadius:0 angleZ:inAngleZ deltaAngleZ:inDeltaZ angleX:0 deltaAngleX:0],
+                       [CCScaleTo actionWithDuration:duration_/2 scale:1],
+                       [CCShow action],
+                       nil],
+                  [CCCallFunc actionWithTarget:self selector:@selector(finish)],
+                  nil ];
+       outA = [CCSequence actions:
+                       [CCSpawn actions:
+                        [CCOrbitCamera actionWithDuration: duration_/2 radius: 1 deltaRadius:0 angleZ:outAngleZ deltaAngleZ:outDeltaZ angleX:0 deltaAngleX:0],
+                        [CCScaleTo actionWithDuration:duration_/2 scale:0.5f],
+                        nil],
+                       [CCHide action],
+                       [CCDelayTime actionWithDuration:duration_/2],                                                   
+                       nil ];
+       
+       inScene_.scale = 0.5f;
+       [inScene_ runAction: inA];
+       [outScene_ runAction: outA];
+}
+@end
+
+//
+// ZoomFlipY Transition
+//
+@implementation CCTransitionZoomFlipY
+-(void) onEnter
+{
+       [super onEnter];
+       
+       CCActionInterval *inA, *outA;
+       [inScene_ setVisible: NO];
+       
+       float inDeltaZ, inAngleZ;
+       float outDeltaZ, outAngleZ;
+
+       if( orientation == kOrientationUpOver ) {
+               inDeltaZ = 90;
+               inAngleZ = 270;
+               outDeltaZ = 90;
+               outAngleZ = 0;
+       } else {
+               inDeltaZ = -90;
+               inAngleZ = 90;
+               outDeltaZ = -90;
+               outAngleZ = 0;
+       }
+       
+       inA = [CCSequence actions:
+                          [CCDelayTime actionWithDuration:duration_/2],
+                          [CCSpawn actions:
+                                [CCOrbitCamera actionWithDuration: duration_/2 radius: 1 deltaRadius:0 angleZ:inAngleZ deltaAngleZ:inDeltaZ angleX:90 deltaAngleX:0],
+                                [CCScaleTo actionWithDuration:duration_/2 scale:1],
+                                [CCShow action],
+                                nil],
+                          [CCCallFunc actionWithTarget:self selector:@selector(finish)],
+                          nil ];
+       outA = [CCSequence actions:
+                               [CCSpawn actions:
+                                [CCOrbitCamera actionWithDuration: duration_/2 radius: 1 deltaRadius:0 angleZ:outAngleZ deltaAngleZ:outDeltaZ angleX:90 deltaAngleX:0],
+                                [CCScaleTo actionWithDuration:duration_/2 scale:0.5f],
+                                nil],                                                  
+                               [CCHide action],
+                               [CCDelayTime actionWithDuration:duration_/2],                                                   
+                               nil ];
+
+       inScene_.scale = 0.5f;
+       [inScene_ runAction: inA];
+       [outScene_ runAction: outA];
+}
+@end
+
+//
+// ZoomFlipAngular Transition
+//
+@implementation CCTransitionZoomFlipAngular
+-(void) onEnter
+{
+       [super onEnter];
+       
+       CCActionInterval *inA, *outA;
+       [inScene_ setVisible: NO];
+       
+       float inDeltaZ, inAngleZ;
+       float outDeltaZ, outAngleZ;
+       
+       if( orientation == kOrientationRightOver ) {
+               inDeltaZ = 90;
+               inAngleZ = 270;
+               outDeltaZ = 90;
+               outAngleZ = 0;
+       } else {
+               inDeltaZ = -90;
+               inAngleZ = 90;
+               outDeltaZ = -90;
+               outAngleZ = 0;
+       }
+               
+       inA = [CCSequence actions:
+                  [CCDelayTime actionWithDuration:duration_/2],
+                  [CCSpawn actions:
+                       [CCOrbitCamera actionWithDuration: duration_/2 radius: 1 deltaRadius:0 angleZ:inAngleZ deltaAngleZ:inDeltaZ angleX:-45 deltaAngleX:0],
+                       [CCScaleTo actionWithDuration:duration_/2 scale:1],
+                       [CCShow action],
+                       nil],                                              
+                  [CCShow action],
+                  [CCCallFunc actionWithTarget:self selector:@selector(finish)],
+                  nil ];
+       outA = [CCSequence actions:
+                       [CCSpawn actions:
+                        [CCOrbitCamera actionWithDuration: duration_/2 radius: 1 deltaRadius:0 angleZ:outAngleZ deltaAngleZ:outDeltaZ angleX:45 deltaAngleX:0],
+                        [CCScaleTo actionWithDuration:duration_/2 scale:0.5f],
+                        nil],                                                  
+                       [CCHide action],
+                       [CCDelayTime actionWithDuration:duration_/2],                                                   
+                       nil ];
+       
+       inScene_.scale = 0.5f;
+       [inScene_ runAction: inA];
+       [outScene_ runAction: outA];
+}
+@end
+
+
+//
+// Fade Transition
+//
+@implementation CCTransitionFade
++(id) transitionWithDuration:(ccTime)d scene:(CCScene*)s withColor:(ccColor3B)color
+{
+       return [[[self alloc] initWithDuration:d scene:s withColor:color] autorelease];
+}
+
+-(id) initWithDuration:(ccTime)d scene:(CCScene*)s withColor:(ccColor3B)aColor
+{
+       if( (self=[super initWithDuration:d scene:s]) ) {
+               color.r = aColor.r;
+               color.g = aColor.g;
+               color.b = aColor.b;
+       }
+       
+       return self;
+}
+
+-(id) initWithDuration:(ccTime)d scene:(CCScene*)s
+{
+       return [self initWithDuration:d scene:s withColor:ccBLACK];
+}
+
+-(void) onEnter
+{
+       [super onEnter];
+       
+       CCLayerColor *l = [CCLayerColor layerWithColor:color];
+       [inScene_ setVisible: NO];
+       
+       [self addChild: l z:2 tag:kSceneFade];
+       
+       
+       CCNode *f = [self getChildByTag:kSceneFade];
+       
+       CCActionInterval *a = [CCSequence actions:
+                                                  [CCFadeIn actionWithDuration:duration_/2],
+                                                  [CCCallFunc actionWithTarget:self selector:@selector(hideOutShowIn)],
+                                                  [CCFadeOut actionWithDuration:duration_/2],
+                                                  [CCCallFunc actionWithTarget:self selector:@selector(finish)],
+                                                  nil ];
+       [f runAction: a];
+}
+
+-(void) onExit
+{
+       [super onExit];
+       [self removeChildByTag:kSceneFade cleanup:NO];
+}
+@end
+
+
+//
+// Cross Fade Transition
+//
+@implementation CCTransitionCrossFade
+
+-(void) draw
+{
+       // override draw since both scenes (textures) are rendered in 1 scene
+}
+
+-(void) onEnter
+{
+       [super onEnter];
+       
+       // create a transparent color layer
+       // in which we are going to add our rendertextures
+       ccColor4B  color = {0,0,0,0};
+       CGSize size = [[CCDirector sharedDirector] winSize];
+       CCLayerColor * layer = [CCLayerColor layerWithColor:color];
+       
+       // create the first render texture for inScene_
+       CCRenderTexture *inTexture = [CCRenderTexture renderTextureWithWidth:size.width height:size.height];
+       inTexture.sprite.anchorPoint= ccp(0.5f,0.5f);
+       inTexture.position = ccp(size.width/2, size.height/2);
+       inTexture.anchorPoint = ccp(0.5f,0.5f);
+       
+       // render inScene_ to its texturebuffer
+       [inTexture begin];
+       [inScene_ visit];
+       [inTexture end];
+       
+       // create the second render texture for outScene_
+       CCRenderTexture *outTexture = [CCRenderTexture renderTextureWithWidth:size.width height:size.height];
+       outTexture.sprite.anchorPoint= ccp(0.5f,0.5f);
+       outTexture.position = ccp(size.width/2, size.height/2);
+       outTexture.anchorPoint = ccp(0.5f,0.5f);
+       
+       // render outScene_ to its texturebuffer
+       [outTexture begin];
+       [outScene_ visit];
+       [outTexture end];
+       
+       // create blend functions
+       
+       ccBlendFunc blend1 = {GL_ONE, GL_ONE}; // inScene_ will lay on background and will not be used with alpha
+       ccBlendFunc blend2 = {GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA}; // we are going to blend outScene_ via alpha 
+       
+       // set blendfunctions
+       [inTexture.sprite setBlendFunc:blend1];
+       [outTexture.sprite setBlendFunc:blend2];        
+       
+       // add render textures to the layer
+       [layer addChild:inTexture];
+       [layer addChild:outTexture];
+       
+       // initial opacity:
+       [inTexture.sprite setOpacity:255];
+       [outTexture.sprite setOpacity:255];
+       
+       // create the blend action
+       CCActionInterval * layerAction = [CCSequence actions:
+                                                                         [CCFadeTo actionWithDuration:duration_ opacity:0],
+                                                                         [CCCallFunc actionWithTarget:self selector:@selector(hideOutShowIn)],
+                                                                         [CCCallFunc actionWithTarget:self selector:@selector(finish)],
+                                                                         nil ];
+       
+       
+       // run the blend action
+       [outTexture.sprite runAction: layerAction];
+       
+       // add the layer (which contains our two rendertextures) to the scene
+       [self addChild: layer z:2 tag:kSceneFade];
+}
+
+// clean up on exit
+-(void) onExit
+{
+       // remove our layer and release all containing objects 
+       [self removeChildByTag:kSceneFade cleanup:NO];
+       
+       [super onExit]; 
+}
+@end
+
+//
+// TurnOffTilesTransition
+//
+@implementation CCTransitionTurnOffTiles
+
+// override addScenes, and change the order
+-(void) sceneOrder
+{
+       inSceneOnTop_ = NO;
+}
+
+-(void) onEnter
+{
+       [super onEnter];
+       CGSize s = [[CCDirector sharedDirector] winSize];
+       float aspect = s.width / s.height;
+       int x = 12 * aspect;
+       int y = 12;
+       
+       id toff = [CCTurnOffTiles actionWithSize: ccg(x,y) duration:duration_];
+       id action = [self easeActionWithAction:toff];
+       [outScene_ runAction: [CCSequence actions: action,
+                                  [CCCallFunc actionWithTarget:self selector:@selector(finish)],
+                                  [CCStopGrid action],
+                                  nil]
+        ];
+
+}
+-(CCActionInterval*) easeActionWithAction:(CCActionInterval*)action
+{
+       return action;
+//     return [EaseIn actionWithAction:action rate:2.0f];
+}
+@end
+
+#pragma mark Split Transitions
+
+//
+// SplitCols Transition
+//
+@implementation CCTransitionSplitCols
+
+-(void) onEnter
+{
+       [super onEnter];
+
+       inScene_.visible = NO;
+       
+       id split = [self action];
+       id seq = [CCSequence actions:
+                               split,
+                               [CCCallFunc actionWithTarget:self selector:@selector(hideOutShowIn)],
+                               [split reverse],
+                               nil
+                         ];
+       [self runAction: [CCSequence actions:
+                          [self easeActionWithAction:seq],
+                          [CCCallFunc actionWithTarget:self selector:@selector(finish)],
+                          [CCStopGrid action],
+                          nil]
+        ];
+}
+
+-(CCActionInterval*) action
+{
+       return [CCSplitCols actionWithCols:3 duration:duration_/2.0f];
+}
+
+-(CCActionInterval*) easeActionWithAction:(CCActionInterval*)action
+{
+       return [CCEaseInOut actionWithAction:action rate:3.0f];
+}
+@end
+
+//
+// SplitRows Transition
+//
+@implementation CCTransitionSplitRows
+-(CCActionInterval*) action
+{
+       return [CCSplitRows actionWithRows:3 duration:duration_/2.0f];
+}
+@end
+
+
+#pragma mark Fade Grid Transitions
+
+//
+// FadeTR Transition
+//
+@implementation CCTransitionFadeTR
+-(void) sceneOrder
+{
+       inSceneOnTop_ = NO;
+}
+
+-(void) onEnter
+{
+       [super onEnter];
+       
+       CGSize s = [[CCDirector sharedDirector] winSize];
+       float aspect = s.width / s.height;
+       int x = 12 * aspect;
+       int y = 12;
+       
+       id action  = [self actionWithSize:ccg(x,y)];
+
+       [outScene_ runAction: [CCSequence actions:
+                                       [self easeActionWithAction:action],
+                                   [CCCallFunc actionWithTarget:self selector:@selector(finish)],
+                                   [CCStopGrid action],
+                                   nil]
+        ];
+}
+
+-(CCActionInterval*) actionWithSize: (ccGridSize) v
+{
+       return [CCFadeOutTRTiles actionWithSize:v duration:duration_];
+}
+
+-(CCActionInterval*) easeActionWithAction:(CCActionInterval*)action
+{
+       return action;
+//     return [EaseIn actionWithAction:action rate:2.0f];
+}
+@end
+
+//
+// FadeBL Transition
+//
+@implementation CCTransitionFadeBL
+-(CCActionInterval*) actionWithSize: (ccGridSize) v
+{
+       return [CCFadeOutBLTiles actionWithSize:v duration:duration_];
+}
+@end
+
+//
+// FadeUp Transition
+//
+@implementation CCTransitionFadeUp
+-(CCActionInterval*) actionWithSize: (ccGridSize) v
+{
+       return [CCFadeOutUpTiles actionWithSize:v duration:duration_];
+}
+@end
+
+//
+// FadeDown Transition
+//
+@implementation CCTransitionFadeDown
+-(CCActionInterval*) actionWithSize: (ccGridSize) v
+{
+       return [CCFadeOutDownTiles actionWithSize:v duration:duration_];
+}
+@end
diff --git a/libs/cocos2d/CCTransitionPageTurn.h b/libs/cocos2d/CCTransitionPageTurn.h
new file mode 100644 (file)
index 0000000..aacb7fc
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2009 Sindesso Pty Ltd http://www.sindesso.com/
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+
+#import "CCTransition.h"
+
+/** CCTransitionPageTurn transition.
+ * A transition which peels back the bottom right hand corner of a scene
+ * to transition to the scene beneath it simulating a page turn
+ *
+ * This uses a 3DAction so it's strongly recommended that depth buffering
+ * is turned on in CCDirector using:
+ *
+ *     [[CCDirector sharedDirector] setDepthBufferFormat:kCCDepthBuffer16]; 
+ *
+ * @since v0.8.2
+ */
+@interface CCTransitionPageTurn : CCTransitionScene
+{
+       BOOL    back_;
+}
+/**
+ * creates a base transition with duration and incoming scene
+ * if back is TRUE then the effect is reversed to appear as if the incoming 
+ * scene is being turned from left over the outgoing scene
+ */
++(id) transitionWithDuration:(ccTime) t scene:(CCScene*)s backwards:(BOOL) back;
+
+/**
+ * creates a base transition with duration and incoming scene
+ * if back is TRUE then the effect is reversed to appear as if the incoming 
+ * scene is being turned from left over the outgoing scene
+ */
+-(id) initWithDuration:(ccTime) t scene:(CCScene*)s backwards:(BOOL) back;
+
+-(CCActionInterval*) actionWithSize:(ccGridSize) vector;
+
+@end
diff --git a/libs/cocos2d/CCTransitionPageTurn.m b/libs/cocos2d/CCTransitionPageTurn.m
new file mode 100644 (file)
index 0000000..bff43a7
--- /dev/null
@@ -0,0 +1,117 @@
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2009 Sindesso Pty Ltd http://www.sindesso.com/
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+
+#import "CCTransitionPageTurn.h"
+#import        "CCActionPageTurn3D.h"
+#import "CCDirector.h"
+
+@implementation CCTransitionPageTurn
+
+/** creates a base transition with duration and incoming scene */
++(id) transitionWithDuration:(ccTime) t scene:(CCScene*)s backwards:(BOOL) back
+{
+       return [[[self alloc] initWithDuration:t scene:s backwards:back] autorelease];
+}
+
+/** initializes a transition with duration and incoming scene */
+-(id) initWithDuration:(ccTime) t scene:(CCScene*)s backwards:(BOOL) back
+{
+       // XXX: needed before [super init]
+       back_ = back;
+
+       if( ( self = [super initWithDuration:t scene:s] ) )
+       {
+               // do something
+       }
+       return self;
+}
+
+-(void) sceneOrder
+{
+       inSceneOnTop_ = back_;
+}
+
+//
+-(void) onEnter
+{
+       [super onEnter];
+       
+       CGSize s = [[CCDirector sharedDirector] winSize];
+       int x, y;
+       if( s.width > s.height)
+       {
+               x = 16;
+               y = 12;
+       }
+       else
+       {
+               x = 12;
+               y = 16;
+       }
+       
+       id action  = [self actionWithSize:ccg(x,y)];
+       
+       if(! back_ )
+       {
+               [outScene_ runAction: [CCSequence actions:
+                                                         action,
+                                                         [CCCallFunc actionWithTarget:self selector:@selector(finish)],
+                                                         [CCStopGrid action],
+                                                         nil]
+                ];
+       }
+       else
+       {
+               // to prevent initial flicker
+               inScene_.visible = NO;
+               [inScene_ runAction: [CCSequence actions:
+                                                        [CCShow action],
+                                                        action,
+                                                        [CCCallFunc actionWithTarget:self selector:@selector(finish)],
+                                                        [CCStopGrid action],
+                                                        nil]
+                ];
+       }
+       
+}
+
+-(CCActionInterval*) actionWithSize: (ccGridSize) v
+{
+       if( back_ )
+       {
+               // Get hold of the PageTurn3DAction
+               return [CCReverseTime actionWithAction:
+                               [CCPageTurn3D actionWithSize:v duration:duration_]];
+       }
+       else
+       {
+               // Get hold of the PageTurn3DAction
+               return [CCPageTurn3D actionWithSize:v duration:duration_];
+       }
+}
+
+@end
+
diff --git a/libs/cocos2d/CCTransitionRadial.h b/libs/cocos2d/CCTransitionRadial.h
new file mode 100644 (file)
index 0000000..6d4a5e0
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2009 Lam Pham
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+#import "CCTransition.h"
+#import "CCProgressTimer.h"
+#import "CCActionProgressTimer.h"
+
+/** CCTransitionRadialCCW transition.
+ A counter colock-wise radial transition to the next scene
+ */
+@interface CCTransitionRadialCCW : CCTransitionScene
+@end
+
+/** CCTransitionRadialCW transition.
+ A counter colock-wise radial transition to the next scene
+*/
+@interface CCTransitionRadialCW : CCTransitionRadialCCW
+@end
diff --git a/libs/cocos2d/CCTransitionRadial.m b/libs/cocos2d/CCTransitionRadial.m
new file mode 100644 (file)
index 0000000..a892f35
--- /dev/null
@@ -0,0 +1,115 @@
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2009 Lam Pham
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+
+
+#import "CCDirector.h"
+#import "CCTransitionRadial.h"
+#import "CCRenderTexture.h"
+#import "CCLayer.h"
+#import "CCActionInstant.h"
+#import "Support/CGPointExtension.h"
+
+enum {
+       kSceneRadial = 0xc001,
+};
+
+#pragma mark -
+#pragma mark Transition Radial CCW
+
+@implementation CCTransitionRadialCCW
+-(void) sceneOrder
+{
+       inSceneOnTop_ = NO;
+}
+
+-(CCProgressTimerType) radialType
+{
+       return kCCProgressTimerTypeRadialCCW;
+}
+
+-(void) onEnter
+{
+       [super onEnter];
+       // create a transparent color layer
+       // in which we are going to add our rendertextures
+       CGSize size = [[CCDirector sharedDirector] winSize];
+               
+       // create the second render texture for outScene
+       CCRenderTexture *outTexture = [CCRenderTexture renderTextureWithWidth:size.width height:size.height];
+       outTexture.sprite.anchorPoint= ccp(0.5f,0.5f);
+       outTexture.position = ccp(size.width/2, size.height/2);
+       outTexture.anchorPoint = ccp(0.5f,0.5f);
+       
+       // render outScene to its texturebuffer
+       [outTexture clear:0 g:0 b:0 a:1];
+       [outTexture begin];
+       [outScene_ visit];
+       [outTexture end];
+       
+       //      Since we've passed the outScene to the texture we don't need it.
+       [self hideOutShowIn];
+       
+       //      We need the texture in RenderTexture.
+       CCProgressTimer *outNode = [CCProgressTimer progressWithTexture:outTexture.sprite.texture];
+       // but it's flipped upside down so we flip the sprite
+       outNode.sprite.flipY = YES;
+       //      Return the radial type that we want to use
+       outNode.type = [self radialType];
+       outNode.percentage = 100.f;
+       outNode.position = ccp(size.width/2, size.height/2);
+       outNode.anchorPoint = ccp(0.5f,0.5f);
+                       
+       // create the blend action
+       CCActionInterval * layerAction = [CCSequence actions:
+                                                                         [CCProgressFromTo actionWithDuration:duration_ from:100.f to:0.f],
+                                                                         [CCCallFunc actionWithTarget:self selector:@selector(finish)],
+                                                                         nil ];        
+       // run the blend action
+       [outNode runAction: layerAction];
+       
+       // add the layer (which contains our two rendertextures) to the scene
+       [self addChild: outNode z:2 tag:kSceneRadial];
+}
+
+// clean up on exit
+-(void) onExit
+{
+       // remove our layer and release all containing objects 
+       [self removeChildByTag:kSceneRadial cleanup:NO];
+       [super onExit]; 
+}
+@end
+
+#pragma mark -
+#pragma mark Transition Radial CW
+
+@implementation CCTransitionRadialCW
+-(CCProgressTimerType) radialType
+{
+       return kCCProgressTimerTypeRadialCW;
+}
+@end
+
diff --git a/libs/cocos2d/Platforms/CCGL.h b/libs/cocos2d/Platforms/CCGL.h
new file mode 100644 (file)
index 0000000..0725f89
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+//
+// Common layer for OpenGL stuff
+//
+
+#import <Availability.h>
+
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+#import <OpenGLES/ES1/gl.h>
+#import <OpenGLES/ES1/glext.h>
+#import <OpenGLES/EAGL.h>
+#import "iOS/glu.h"
+#import "iOS/EAGLView.h"
+
+#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED)
+#import <OpenGL/gl.h>
+#import <OpenGL/glu.h>
+#import <Cocoa/Cocoa.h>        // needed for NSOpenGLView
+#import "Mac/MacGLView.h"
+#endif
+
+
+// iOS
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+#define CC_GLVIEW                                      EAGLView
+#define ccglOrtho                                      glOrthof
+#define        ccglClearDepth                          glClearDepthf
+#define ccglGenerateMipmap                     glGenerateMipmapOES
+#define ccglGenFramebuffers                    glGenFramebuffersOES
+#define ccglBindFramebuffer                    glBindFramebufferOES
+#define ccglFramebufferTexture2D       glFramebufferTexture2DOES
+#define ccglDeleteFramebuffers         glDeleteFramebuffersOES
+#define ccglCheckFramebufferStatus     glCheckFramebufferStatusOES
+#define ccglTranslate                          glTranslatef
+
+#define CC_GL_FRAMEBUFFER                      GL_FRAMEBUFFER_OES
+#define CC_GL_FRAMEBUFFER_BINDING      GL_FRAMEBUFFER_BINDING_OES
+#define CC_GL_COLOR_ATTACHMENT0                GL_COLOR_ATTACHMENT0_OES
+#define CC_GL_FRAMEBUFFER_COMPLETE     GL_FRAMEBUFFER_COMPLETE_OES
+
+// Mac
+#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED)
+#define CC_GLVIEW                                      MacGLView
+#define ccglOrtho                                      glOrtho
+#define        ccglClearDepth                          glClearDepth
+#define ccglGenerateMipmap                     glGenerateMipmap
+#define ccglGenFramebuffers                    glGenFramebuffers
+#define ccglBindFramebuffer                    glBindFramebuffer
+#define ccglFramebufferTexture2D       glFramebufferTexture2D
+#define ccglDeleteFramebuffers         glDeleteFramebuffers
+#define ccglCheckFramebufferStatus     glCheckFramebufferStatus
+#define ccglTranslate                          glTranslated
+
+#define CC_GL_FRAMEBUFFER                      GL_FRAMEBUFFER
+#define CC_GL_FRAMEBUFFER_BINDING      GL_FRAMEBUFFER_BINDING
+#define CC_GL_COLOR_ATTACHMENT0                GL_COLOR_ATTACHMENT0
+#define CC_GL_FRAMEBUFFER_COMPLETE     GL_FRAMEBUFFER_COMPLETE
+
+#endif
diff --git a/libs/cocos2d/Platforms/CCNS.h b/libs/cocos2d/Platforms/CCNS.h
new file mode 100644 (file)
index 0000000..c595a18
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+//
+// Common layer for NS (Next-Step) stuff
+//
+
+#import <Availability.h>
+
+#import <Foundation/Foundation.h> //   for NSObject
+
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+
+#define CCRectFromString(__r__)                CGRectFromString(__r__)
+#define CCPointFromString(__p__)       CGPointFromString(__p__)
+#define CCSizeFromString(__s__)                CGSizeFromString(__s__)
+#define CCNSSizeToCGSize
+#define CCNSRectToCGRect
+#define CCNSPointToCGPoint
+#define CCTextAlignment                                UITextAlignment
+#define CCTextAlignmentCenter          UITextAlignmentCenter
+#define CCTextAlignmentLeft                    UITextAlignmentLeft
+#define CCTextAlignmentRight           UITextAlignmentRight
+#define CCLineBreakMode                                UILineBreakMode
+#define CCLineBreakModeWordWrap                UILineBreakModeWordWrap 
+#define CCLineBreakModeCharacterWrap   UILineBreakModeCharacterWrap
+#define CCLineBreakModeClip                    UILineBreakModeClip
+#define CCLineBreakModeHeadTruncation  UILineBreakModeHeadTruncation
+#define CCLineBreakModeTailTruncation  UILineBreakModeTailTruncation
+#define CCLineBreakModeMiddleTruncation        UILineBreakModeMiddleTruncation
+
+
+
+#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED)
+
+#define CCRectFromString(__r__)                NSRectToCGRect( NSRectFromString(__r__) )
+#define CCPointFromString(__p__)       NSPointToCGPoint( NSPointFromString(__p__) )
+#define CCSizeFromString(__s__)                NSSizeToCGSize( NSSizeFromString(__s__) )
+#define CCNSSizeToCGSize                       NSSizeToCGSize
+#define CCNSRectToCGRect                       NSRectToCGRect
+#define CCNSPointToCGPoint                     NSPointToCGPoint
+#define CCTextAlignment                                NSTextAlignment
+#define CCTextAlignmentCenter          NSCenterTextAlignment
+#define CCTextAlignmentLeft                    NSLeftTextAlignment
+#define CCTextAlignmentRight           NSRightTextAlignment
+#define CCLineBreakMode                                NSLineBreakMode
+#define CCLineBreakModeWordWrap                NSLineBreakByWordWrapping
+#define CCLineBreakModeClip                    -1
+#define CCLineBreakModeHeadTruncation  -1
+#define CCLineBreakModeTailTruncation  -1
+#define CCLineBreakModeMiddleTruncation        -1
+
+
+#endif
+
+
diff --git a/libs/cocos2d/Platforms/Mac/CCDirectorMac.h b/libs/cocos2d/Platforms/Mac/CCDirectorMac.h
new file mode 100644 (file)
index 0000000..0d623b4
--- /dev/null
@@ -0,0 +1,103 @@
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+
+// Only compile this code on Mac. These files should not be included on your iOS project.
+// But in case they are included, it won't be compiled.
+#import <Availability.h>
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED)
+
+#import <QuartzCore/CVDisplayLink.h>
+#import "../../CCDirector.h"
+
+enum  {
+       /// If the window is resized, it won't be autoscaled
+       kCCDirectorResize_NoScale,
+       /// If the window is resized, it will be autoscaled (default behavior)
+       kCCDirectorResize_AutoScale,
+};
+
+@interface CCDirector (MacExtension)
+/** converts an NSEvent to GL coordinates */
+-(CGPoint) convertEventToGL:(NSEvent*)event;
+@end
+
+/** Base class of Mac directors
+ @since v0.99.5
+ */
+@interface CCDirectorMac : CCDirector
+{
+       BOOL                    isFullScreen_;
+       int                             resizeMode_;
+       CGPoint                 winOffset_;
+    CGSize                     originalWinSize_;
+       
+       NSWindow                *fullScreenWindow_;
+    
+       // cache
+       NSWindow                *windowGLView_;
+    NSView          *superViewGLView_;
+    NSRect          originalWinRect_; // Original size and position
+}
+
+// whether or not the view is in fullscreen mode
+@property (nonatomic, readonly) BOOL isFullScreen;
+
+// resize mode: with or without scaling
+@property (nonatomic, readwrite) int resizeMode;
+
+@property (nonatomic, readwrite) CGSize originalWinSize;
+
+/** Sets the view in fullscreen or window mode */
+- (void) setFullScreen:(BOOL)fullscreen;
+
+/** Converts window size coordiantes to logical coordinates.
+ Useful only if resizeMode is kCCDirectorResize_Scale.
+ If resizeMode is kCCDirectorResize_NoScale, then no conversion will be done.
+*/
+- (CGPoint) convertToLogicalCoordinates:(CGPoint)coordinates;
+@end
+
+
+/** DisplayLinkDirector is a Director that synchronizes timers with the refresh rate of the display.
+ *
+ * Features and Limitations:
+ * - Only available on 3.1+
+ * - Scheduled timers & drawing are synchronizes with the refresh rate of the display
+ * - Only supports animation intervals of 1/60 1/30 & 1/15
+ *
+ * It is the recommended Director if the SDK is 3.1 or newer
+ *
+ * @since v0.8.2
+ */
+@interface CCDirectorDisplayLink : CCDirectorMac
+{
+       CVDisplayLinkRef displayLink;
+}
+@end
+
+#endif // __MAC_OS_X_VERSION_MAX_ALLOWED
+
diff --git a/libs/cocos2d/Platforms/Mac/CCDirectorMac.m b/libs/cocos2d/Platforms/Mac/CCDirectorMac.m
new file mode 100644 (file)
index 0000000..477081e
--- /dev/null
@@ -0,0 +1,479 @@
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+// Only compile this code on Mac. These files should not be included on your iOS project.
+// But in case they are included, it won't be compiled.
+#import <Availability.h>
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED)
+
+#import <sys/time.h>
+#import "CCDirectorMac.h"
+#import "CCEventDispatcher.h"
+#import "MacGLView.h"
+
+#import "../../CCNode.h"
+#import "../../CCScheduler.h"
+#import "../../ccMacros.h"
+
+#pragma mark -
+#pragma mark Director Mac extensions
+
+
+@interface CCDirector ()
+-(void) setNextScene;
+-(void) showFPS;
+-(void) calculateDeltaTime;
+@end
+
+@implementation CCDirector (MacExtension)
+-(CGPoint) convertEventToGL:(NSEvent*)event
+{
+       NSPoint point = [openGLView_ convertPoint:[event locationInWindow] fromView:nil];
+       CGPoint p = NSPointToCGPoint(point);
+       
+       return  [(CCDirectorMac*)self convertToLogicalCoordinates:p];
+}
+
+@end
+
+#pragma mark -
+#pragma mark Director Mac
+
+@implementation CCDirectorMac
+
+@synthesize isFullScreen = isFullScreen_;
+@synthesize originalWinSize = originalWinSize_;
+
+-(id) init
+{
+       if( (self = [super init]) ) {
+               isFullScreen_ = NO;
+               resizeMode_ = kCCDirectorResize_AutoScale;
+               
+        originalWinSize_ = CGSizeZero;
+               fullScreenWindow_ = nil;
+               windowGLView_ = nil;
+               winOffset_ = CGPointZero;
+       }
+       
+       return self;
+}
+
+- (void) dealloc
+{
+    [superViewGLView_ release];
+       [fullScreenWindow_ release];
+       [windowGLView_ release];
+       [super dealloc];
+}
+
+//
+// setFullScreen code taken from GLFullScreen example by Apple
+//
+- (void) setFullScreen:(BOOL)fullscreen
+{
+       // Mac OS X 10.6 and later offer a simplified mechanism to create full-screen contexts
+#if MAC_OS_X_VERSION_MIN_REQUIRED > MAC_OS_X_VERSION_10_5
+       
+    if (isFullScreen_ == fullscreen) return;
+    
+    if( fullscreen ) {
+        originalWinRect_ = [openGLView_ frame];
+
+        // Cache normal window and superview of openGLView
+        if(!windowGLView_)
+            windowGLView_ = [[openGLView_ window] retain];
+        
+        [superViewGLView_ release];
+        superViewGLView_ = [[openGLView_ superview] retain];
+        
+                              
+        // Get screen size
+        NSRect displayRect = [[NSScreen mainScreen] frame];
+        
+        // Create a screen-sized window on the display you want to take over
+        fullScreenWindow_ = [[MacWindow alloc] initWithFrame:displayRect fullscreen:YES];
+        
+        // Remove glView from window
+        [openGLView_ removeFromSuperview];
+        
+        // Set new frame
+        [openGLView_ setFrame:displayRect];
+        
+        // Attach glView to fullscreen window
+        [fullScreenWindow_ setContentView:openGLView_];
+        
+        // Show the fullscreen window
+        [fullScreenWindow_ makeKeyAndOrderFront:self];
+               [fullScreenWindow_ makeMainWindow];
+        
+    } else {
+        
+        // Remove glView from fullscreen window
+        [openGLView_ removeFromSuperview];
+        
+        // Release fullscreen window
+        [fullScreenWindow_ release];
+        fullScreenWindow_ = nil;
+        
+        // Attach glView to superview
+        [superViewGLView_ addSubview:openGLView_];
+        
+        // Set new frame
+        [openGLView_ setFrame:originalWinRect_];
+        
+        // Show the window
+        [windowGLView_ makeKeyAndOrderFront:self];
+               [windowGLView_ makeMainWindow];
+    }
+    isFullScreen_ = fullscreen;
+    
+    [openGLView_ retain]; // Retain +1
+    
+    // re-configure glView
+    [self setOpenGLView:openGLView_];
+    
+    [openGLView_ release]; // Retain -1
+    
+    [openGLView_ setNeedsDisplay:YES];
+#else
+#error Full screen is not supported for Mac OS 10.5 or older yet
+#error If you don't want FullScreen support, you can safely remove these 2 lines
+#endif
+}
+
+-(void) setOpenGLView:(MacGLView *)view
+{
+       [super setOpenGLView:view];
+       
+       // cache the NSWindow and NSOpenGLView created from the NIB
+       if( !isFullScreen_ && CGSizeEqualToSize(originalWinSize_, CGSizeZero))
+    {
+               originalWinSize_ = winSizeInPixels_;
+       }
+}
+
+-(int) resizeMode
+{
+       return resizeMode_;
+}
+
+-(void) setResizeMode:(int)mode
+{
+       if( mode != resizeMode_ ) {
+
+               resizeMode_ = mode;
+
+        [self setProjection:projection_];
+        [openGLView_ setNeedsDisplay: YES];
+       }
+}
+
+-(void) setProjection:(ccDirectorProjection)projection
+{
+       CGSize size = winSizeInPixels_;
+       
+       CGPoint offset = CGPointZero;
+       float widthAspect = size.width;
+       float heightAspect = size.height;
+       
+       
+       if( resizeMode_ == kCCDirectorResize_AutoScale && ! CGSizeEqualToSize(originalWinSize_, CGSizeZero ) ) {
+               
+               size = originalWinSize_;
+
+               float aspect = originalWinSize_.width / originalWinSize_.height;
+               widthAspect = winSizeInPixels_.width;
+               heightAspect = winSizeInPixels_.width / aspect;
+               
+               if( heightAspect > winSizeInPixels_.height ) {
+                       widthAspect = winSizeInPixels_.height * aspect;
+                       heightAspect = winSizeInPixels_.height;                 
+               }
+               
+               winOffset_.x = (winSizeInPixels_.width - widthAspect) / 2;
+               winOffset_.y =  (winSizeInPixels_.height - heightAspect) / 2;
+               
+               offset = winOffset_;
+
+       }
+
+       switch (projection) {
+               case kCCDirectorProjection2D:
+                       glViewport(offset.x, offset.y, widthAspect, heightAspect);
+                       glMatrixMode(GL_PROJECTION);
+                       glLoadIdentity();
+                       ccglOrtho(0, size.width, 0, size.height, -1024, 1024);
+                       glMatrixMode(GL_MODELVIEW);
+                       glLoadIdentity();
+                       break;
+                       
+               case kCCDirectorProjection3D:
+                       glViewport(offset.x, offset.y, widthAspect, heightAspect);
+                       glMatrixMode(GL_PROJECTION);
+                       glLoadIdentity();
+                       gluPerspective(60, (GLfloat)widthAspect/heightAspect, 0.1f, 1500.0f);
+                       
+                       glMatrixMode(GL_MODELVIEW);     
+                       glLoadIdentity();
+                       
+                       float eyeZ = size.height * [self getZEye] / winSizeInPixels_.height;
+
+                       gluLookAt( size.width/2, size.height/2, eyeZ,
+                                         size.width/2, size.height/2, 0,
+                                         0.0f, 1.0f, 0.0f);                    
+                       break;
+                       
+               case kCCDirectorProjectionCustom:
+                       if( projectionDelegate_ )
+                               [projectionDelegate_ updateProjection];
+                       break;
+                       
+               default:
+                       CCLOG(@"cocos2d: Director: unrecognized projecgtion");
+                       break;
+       }
+       
+       projection_ = projection;
+}
+
+// If scaling is supported, then it should always return the original size
+// otherwise it should return the "real" size.
+-(CGSize) winSize
+{
+       if( resizeMode_ == kCCDirectorResize_AutoScale )
+               return originalWinSize_;
+    
+       return winSizeInPixels_;
+}
+
+-(CGSize) winSizeInPixels
+{
+       return [self winSize];
+}
+
+- (CGPoint) convertToLogicalCoordinates:(CGPoint)coords
+{
+       CGPoint ret;
+       
+       if( resizeMode_ == kCCDirectorResize_NoScale )
+               ret = coords;
+       
+       else {
+       
+               float x_diff = originalWinSize_.width / (winSizeInPixels_.width - winOffset_.x * 2);
+               float y_diff = originalWinSize_.height / (winSizeInPixels_.height - winOffset_.y * 2);
+               
+               float adjust_x = (winSizeInPixels_.width * x_diff - originalWinSize_.width ) / 2;
+               float adjust_y = (winSizeInPixels_.height * y_diff - originalWinSize_.height ) / 2;
+               
+               ret = CGPointMake( (x_diff * coords.x) - adjust_x, ( y_diff * coords.y ) - adjust_y );          
+       }
+       
+       return ret;
+}
+@end
+
+
+#pragma mark -
+#pragma mark DirectorDisplayLink
+
+
+@implementation CCDirectorDisplayLink
+
+- (CVReturn) getFrameForTime:(const CVTimeStamp*)outputTime
+{
+#if CC_DIRECTOR_MAC_USE_DISPLAY_LINK_THREAD
+       if( ! runningThread_ )
+               runningThread_ = [NSThread currentThread];
+       
+       NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+
+       [self drawScene];
+       [[CCEventDispatcher sharedDispatcher] dispatchQueuedEvents];
+       
+       [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:nil];
+       
+       [pool release];
+
+#else
+       [self performSelector:@selector(drawScene) onThread:runningThread_ withObject:nil waitUntilDone:YES];
+#endif
+       
+    return kCVReturnSuccess;
+}
+
+// This is the renderer output callback function
+static CVReturn MyDisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeStamp* now, const CVTimeStamp* outputTime, CVOptionFlags flagsIn, CVOptionFlags* flagsOut, void* displayLinkContext)
+{
+    CVReturn result = [(CCDirectorDisplayLink*)displayLinkContext getFrameForTime:outputTime];
+    return result;
+}
+
+- (void) startAnimation
+{
+#if ! CC_DIRECTOR_MAC_USE_DISPLAY_LINK_THREAD
+       runningThread_ = [[NSThread alloc] initWithTarget:self selector:@selector(mainLoop) object:nil];
+       [runningThread_ start]; 
+#endif
+       
+       gettimeofday( &lastUpdate_, NULL);
+       
+       // Create a display link capable of being used with all active displays
+       CVDisplayLinkCreateWithActiveCGDisplays(&displayLink);
+       
+       // Set the renderer output callback function
+       CVDisplayLinkSetOutputCallback(displayLink, &MyDisplayLinkCallback, self);
+       
+       // Set the display link for the current renderer
+       CGLContextObj cglContext = [[openGLView_ openGLContext] CGLContextObj];
+       CGLPixelFormatObj cglPixelFormat = [[openGLView_ pixelFormat] CGLPixelFormatObj];
+       CVDisplayLinkSetCurrentCGDisplayFromOpenGLContext(displayLink, cglContext, cglPixelFormat);
+       
+       // Activate the display link
+       CVDisplayLinkStart(displayLink);
+}
+
+- (void) stopAnimation
+{
+       if( displayLink ) {
+               CVDisplayLinkStop(displayLink);
+               CVDisplayLinkRelease(displayLink);
+               displayLink = NULL;
+               
+#if ! CC_DIRECTOR_MAC_USE_DISPLAY_LINK_THREAD
+               [runningThread_ cancel];
+               [runningThread_ release];
+               runningThread_ = nil;
+#endif
+       }
+}
+
+-(void) dealloc
+{
+       if( displayLink ) {
+               CVDisplayLinkStop(displayLink);
+               CVDisplayLinkRelease(displayLink);
+       }
+       [super dealloc];
+}
+
+//
+// Mac Director has its own thread
+//
+-(void) mainLoop
+{
+       while( ![[NSThread currentThread] isCancelled] ) {
+               // There is no autorelease pool when this method is called because it will be called from a background thread
+               // It's important to create one or you will leak objects
+               NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+               
+               [[NSRunLoop currentRunLoop] run];
+
+               [pool release];
+       }
+}
+               
+//
+// Draw the Scene
+//
+- (void) drawScene
+{    
+       // We draw on a secondary thread through the display link
+       // When resizing the view, -reshape is called automatically on the main thread
+       // Add a mutex around to avoid the threads accessing the context simultaneously when resizing
+       CGLLockContext([[openGLView_ openGLContext] CGLContextObj]);
+       [[openGLView_ openGLContext] makeCurrentContext];
+       
+       /* calculate "global" dt */
+       [self calculateDeltaTime];
+       
+       /* tick before glClear: issue #533 */
+       if( ! isPaused_ ) {
+               [[CCScheduler sharedScheduler] tick: dt];       
+       }
+       
+       glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+       
+       /* to avoid flickr, nextScene MUST be here: after tick and before draw.
+        XXX: Which bug is this one. It seems that it can't be reproduced with v0.9 */
+       if( nextScene_ )
+               [self setNextScene];
+       
+       glPushMatrix();
+       
+       
+       // By default enable VertexArray, ColorArray, TextureCoordArray and Texture2D
+       CC_ENABLE_DEFAULT_GL_STATES();
+       
+       /* draw the scene */
+       [runningScene_ visit];
+       
+       /* draw the notification node */
+       [notificationNode_ visit];
+
+       if( displayFPS_ )
+               [self showFPS];
+       
+#if CC_ENABLE_PROFILERS
+       [self showProfilers];
+#endif
+       
+       CC_DISABLE_DEFAULT_GL_STATES();
+       
+       glPopMatrix();
+                       
+       [[openGLView_ openGLContext] flushBuffer];      
+       CGLUnlockContext([[openGLView_ openGLContext] CGLContextObj]);
+}
+
+// set the event dispatcher
+-(void) setOpenGLView:(MacGLView *)view
+{
+       if( view != openGLView_ ) {
+               
+               [super setOpenGLView:view];
+                               
+               CCEventDispatcher *eventDispatcher = [CCEventDispatcher sharedDispatcher];
+               [openGLView_ setEventDelegate: eventDispatcher];
+               [eventDispatcher setDispatchEvents: YES];
+               
+               // Enable Touches. Default no.
+               [view setAcceptsTouchEvents:NO];
+//             [view setAcceptsTouchEvents:YES];
+               
+
+               // Synchronize buffer swaps with vertical refresh rate
+               [[view openGLContext] makeCurrentContext];
+               GLint swapInt = 1;
+               [[view openGLContext] setValues:&swapInt forParameter:NSOpenGLCPSwapInterval]; 
+       }
+}
+
+@end
+
+#endif // __MAC_OS_X_VERSION_MAX_ALLOWED
diff --git a/libs/cocos2d/Platforms/Mac/CCEventDispatcher.h b/libs/cocos2d/Platforms/Mac/CCEventDispatcher.h
new file mode 100644 (file)
index 0000000..06889e8
--- /dev/null
@@ -0,0 +1,277 @@
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+// Only compile this code on Mac. These files should not be included on your iOS project.
+// But in case they are included, it won't be compiled.
+#import <Availability.h>
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED)
+
+#import <Cocoa/Cocoa.h>
+
+#import "MacGLView.h"
+#import "../../Support/uthash.h"       // hack: uthash needs to be imported before utlist to prevent warning
+#import "../../Support/utlist.h"
+#import "../../ccConfig.h"
+
+#pragma mark -
+#pragma mark CCMouseEventDelegate
+
+/** CCMouseEventDelegate protocol.
+ Implement it in your node to receive any of mouse events
+ */
+@protocol CCMouseEventDelegate <NSObject>
+@optional
+
+//
+// left
+//
+/** called when the "mouseDown" event is received.
+ Return YES to avoid propagating the event to other delegates.
+ */
+-(BOOL) ccMouseDown:(NSEvent*)event;
+
+/** called when the "mouseDragged" event is received.
+ Return YES to avoid propagating the event to other delegates.
+ */
+-(BOOL) ccMouseDragged:(NSEvent*)event;
+
+/** called when the "mouseMoved" event is received.
+ Return YES to avoid propagating the event to other delegates.
+ By default, "mouseMoved" is disabled. To enable it, send the "setAcceptsMouseMovedEvents:YES" message to the main window.
+ */
+-(BOOL) ccMouseMoved:(NSEvent*)event;
+
+/** called when the "mouseUp" event is received.
+ Return YES to avoid propagating the event to other delegates.
+ */
+-(BOOL) ccMouseUp:(NSEvent*)event;
+
+
+//
+// right
+//
+
+/** called when the "rightMouseDown" event is received.
+ Return YES to avoid propagating the event to other delegates.
+ */
+-(BOOL) ccRightMouseDown:(NSEvent*)event;
+
+/** called when the "rightMouseDragged" event is received.
+ Return YES to avoid propagating the event to other delegates.
+ */
+-(BOOL) ccRightMouseDragged:(NSEvent*)event;
+
+/** called when the "rightMouseUp" event is received.
+ Return YES to avoid propagating the event to other delegates.
+ */
+-(BOOL) ccRightMouseUp:(NSEvent*)event;
+
+//
+// other
+//
+
+/** called when the "otherMouseDown" event is received.
+ Return YES to avoid propagating the event to other delegates.
+ */
+-(BOOL) ccOtherMouseDown:(NSEvent*)event;
+
+/** called when the "otherMouseDragged" event is received.
+ Return YES to avoid propagating the event to other delegates.
+ */
+-(BOOL) ccOtherMouseDragged:(NSEvent*)event;
+
+/** called when the "otherMouseUp" event is received.
+ Return YES to avoid propagating the event to other delegates.
+ */
+-(BOOL) ccOtherMouseUp:(NSEvent*)event;
+
+//
+// scroll wheel
+//
+
+/** called when the "scrollWheel" event is received.
+ Return YES to avoid propagating the event to other delegates.
+ */
+- (BOOL)ccScrollWheel:(NSEvent *)theEvent;
+
+
+//
+// enter / exit
+//
+
+/** called when the "mouseEntered" event is received.
+ Return YES to avoid propagating the event to other delegates.
+ */
+- (void)ccMouseEntered:(NSEvent *)theEvent;
+
+/** called when the "mouseExited" event is received.
+ Return YES to avoid propagating the event to other delegates.
+ */
+- (void)ccMouseExited:(NSEvent *)theEvent;
+
+@end
+
+#pragma mark -
+#pragma mark CCKeyboardEventDelegate
+
+/** CCKeyboardEventDelegate protocol.
+ Implement it in your node to receive any of keyboard events
+ */
+@protocol CCKeyboardEventDelegate <NSObject>
+@optional
+/** called when the "keyUp" event is received.
+ Return YES to avoid propagating the event to other delegates.
+ */
+-(BOOL) ccKeyUp:(NSEvent*)event;
+
+/** called when the "keyDown" event is received.
+ Return YES to avoid propagating the event to other delegates.
+ */
+-(BOOL) ccKeyDown:(NSEvent*)event;
+/** called when the "flagsChanged" event is received.
+ Return YES to avoid propagating the event to other delegates.
+ */
+-(BOOL) ccFlagsChanged:(NSEvent*)event;
+@end
+
+#pragma mark -
+#pragma mark CCTouchEventDelegate
+
+/** CCTouchEventDelegate protocol.
+ Implement it in your node to receive any of touch events
+ */
+@protocol CCTouchEventDelegate <NSObject>
+@optional
+/** called when the "touchesBegan" event is received.
+ Return YES to avoid propagating the event to other delegates.
+ */
+- (BOOL)ccTouchesBeganWithEvent:(NSEvent *)event;
+
+/** called when the "touchesMoved" event is received.
+ Return YES to avoid propagating the event to other delegates.
+ */
+- (BOOL)ccTouchesMovedWithEvent:(NSEvent *)event;
+
+/** called when the "touchesEnded" event is received.
+ Return YES to avoid propagating the event to other delegates.
+ */
+- (BOOL)ccTouchesEndedWithEvent:(NSEvent *)event;
+
+/** called when the "touchesCancelled" event is received.
+ Return YES to avoid propagating the event to other delegates.
+ */
+- (BOOL)ccTouchesCancelledWithEvent:(NSEvent *)event;
+
+@end
+
+
+#pragma mark -
+#pragma mark CCEventDispatcher
+
+struct _listEntry;
+
+/** CCEventDispatcher
+ This is object is responsible for dispatching the events:
+       - Mouse events
+       - Keyboard events
+       - Touch events
+ Only available on Mac
+ */
+@interface CCEventDispatcher : NSObject <MacEventDelegate> {
+
+       BOOL                                    dispatchEvents_;
+       
+       struct  _listEntry              *keyboardDelegates_;
+       struct  _listEntry              *mouseDelegates_;
+       struct  _listEntry              *touchDelegates_;
+}
+
+@property (nonatomic, readwrite) BOOL dispatchEvents;
+
+
+/** CCEventDispatcher singleton */
++(CCEventDispatcher*) sharedDispatcher;
+
+#pragma mark CCEventDispatcher - Mouse
+
+/** Adds a mouse delegate to the dispatcher's list.
+ Delegates with a lower priority value will be called before higher priority values.
+ All the events will be propgated to all the delegates, unless the one delegate returns YES.
+
+ IMPORTANT: The delegate will be retained.
+ */
+-(void) addMouseDelegate:(id<CCMouseEventDelegate>) delegate priority:(NSInteger)priority;
+
+/** removes a mouse delegate */
+-(void) removeMouseDelegate:(id) delegate;
+
+/** Removes all mouse delegates, releasing all the delegates */
+-(void) removeAllMouseDelegates;
+
+#pragma mark CCEventDispatcher - Keyboard
+
+/** Adds a Keyboard delegate to the dispatcher's list.
+ Delegates with a lower priority value will be called before higher priority values.
+ All the events will be propgated to all the delegates, unless the one delegate returns YES.
+ IMPORTANT: The delegate will be retained.
+ */
+-(void) addKeyboardDelegate:(id<CCKeyboardEventDelegate>) delegate priority:(NSInteger)priority;
+
+/** removes a mouse delegate */
+-(void) removeKeyboardDelegate:(id) delegate;
+
+/** Removes all mouse delegates, releasing all the delegates */
+-(void) removeAllKeyboardDelegates;
+
+#pragma mark CCEventDispatcher - Touches
+
+/** Adds a Touch delegate to the dispatcher's list.
+ Delegates with a lower priority value will be called before higher priority values.
+ All the events will be propgated to all the delegates, unless the one delegate returns YES.
+ IMPORTANT: The delegate will be retained.
+ */
+- (void)addTouchDelegate:(id<CCTouchEventDelegate>)delegate priority:(NSInteger)priority;
+
+/** Removes a touch delegate */
+- (void)removeTouchDelegate:(id) delegate;
+
+/** Removes all touch delegates, releasing all the delegates */
+- (void)removeAllTouchDelegates;
+
+#pragma mark CCEventDispatcher - Dispatch Events
+
+#if CC_DIRECTOR_MAC_USE_DISPLAY_LINK_THREAD
+-(void) dispatchQueuedEvents;
+#endif
+
+@end
+
+
+#endif // __MAC_OS_X_VERSION_MAX_ALLOWED
diff --git a/libs/cocos2d/Platforms/Mac/CCEventDispatcher.m b/libs/cocos2d/Platforms/Mac/CCEventDispatcher.m
new file mode 100644 (file)
index 0000000..f15cf1c
--- /dev/null
@@ -0,0 +1,645 @@
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+// Only compile this code on Mac. These files should not be included on your iOS project.
+// But in case they are included, it won't be compiled.
+#import <Availability.h>
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED)
+
+#import "CCEventDispatcher.h"
+#import "../../ccConfig.h"
+
+static CCEventDispatcher *sharedDispatcher = nil;
+
+enum  {
+       // mouse
+       kCCImplementsMouseDown                  = 1 << 0,
+       kCCImplementsMouseMoved                 = 1 << 1,
+       kCCImplementsMouseDragged               = 1 << 2,       
+       kCCImplementsMouseUp                    = 1 << 3,
+       kCCImplementsRightMouseDown             = 1 << 4,
+       kCCImplementsRightMouseDragged  = 1 << 5,
+       kCCImplementsRightMouseUp               = 1 << 6,
+       kCCImplementsOtherMouseDown             = 1 << 7,
+       kCCImplementsOtherMouseDragged  = 1 << 8,
+       kCCImplementsOtherMouseUp               = 1 << 9,
+       kCCImplementsScrollWheel                = 1 << 10,
+       kCCImplementsMouseEntered               = 1 << 11,
+       kCCImplementsMouseExited                = 1 << 12,
+
+       kCCImplementsTouchesBegan               = 1 << 13,
+       kCCImplementsTouchesMoved               = 1 << 14,
+       kCCImplementsTouchesEnded               = 1 << 15,
+       kCCImplementsTouchesCancelled           = 1 << 16,
+
+       // keyboard
+       kCCImplementsKeyUp                              = 1 << 0,
+       kCCImplementsKeyDown                    = 1 << 1,
+       kCCImplementsFlagsChanged               = 1 << 2,
+};
+
+
+typedef struct _listEntry
+{
+       struct  _listEntry      *prev, *next;
+       id                                      delegate;
+       NSInteger                       priority;
+       NSUInteger                      flags;
+} tListEntry;
+
+
+#if CC_DIRECTOR_MAC_USE_DISPLAY_LINK_THREAD
+
+#define QUEUE_EVENT_MAX 128
+struct _eventQueue {
+       SEL             selector;
+       NSEvent *event;
+};
+
+static struct  _eventQueue eventQueue[QUEUE_EVENT_MAX];
+static int             eventQueueCount;
+
+#endif // CC_DIRECTOR_MAC_USE_DISPLAY_LINK_THREAD
+
+
+@implementation CCEventDispatcher
+
+@synthesize dispatchEvents=dispatchEvents_;
+
+
++(CCEventDispatcher*) sharedDispatcher
+{
+       @synchronized(self) {
+               if (sharedDispatcher == nil)
+                       sharedDispatcher = [[self alloc] init]; // assignment not done here
+       }
+       return sharedDispatcher;
+}
+
++(id) allocWithZone:(NSZone *)zone
+{
+       @synchronized(self) {
+               NSAssert(sharedDispatcher == nil, @"Attempted to allocate a second instance of a singleton.");
+               return [super allocWithZone:zone];
+       }
+       return nil; // on subsequent allocation attempts return nil
+}
+
+-(id) init
+{
+       if( (self = [super init]) )
+       {
+               // events enabled by default
+               dispatchEvents_ = YES;
+
+               // delegates
+               keyboardDelegates_ = NULL;
+               mouseDelegates_ = NULL;
+                touchDelegates_ = NULL;
+               
+#if    CC_DIRECTOR_MAC_USE_DISPLAY_LINK_THREAD
+               eventQueueCount = 0;
+#endif
+       }
+       
+       return self;
+}
+
+- (void) dealloc
+{
+       [super dealloc];
+}
+
+#pragma mark CCEventDispatcher - add / remove delegates
+
+-(void) addDelegate:(id)delegate priority:(NSInteger)priority flags:(NSUInteger)flags list:(tListEntry**)list
+{
+       tListEntry *listElement = malloc( sizeof(*listElement) );
+       
+       listElement->delegate = [delegate retain];
+       listElement->priority = priority;
+       listElement->flags = flags;
+       listElement->next = listElement->prev = NULL;
+       
+       // empty list ?
+       if( ! *list ) {
+               DL_APPEND( *list, listElement );
+               
+       } else {
+               BOOL added = NO;                
+               
+               for( tListEntry *elem = *list; elem ; elem = elem->next ) {
+                       if( priority < elem->priority ) {
+                               
+                               if( elem == *list )
+                                       DL_PREPEND(*list, listElement);
+                               else {
+                                       listElement->next = elem;
+                                       listElement->prev = elem->prev;
+                                       
+                                       elem->prev->next = listElement;
+                                       elem->prev = listElement;
+                               }
+                               
+                               added = YES;
+                               break;
+                       }
+               }
+               
+               // Not added? priority has the higher value. Append it.
+               if( !added )
+                       DL_APPEND(*list, listElement);
+       }
+}
+
+-(void) removeDelegate:(id)delegate fromList:(tListEntry**)list
+{
+       tListEntry *entry, *tmp;
+       
+       // updates with priority < 0
+       DL_FOREACH_SAFE( *list, entry, tmp ) {
+               if( entry->delegate == delegate ) {
+                       DL_DELETE( *list, entry );
+                       [delegate release];
+                       free(entry);
+                       break;
+               }
+       }
+}
+
+-(void) removeAllDelegatesFromList:(tListEntry**)list
+{
+       tListEntry *entry, *tmp;
+
+       DL_FOREACH_SAFE( *list, entry, tmp ) {
+               DL_DELETE( *list, entry );
+               free(entry);
+       }
+}
+
+
+-(void) addMouseDelegate:(id<CCMouseEventDelegate>) delegate priority:(NSInteger)priority
+{
+       NSUInteger flags = 0;
+       
+       flags |= ( [delegate respondsToSelector:@selector(ccMouseDown:)] ? kCCImplementsMouseDown : 0 );
+       flags |= ( [delegate respondsToSelector:@selector(ccMouseDragged:)] ? kCCImplementsMouseDragged : 0 );
+       flags |= ( [delegate respondsToSelector:@selector(ccMouseMoved:)] ? kCCImplementsMouseMoved : 0 );
+       flags |= ( [delegate respondsToSelector:@selector(ccMouseUp:)] ? kCCImplementsMouseUp : 0 );
+
+       flags |= ( [delegate respondsToSelector:@selector(ccRightMouseDown:)] ? kCCImplementsRightMouseDown : 0 );
+       flags |= ( [delegate respondsToSelector:@selector(ccRightMouseDragged:)] ? kCCImplementsRightMouseDragged : 0 );
+       flags |= ( [delegate respondsToSelector:@selector(ccRightMouseUp:)] ? kCCImplementsRightMouseUp : 0 );
+
+       flags |= ( [delegate respondsToSelector:@selector(ccOtherMouseDown:)] ? kCCImplementsOtherMouseDown : 0 );
+       flags |= ( [delegate respondsToSelector:@selector(ccOtherMouseDragged:)] ? kCCImplementsOtherMouseDragged : 0 );
+       flags |= ( [delegate respondsToSelector:@selector(ccOtherMouseUp:)] ? kCCImplementsOtherMouseUp : 0 );
+
+       flags |= ( [delegate respondsToSelector:@selector(ccMouseEntered:)] ? kCCImplementsMouseEntered : 0 );
+       flags |= ( [delegate respondsToSelector:@selector(ccMouseExited:)] ? kCCImplementsMouseExited : 0 );
+
+       flags |= ( [delegate respondsToSelector:@selector(ccScrollWheel:)] ? kCCImplementsScrollWheel : 0 );
+
+       [self addDelegate:delegate priority:priority flags:flags list:&mouseDelegates_];
+}
+
+-(void) removeMouseDelegate:(id) delegate
+{
+       [self removeDelegate:delegate fromList:&mouseDelegates_];
+}
+
+-(void) removeAllMouseDelegates
+{
+       [self removeAllDelegatesFromList:&mouseDelegates_];
+}
+
+-(void) addKeyboardDelegate:(id<CCKeyboardEventDelegate>) delegate priority:(NSInteger)priority
+{
+       NSUInteger flags = 0;
+       
+       flags |= ( [delegate respondsToSelector:@selector(ccKeyUp:)] ? kCCImplementsKeyUp : 0 );
+       flags |= ( [delegate respondsToSelector:@selector(ccKeyDown:)] ? kCCImplementsKeyDown : 0 );
+       flags |= ( [delegate respondsToSelector:@selector(ccFlagsChanged:)] ? kCCImplementsFlagsChanged : 0 );
+       
+       [self addDelegate:delegate priority:priority flags:flags list:&keyboardDelegates_];
+}
+
+-(void) removeKeyboardDelegate:(id) delegate
+{
+       [self removeDelegate:delegate fromList:&keyboardDelegates_];
+}
+
+-(void) removeAllKeyboardDelegates
+{
+       [self removeAllDelegatesFromList:&keyboardDelegates_];
+}
+
+-(void) addTouchDelegate:(id<CCTouchEventDelegate>) delegate priority:(NSInteger)priority
+{
+       NSUInteger flags = 0;
+       
+       flags |= ( [delegate respondsToSelector:@selector(ccTouchesBeganWithEvent:)] ? kCCImplementsTouchesBegan : 0 );
+       flags |= ( [delegate respondsToSelector:@selector(ccTouchesMovedWithEvent:)] ? kCCImplementsTouchesMoved : 0 );
+       flags |= ( [delegate respondsToSelector:@selector(ccTouchesEndedWithEvent:)] ? kCCImplementsTouchesEnded : 0 );
+       flags |= ( [delegate respondsToSelector:@selector(ccTouchesCancelledWithEvent:)] ? kCCImplementsTouchesCancelled : 0 );
+       
+       [self addDelegate:delegate priority:priority flags:flags list:&touchDelegates_];
+}
+
+-(void) removeTouchDelegate:(id) delegate
+{
+       [self removeDelegate:delegate fromList:&touchDelegates_];
+}
+
+-(void) removeAllTouchDelegates
+{
+       [self removeAllDelegatesFromList:&touchDelegates_];
+}
+
+
+#pragma mark CCEventDispatcher - Mouse events
+//
+// Mouse events
+//
+
+//
+// Left
+//
+- (void)mouseDown:(NSEvent *)event
+{
+       if( dispatchEvents_ ) {
+               tListEntry *entry, *tmp;
+
+               DL_FOREACH_SAFE( mouseDelegates_, entry, tmp ) {
+                       if ( entry->flags & kCCImplementsMouseDown ) {
+                               void *swallows = [entry->delegate performSelector:@selector(ccMouseDown:) withObject:event];
+                               if( swallows )
+                                       break;
+                       }
+               }
+       }
+}
+
+- (void)mouseMoved:(NSEvent *)event
+{
+       if( dispatchEvents_ ) {
+               tListEntry *entry, *tmp;
+               
+               DL_FOREACH_SAFE( mouseDelegates_, entry, tmp ) {
+                       if ( entry->flags & kCCImplementsMouseMoved ) {
+                               void *swallows = [entry->delegate performSelector:@selector(ccMouseMoved:) withObject:event];
+                               if( swallows )
+                                       break;
+                       }
+               }
+       }
+}
+
+- (void)mouseDragged:(NSEvent *)event
+{
+       if( dispatchEvents_ ) {
+               tListEntry *entry, *tmp;
+               
+               DL_FOREACH_SAFE( mouseDelegates_, entry, tmp ) {
+                       if ( entry->flags & kCCImplementsMouseDragged ) {
+                               void *swallows = [entry->delegate performSelector:@selector(ccMouseDragged:) withObject:event];
+                               if( swallows )
+                                       break;
+                       }
+               }
+       }
+}
+
+- (void)mouseUp:(NSEvent *)event
+{
+       if( dispatchEvents_ ) {
+               tListEntry *entry, *tmp;
+               
+               DL_FOREACH_SAFE( mouseDelegates_, entry, tmp ) {
+                       if ( entry->flags & kCCImplementsMouseUp ) {
+                               void *swallows = [entry->delegate performSelector:@selector(ccMouseUp:) withObject:event];
+                               if( swallows )
+                                       break;
+                       }
+               }
+       }
+}
+
+//
+// Mouse Right
+//
+- (void)rightMouseDown:(NSEvent *)event
+{
+       if( dispatchEvents_ ) {
+               tListEntry *entry, *tmp;
+               
+               DL_FOREACH_SAFE( mouseDelegates_, entry, tmp ) {
+                       if ( entry->flags & kCCImplementsRightMouseDown ) {
+                               void *swallows = [entry->delegate performSelector:@selector(ccRightMouseDown:) withObject:event];
+                               if( swallows )
+                                       break;
+                       }
+               }
+       }
+}
+
+- (void)rightMouseDragged:(NSEvent *)event
+{
+       if( dispatchEvents_ ) {
+               tListEntry *entry, *tmp;
+               
+               DL_FOREACH_SAFE( mouseDelegates_, entry, tmp ) {
+                       if ( entry->flags & kCCImplementsRightMouseDragged ) {
+                               void *swallows = [entry->delegate performSelector:@selector(ccRightMouseDragged:) withObject:event];
+                               if( swallows )
+                                       break;
+                       }
+               }
+       }
+}
+
+- (void)rightMouseUp:(NSEvent *)event
+{
+       if( dispatchEvents_ ) {
+               tListEntry *entry, *tmp;
+               
+               DL_FOREACH_SAFE( mouseDelegates_, entry, tmp ) {
+                       if ( entry->flags & kCCImplementsRightMouseUp ) {
+                               void *swallows = [entry->delegate performSelector:@selector(ccRightMouseUp:) withObject:event];
+                               if( swallows )
+                                       break;
+                       }
+               }
+       }
+}
+
+//
+// Mouse Other
+//
+- (void)otherMouseDown:(NSEvent *)event
+{
+       if( dispatchEvents_ ) {
+               tListEntry *entry, *tmp;
+               
+               DL_FOREACH_SAFE( mouseDelegates_, entry, tmp ) {
+                       if ( entry->flags & kCCImplementsOtherMouseDown ) {
+                               void *swallows = [entry->delegate performSelector:@selector(ccOtherMouseDown:) withObject:event];
+                               if( swallows )
+                                       break;
+                       }
+               }
+       }
+}
+
+- (void)otherMouseDragged:(NSEvent *)event
+{
+       if( dispatchEvents_ ) {
+               tListEntry *entry, *tmp;
+               
+               DL_FOREACH_SAFE( mouseDelegates_, entry, tmp ) {
+                       if ( entry->flags & kCCImplementsOtherMouseDragged ) {
+                               void *swallows = [entry->delegate performSelector:@selector(ccOtherMouseDragged:) withObject:event];
+                               if( swallows )
+                                       break;
+                       }
+               }
+       }
+}
+
+- (void)otherMouseUp:(NSEvent *)event
+{
+       if( dispatchEvents_ ) {
+               tListEntry *entry, *tmp;
+               
+               DL_FOREACH_SAFE( mouseDelegates_, entry, tmp ) {
+                       if ( entry->flags & kCCImplementsOtherMouseUp ) {
+                               void *swallows = [entry->delegate performSelector:@selector(ccOtherMouseUp:) withObject:event];
+                               if( swallows )
+                                       break;
+                       }
+               }
+       }
+}
+
+//
+// Scroll Wheel
+//
+- (void)scrollWheel:(NSEvent *)event
+{
+       if( dispatchEvents_ ) {
+               tListEntry *entry, *tmp;
+               
+               DL_FOREACH_SAFE( mouseDelegates_, entry, tmp ) {
+                       if ( entry->flags & kCCImplementsScrollWheel ) {
+                               void *swallows = [entry->delegate performSelector:@selector(ccScrollWheel:) withObject:event];
+                               if( swallows )
+                                       break;
+                       }
+               }
+       }
+}
+
+//
+// Mouse enter / exit
+- (void)mouseExited:(NSEvent *)event
+{
+       if( dispatchEvents_ ) {
+               tListEntry *entry, *tmp;
+               
+               DL_FOREACH_SAFE( mouseDelegates_, entry, tmp ) {
+                       if ( entry->flags & kCCImplementsMouseEntered ) {
+                               void *swallows = [entry->delegate performSelector:@selector(ccMouseEntered:) withObject:event];
+                               if( swallows )
+                                       break;
+                       }
+               }
+       }       
+}
+
+- (void)mouseEntered:(NSEvent *)event
+{
+       if( dispatchEvents_ ) {
+               tListEntry *entry, *tmp;
+               
+               DL_FOREACH_SAFE( mouseDelegates_, entry, tmp ) {
+                       if ( entry->flags & kCCImplementsMouseExited) {
+                               void *swallows = [entry->delegate performSelector:@selector(ccMouseExited:) withObject:event];
+                               if( swallows )
+                                       break;
+                       }
+               }
+       }       
+}
+
+
+#pragma mark CCEventDispatcher - Keyboard events
+
+// Keyboard events
+- (void)keyDown:(NSEvent *)event
+{
+       if( dispatchEvents_ ) {
+               tListEntry *entry, *tmp;
+               
+               DL_FOREACH_SAFE( keyboardDelegates_, entry, tmp ) {
+                       if ( entry->flags & kCCImplementsKeyDown ) {
+                               void *swallows = [entry->delegate performSelector:@selector(ccKeyDown:) withObject:event];
+                               if( swallows )
+                                       break;
+                       }
+               }
+       }
+}
+
+- (void)keyUp:(NSEvent *)event
+{
+       if( dispatchEvents_ ) {
+               tListEntry *entry, *tmp;
+               
+               DL_FOREACH_SAFE( keyboardDelegates_, entry, tmp ) {
+                       if ( entry->flags & kCCImplementsKeyUp ) {
+                               void *swallows = [entry->delegate performSelector:@selector(ccKeyUp:) withObject:event];
+                               if( swallows )
+                                       break;
+                       }
+               }
+       }
+}
+
+- (void)flagsChanged:(NSEvent *)event
+{
+       if( dispatchEvents_ ) {
+               tListEntry *entry, *tmp;
+               
+               DL_FOREACH_SAFE( keyboardDelegates_, entry, tmp ) {
+                       if ( entry->flags & kCCImplementsFlagsChanged ) {
+                               void *swallows = [entry->delegate performSelector:@selector(ccFlagsChanged:) withObject:event];
+                               if( swallows )
+                                       break;
+                       }
+               }
+       }
+}
+
+
+#pragma mark CCEventDispatcher - Touch events
+
+- (void)touchesBeganWithEvent:(NSEvent *)event
+{
+       if( dispatchEvents_ ) {
+               tListEntry *entry, *tmp;
+               
+               DL_FOREACH_SAFE( touchDelegates_, entry, tmp ) {
+                       if ( entry->flags & kCCImplementsTouchesBegan) {
+                               void *swallows = [entry->delegate performSelector:@selector(ccTouchesBeganWithEvent:) withObject:event];
+                               if( swallows )
+                                       break;
+                       }
+               }
+       }       
+}
+
+- (void)touchesMovedWithEvent:(NSEvent *)event
+{
+       if( dispatchEvents_ ) {
+               tListEntry *entry, *tmp;
+               
+               DL_FOREACH_SAFE( touchDelegates_, entry, tmp ) {
+                       if ( entry->flags & kCCImplementsTouchesMoved) {
+                               void *swallows = [entry->delegate performSelector:@selector(ccTouchesMovedWithEvent:) withObject:event];
+                               if( swallows )
+                                       break;
+                       }
+               }
+       }       
+}
+
+- (void)touchesEndedWithEvent:(NSEvent *)event
+{
+       if( dispatchEvents_ ) {
+               tListEntry *entry, *tmp;
+               
+               DL_FOREACH_SAFE( touchDelegates_, entry, tmp ) {
+                       if ( entry->flags & kCCImplementsTouchesEnded) {
+                               void *swallows = [entry->delegate performSelector:@selector(ccTouchesEndedWithEvent:) withObject:event];
+                               if( swallows )
+                                       break;
+                       }
+               }
+       }       
+}
+
+- (void)touchesCancelledWithEvent:(NSEvent *)event
+{
+       if( dispatchEvents_ ) {
+               tListEntry *entry, *tmp;
+               
+               DL_FOREACH_SAFE( touchDelegates_, entry, tmp ) {
+                       if ( entry->flags & kCCImplementsTouchesCancelled) {
+                               void *swallows = [entry->delegate performSelector:@selector(ccTouchesCancelledWithEvent:) withObject:event];
+                               if( swallows )
+                                       break;
+                       }
+               }
+       }       
+}
+
+
+#pragma mark CCEventDispatcher - queue events
+
+#if CC_DIRECTOR_MAC_USE_DISPLAY_LINK_THREAD
+-(void) queueEvent:(NSEvent*)event selector:(SEL)selector
+{
+       NSAssert( eventQueueCount < QUEUE_EVENT_MAX, @"CCEventDispatcher: recompile. Increment QUEUE_EVENT_MAX value");
+
+       @synchronized (self) {
+               eventQueue[eventQueueCount].selector = selector;
+               eventQueue[eventQueueCount].event = [event copy];
+               
+               eventQueueCount++;
+       }
+}
+
+-(void) dispatchQueuedEvents
+{
+       @synchronized (self) {
+               for( int i=0; i < eventQueueCount; i++ ) {
+                       SEL sel = eventQueue[i].selector;
+                       NSEvent *event = eventQueue[i].event;
+                       
+                       [self performSelector:sel withObject:event];
+                       
+                       [event release];
+               }
+               
+               eventQueueCount = 0;
+       }
+}
+#endif // CC_DIRECTOR_MAC_USE_DISPLAY_LINK_THREAD
+
+
+@end
+
+#endif // __MAC_OS_X_VERSION_MAX_ALLOWED
\ No newline at end of file
diff --git a/libs/cocos2d/Platforms/Mac/MacGLView.h b/libs/cocos2d/Platforms/Mac/MacGLView.h
new file mode 100644 (file)
index 0000000..8099273
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+// Only compile this code on Mac. These files should not be included on your iOS project.
+// But in case they are included, it won't be compiled.
+#import <Availability.h>
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED)
+
+#import <Cocoa/Cocoa.h>
+
+#import "../../ccConfig.h"
+
+//PROTOCOLS:
+
+@protocol MacEventDelegate <NSObject>
+// Mouse
+- (void)mouseDown:(NSEvent *)theEvent;
+- (void)mouseUp:(NSEvent *)theEvent;
+- (void)mouseMoved:(NSEvent *)theEvent;
+- (void)mouseDragged:(NSEvent *)theEvent;
+- (void)rightMouseDown:(NSEvent*)event;
+- (void)rightMouseDragged:(NSEvent*)event;
+- (void)rightMouseUp:(NSEvent*)event;
+- (void)otherMouseDown:(NSEvent*)event;
+- (void)otherMouseDragged:(NSEvent*)event;
+- (void)otherMouseUp:(NSEvent*)event;
+- (void)scrollWheel:(NSEvent *)theEvent;
+- (void)mouseEntered:(NSEvent *)theEvent;
+- (void)mouseExited:(NSEvent *)theEvent;
+
+
+// Keyboard
+- (void)keyDown:(NSEvent *)theEvent;
+- (void)keyUp:(NSEvent *)theEvent;
+- (void)flagsChanged:(NSEvent *)theEvent;
+
+// Touches
+- (void)touchesBeganWithEvent:(NSEvent *)event;
+- (void)touchesMovedWithEvent:(NSEvent *)event;
+- (void)touchesEndedWithEvent:(NSEvent *)event;
+- (void)touchesCancelledWithEvent:(NSEvent *)event;
+
+#if CC_DIRECTOR_MAC_USE_DISPLAY_LINK_THREAD
+- (void)queueEvent:(NSEvent*)event selector:(SEL)selector;
+#endif
+
+@end
+
+/** MacGLView
+ Only available for Mac OS X
+ */
+@interface MacGLView : NSOpenGLView {
+       id<MacEventDelegate> eventDelegate_;
+}
+
+@property (nonatomic, readwrite, assign) id<MacEventDelegate> eventDelegate;
+
+// initializes the MacGLView with a frame rect and an OpenGL context
+- (id) initWithFrame:(NSRect)frameRect shareContext:(NSOpenGLContext*)context;
+
+// private
++(void) load_;
+@end
+
+#endif // __MAC_OS_X_VERSION_MAX_ALLOWED
\ No newline at end of file
diff --git a/libs/cocos2d/Platforms/Mac/MacGLView.m b/libs/cocos2d/Platforms/Mac/MacGLView.m
new file mode 100644 (file)
index 0000000..d305791
--- /dev/null
@@ -0,0 +1,242 @@
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+/*
+ * Idea of subclassing NSOpenGLView was taken from  "TextureUpload" Apple's sample
+ */
+
+// Only compile this code on Mac. These files should not be included on your iOS project.
+// But in case they are included, it won't be compiled.
+#import <Availability.h>
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED)
+
+#import "MacGLView.h"
+#import <OpenGL/gl.h>
+
+#import "CCDirectorMac.h"
+#import "../../ccConfig.h"
+
+
+@implementation MacGLView
+
+@synthesize eventDelegate = eventDelegate_;
+
++(void) load_
+{
+       NSLog(@"%@ loaded", self);
+}
+
+- (id) initWithFrame:(NSRect)frameRect
+{
+       self = [self initWithFrame:frameRect shareContext:nil];
+       return self;
+}
+
+- (id) initWithFrame:(NSRect)frameRect shareContext:(NSOpenGLContext*)context
+{
+    NSOpenGLPixelFormatAttribute attribs[] =
+    {
+               NSOpenGLPFAAccelerated,
+               NSOpenGLPFANoRecovery,
+               NSOpenGLPFADoubleBuffer,
+               NSOpenGLPFADepthSize, 24,
+               
+               0
+    };
+       
+       NSOpenGLPixelFormat *pixelFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:attribs];
+       
+       if (!pixelFormat)
+               NSLog(@"No OpenGL pixel format");
+       
+       if( (self = [super initWithFrame:frameRect pixelFormat:[pixelFormat autorelease]]) ) {
+               
+               if( context )
+                       [self setOpenGLContext:context];
+
+               // Synchronize buffer swaps with vertical refresh rate
+               GLint swapInt = 1;
+               [[self openGLContext] setValues:&swapInt forParameter:NSOpenGLCPSwapInterval]; 
+               
+//             GLint order = -1;
+//             [[self openGLContext] setValues:&order forParameter:NSOpenGLCPSurfaceOrder];
+               
+               // event delegate
+               eventDelegate_ = nil;           
+       }
+       
+       return self;
+}
+       
+- (void) reshape
+{
+       // We draw on a secondary thread through the display link
+       // When resizing the view, -reshape is called automatically on the main thread
+       // Add a mutex around to avoid the threads accessing the context simultaneously when resizing
+       CGLLockContext([[self openGLContext] CGLContextObj]);
+       
+       NSRect rect = [self bounds];
+       
+       CCDirector *director = [CCDirector sharedDirector];
+       [director reshapeProjection: NSSizeToCGSize(rect.size) ];
+       
+       // avoid flicker
+       [director drawScene];
+//     [self setNeedsDisplay:YES];
+       
+       CGLUnlockContext([[self openGLContext] CGLContextObj]);
+}
+
+- (void) dealloc
+{      
+
+       [super dealloc];
+}
+
+#if CC_DIRECTOR_MAC_USE_DISPLAY_LINK_THREAD
+#define DISPATCH_EVENT(__event__, __selector__) [eventDelegate_ queueEvent:__event__ selector:__selector__];
+#else
+#define DISPATCH_EVENT(__event__, __selector__)                                                                                                \
+       id obj = eventDelegate_;                                                                                                                                \
+       [obj performSelector:__selector__                                                                                                               \
+                       onThread:[(CCDirectorMac*)[CCDirector sharedDirector] runningThread]                    \
+                 withObject:__event__                                                                                                                          \
+          waitUntilDone:NO];
+#endif
+
+#pragma mark MacGLView - Mouse events
+- (void)mouseDown:(NSEvent *)theEvent
+{
+       DISPATCH_EVENT(theEvent, _cmd);
+}
+
+- (void)mouseMoved:(NSEvent *)theEvent
+{
+       DISPATCH_EVENT(theEvent, _cmd);
+}
+
+- (void)mouseDragged:(NSEvent *)theEvent
+{
+       DISPATCH_EVENT(theEvent, _cmd);
+}
+
+- (void)mouseUp:(NSEvent *)theEvent
+{
+       DISPATCH_EVENT(theEvent, _cmd);
+}
+
+- (void)rightMouseDown:(NSEvent *)theEvent {
+       DISPATCH_EVENT(theEvent, _cmd);
+}
+
+- (void)rightMouseDragged:(NSEvent *)theEvent {
+       DISPATCH_EVENT(theEvent, _cmd);
+}
+
+- (void)rightMouseUp:(NSEvent *)theEvent {
+       DISPATCH_EVENT(theEvent, _cmd);
+}
+
+- (void)otherMouseDown:(NSEvent *)theEvent {
+       DISPATCH_EVENT(theEvent, _cmd);
+}
+
+- (void)otherMouseDragged:(NSEvent *)theEvent {
+       DISPATCH_EVENT(theEvent, _cmd);
+}
+
+- (void)otherMouseUp:(NSEvent *)theEvent {
+       DISPATCH_EVENT(theEvent, _cmd);
+}
+
+- (void)mouseEntered:(NSEvent *)theEvent {
+       DISPATCH_EVENT(theEvent, _cmd);
+}
+
+- (void)mouseExited:(NSEvent *)theEvent {
+       DISPATCH_EVENT(theEvent, _cmd);
+}
+
+-(void) scrollWheel:(NSEvent *)theEvent {
+       DISPATCH_EVENT(theEvent, _cmd);
+}
+
+#pragma mark MacGLView - Key events
+
+-(BOOL) becomeFirstResponder
+{
+       return YES;
+}
+
+-(BOOL) acceptsFirstResponder
+{
+       return YES;
+}
+
+-(BOOL) resignFirstResponder
+{
+       return YES;
+}
+
+- (void)keyDown:(NSEvent *)theEvent
+{
+       DISPATCH_EVENT(theEvent, _cmd);
+}
+
+- (void)keyUp:(NSEvent *)theEvent
+{
+       DISPATCH_EVENT(theEvent, _cmd);
+}
+
+- (void)flagsChanged:(NSEvent *)theEvent
+{
+       DISPATCH_EVENT(theEvent, _cmd);
+}
+
+#pragma mark MacGLView - Touch events
+- (void)touchesBeganWithEvent:(NSEvent *)theEvent
+{
+       DISPATCH_EVENT(theEvent, _cmd);
+}
+
+- (void)touchesMovedWithEvent:(NSEvent *)theEvent
+{
+       DISPATCH_EVENT(theEvent, _cmd);
+}
+
+- (void)touchesEndedWithEvent:(NSEvent *)theEvent
+{
+       DISPATCH_EVENT(theEvent, _cmd);
+}
+
+- (void)touchesCancelledWithEvent:(NSEvent *)theEvent
+{
+       DISPATCH_EVENT(theEvent, _cmd);
+}
+
+@end
+
+#endif // __MAC_OS_X_VERSION_MAX_ALLOWED
\ No newline at end of file
diff --git a/libs/cocos2d/Platforms/Mac/MacWindow.h b/libs/cocos2d/Platforms/Mac/MacWindow.h
new file mode 100644 (file)
index 0000000..716fe9b
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2010 Ricardo Quesada
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+// Only compile this code on Mac. These files should not be included on your iOS project.
+// But in case they are included, it won't be compiled.
+#import <Availability.h>
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED)
+
+#import <Cocoa/Cocoa.h>
+
+
+@interface MacWindow : NSWindow
+{
+}
+- (id) initWithFrame:(NSRect)frame fullscreen:(BOOL)fullscreen;
+
+@end
+
+
+#endif // __MAC_OS_X_VERSION_MAX_ALLOWED
\ No newline at end of file
diff --git a/libs/cocos2d/Platforms/Mac/MacWindow.m b/libs/cocos2d/Platforms/Mac/MacWindow.m
new file mode 100644 (file)
index 0000000..28736a3
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2010 Ricardo Quesada
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+// Only compile this code on Mac. These files should not be included on your iOS project.
+// But in case they are included, it won't be compiled.
+#import <Availability.h>
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED)
+
+#import "MacWindow.h"
+
+
+@implementation MacWindow
+
+- (id) initWithFrame:(NSRect)frame fullscreen:(BOOL)fullscreen
+{
+       int styleMask = fullscreen ? NSBackingStoreBuffered : ( NSTitledWindowMask | NSClosableWindowMask );
+       self = [self initWithContentRect:frame
+                                                  styleMask:styleMask
+                                                        backing:NSBackingStoreBuffered
+                                                          defer:YES];
+       
+       if (self != nil)
+       {
+               if(fullscreen)
+               {
+                       [self setLevel:NSMainMenuWindowLevel+1];
+                       [self setHidesOnDeactivate:YES];
+                       [self setHasShadow:NO];
+               }
+               
+               [self setAcceptsMouseMovedEvents:NO];
+               [self setOpaque:YES];
+       }
+       return self;
+}
+
+- (BOOL) canBecomeKeyWindow
+{
+       return YES;
+}
+
+- (BOOL) canBecomeMainWindow
+{
+       return YES;
+}
+@end
+
+#endif // __MAC_OS_X_VERSION_MAX_ALLOWED
diff --git a/libs/cocos2d/Platforms/iOS/CCDirectorIOS.h b/libs/cocos2d/Platforms/iOS/CCDirectorIOS.h
new file mode 100644 (file)
index 0000000..1c264e4
--- /dev/null
@@ -0,0 +1,255 @@
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2008-2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+
+// Only compile this code on iOS. These files should NOT be included on your Mac project.
+// But in case they are included, it won't be compiled.
+#import <Availability.h>
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+
+#import "../../CCDirector.h"
+
+/** @typedef ccDeviceOrientation
+ Possible device orientations
+ */
+typedef enum {
+       /// Device oriented vertically, home button on the bottom
+       kCCDeviceOrientationPortrait = UIDeviceOrientationPortrait,     
+       /// Device oriented vertically, home button on the top
+    kCCDeviceOrientationPortraitUpsideDown = UIDeviceOrientationPortraitUpsideDown,
+       /// Device oriented horizontally, home button on the right
+    kCCDeviceOrientationLandscapeLeft = UIDeviceOrientationLandscapeLeft,
+       /// Device oriented horizontally, home button on the left
+    kCCDeviceOrientationLandscapeRight = UIDeviceOrientationLandscapeRight,
+       
+       // Backward compatibility stuff
+       CCDeviceOrientationPortrait = kCCDeviceOrientationPortrait,
+       CCDeviceOrientationPortraitUpsideDown = kCCDeviceOrientationPortraitUpsideDown,
+       CCDeviceOrientationLandscapeLeft = kCCDeviceOrientationLandscapeLeft,
+       CCDeviceOrientationLandscapeRight = kCCDeviceOrientationLandscapeRight,
+} ccDeviceOrientation;
+
+/** @typedef ccDirectorType
+ Possible Director Types.
+ @since v0.8.2
+ */
+typedef enum {
+       /** Will use a Director that triggers the main loop from an NSTimer object
+        *
+        * Features and Limitations:
+        * - Integrates OK with UIKit objects
+        * - It the slowest director
+        * - The invertal update is customizable from 1 to 60
+        */
+       kCCDirectorTypeNSTimer,
+       
+       /** will use a Director that triggers the main loop from a custom main loop.
+        *
+        * Features and Limitations:
+        * - Faster than NSTimer Director
+        * - It doesn't integrate well with UIKit objecgts
+        * - The interval update can't be customizable
+        */
+       kCCDirectorTypeMainLoop,
+       
+       /** Will use a Director that triggers the main loop from a thread, but the main loop will be executed on the main thread.
+        *
+        * Features and Limitations:
+        * - Faster than NSTimer Director
+        * - It doesn't integrate well with UIKit objecgts
+        * - The interval update can't be customizable
+        */
+       kCCDirectorTypeThreadMainLoop,
+       
+       /** Will use a Director that synchronizes timers with the refresh rate of the display.
+        *
+        * Features and Limitations:
+        * - Faster than NSTimer Director
+        * - Only available on 3.1+
+        * - Scheduled timers & drawing are synchronizes with the refresh rate of the display
+        * - Integrates OK with UIKit objects
+        * - The interval update can be 1/60, 1/30, 1/15
+        */     
+       kCCDirectorTypeDisplayLink,
+       
+       /** Default director is the NSTimer directory */
+       kCCDirectorTypeDefault = kCCDirectorTypeNSTimer,
+       
+       // backward compatibility stuff
+       CCDirectorTypeNSTimer = kCCDirectorTypeNSTimer,
+       CCDirectorTypeMainLoop = kCCDirectorTypeMainLoop,
+       CCDirectorTypeThreadMainLoop = kCCDirectorTypeThreadMainLoop,
+       CCDirectorTypeDisplayLink = kCCDirectorTypeDisplayLink,
+       CCDirectorTypeDefault = kCCDirectorTypeDefault,
+       
+       
+} ccDirectorType;
+
+/** CCDirector extensions for iPhone
+ */
+@interface CCDirector (iOSExtension)
+
+// rotates the screen if an orientation differnent than Portrait is used
+-(void) applyOrientation;
+
+/** Sets the device orientation.
+ If the orientation is going to be controlled by an UIViewController, then the orientation should be Portrait
+ */
+-(void) setDeviceOrientation:(ccDeviceOrientation)orientation;
+
+/** returns the device orientation */
+-(ccDeviceOrientation) deviceOrientation;
+
+/** The size in pixels of the surface. It could be different than the screen size.
+ High-res devices might have a higher surface size than the screen size.
+ In non High-res device the contentScale will be emulated.
+
+ The recommend way to enable Retina Display is by using the "enableRetinaDisplay:(BOOL)enabled" method.
+
+ @since v0.99.4
+ */
+-(void) setContentScaleFactor:(CGFloat)scaleFactor;
+
+/** Will enable Retina Display on devices that supports it.
+ It will enable Retina Display on iPhone4 and iPod Touch 4.
+ It will return YES, if it could enabled it, otherwise it will return NO.
+ This is the recommened way to enable Retina Display.
+ @since v0.99.5
+ */
+-(BOOL) enableRetinaDisplay:(BOOL)yes;
+
+
+/** returns the content scale factor */
+-(CGFloat) contentScaleFactor;
+@end
+
+@interface CCDirector (iOSExtensionClassMethods)
+
+/** There are 4 types of Director.
+ - kCCDirectorTypeNSTimer (default)
+ - kCCDirectorTypeMainLoop
+ - kCCDirectorTypeThreadMainLoop
+ - kCCDirectorTypeDisplayLink
+ Each Director has it's own benefits, limitations.
+ If you are using SDK 3.1 or newer it is recommed to use the DisplayLink director
+ This method should be called before any other call to the director.
+ It will return NO if the director type is kCCDirectorTypeDisplayLink and the running SDK is < 3.1. Otherwise it will return YES.
+ @since v0.8.2
+ */
++(BOOL) setDirectorType:(ccDirectorType) directorType;
+@end
+
+#pragma mark -
+#pragma mark CCDirectorIOS
+
+/** CCDirectorIOS: Base class of iOS directors
+ @since v0.99.5
+ */
+@interface CCDirectorIOS : CCDirector
+{
+       /* orientation */
+       ccDeviceOrientation     deviceOrientation_;
+       
+       /* contentScaleFactor could be simulated */
+       BOOL    isContentScaleSupported_;
+       
+}
+@end
+
+/** FastDirector is a Director that triggers the main loop as fast as possible.
+ *
+ * Features and Limitations:
+ *  - Faster than "normal" director
+ *  - Consumes more battery than the "normal" director
+ *  - It has some issues while using UIKit objects
+ */
+@interface CCDirectorFast : CCDirectorIOS
+{
+       BOOL isRunning;
+       
+       NSAutoreleasePool       *autoreleasePool;
+}
+-(void) mainLoop;
+@end
+
+/** ThreadedFastDirector is a Director that triggers the main loop from a thread.
+ *
+ * Features and Limitations:
+ *  - Faster than "normal" director
+ *  - Consumes more battery than the "normal" director
+ *  - It can be used with UIKit objects
+ *
+ * @since v0.8.2
+ */
+@interface CCDirectorFastThreaded : CCDirectorIOS
+{
+       BOOL isRunning; 
+}
+-(void) mainLoop;
+@end
+
+/** DisplayLinkDirector is a Director that synchronizes timers with the refresh rate of the display.
+ *
+ * Features and Limitations:
+ * - Only available on 3.1+
+ * - Scheduled timers & drawing are synchronizes with the refresh rate of the display
+ * - Only supports animation intervals of 1/60 1/30 & 1/15
+ *
+ * It is the recommended Director if the SDK is 3.1 or newer
+ *
+ * @since v0.8.2
+ */
+@interface CCDirectorDisplayLink : CCDirectorIOS
+{
+       id displayLink;
+}
+-(void) mainLoop:(id)sender;
+@end
+
+/** TimerDirector is a Director that calls the main loop from an NSTimer object
+ *
+ * Features and Limitations:
+ * - Integrates OK with UIKit objects
+ * - It the slowest director
+ * - The invertal update is customizable from 1 to 60
+ *
+ * It is the default Director.
+ */
+@interface CCDirectorTimer : CCDirectorIOS
+{
+       NSTimer *animationTimer;
+}
+-(void) mainLoop;
+@end
+
+// optimization. Should only be used to read it. Never to write it.
+extern CGFloat __ccContentScaleFactor;
+
+#endif // __IPHONE_OS_VERSION_MAX_ALLOWED
diff --git a/libs/cocos2d/Platforms/iOS/CCDirectorIOS.m b/libs/cocos2d/Platforms/iOS/CCDirectorIOS.m
new file mode 100644 (file)
index 0000000..111ae9f
--- /dev/null
@@ -0,0 +1,730 @@
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+// Only compile this code on iOS. These files should NOT be included on your Mac project.
+// But in case they are included, it won't be compiled.
+#import <Availability.h>
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+
+#import <unistd.h>
+
+// cocos2d imports
+#import "CCDirectorIOS.h"
+#import "CCTouchDelegateProtocol.h"
+#import "CCTouchDispatcher.h"
+#import "../../CCScheduler.h"
+#import "../../CCActionManager.h"
+#import "../../CCTextureCache.h"
+#import "../../ccMacros.h"
+#import "../../CCScene.h"
+
+// support imports
+#import "glu.h"
+#import "../../Support/OpenGL_Internal.h"
+#import "../../Support/CGPointExtension.h"
+
+#import "CCLayer.h"
+
+#if CC_ENABLE_PROFILERS
+#import "../../Support/CCProfiling.h"
+#endif
+
+
+#pragma mark -
+#pragma mark Director - global variables (optimization)
+
+CGFloat        __ccContentScaleFactor = 1;
+
+#pragma mark -
+#pragma mark Director iOS
+
+@interface CCDirector ()
+-(void) setNextScene;
+-(void) showFPS;
+-(void) calculateDeltaTime;
+@end
+
+@implementation CCDirector (iOSExtensionClassMethods)
+
++(Class) defaultDirector
+{
+       return [CCDirectorTimer class];
+}
+
++ (BOOL) setDirectorType:(ccDirectorType)type
+{
+       if( type == CCDirectorTypeDisplayLink ) {
+               NSString *reqSysVer = @"3.1";
+               NSString *currSysVer = [[UIDevice currentDevice] systemVersion];
+               
+               if([currSysVer compare:reqSysVer options:NSNumericSearch] == NSOrderedAscending)
+                       return NO;
+       }
+       switch (type) {
+               case CCDirectorTypeNSTimer:
+                       [CCDirectorTimer sharedDirector];
+                       break;
+               case CCDirectorTypeDisplayLink:
+                       [CCDirectorDisplayLink sharedDirector];
+                       break;
+               case CCDirectorTypeMainLoop:
+                       [CCDirectorFast sharedDirector];
+                       break;
+               case CCDirectorTypeThreadMainLoop:
+                       [CCDirectorFastThreaded sharedDirector];
+                       break;
+               default:
+                       NSAssert(NO,@"Unknown director type");
+       }
+       
+       return YES;
+}
+
+@end
+
+
+
+#pragma mark -
+#pragma mark CCDirectorIOS
+
+@interface CCDirectorIOS ()
+-(void) updateContentScaleFactor;
+
+@end
+
+@implementation CCDirectorIOS
+
+- (id) init
+{  
+       if( (self=[super init]) ) {
+                               
+               // portrait mode default
+               deviceOrientation_ = CCDeviceOrientationPortrait;
+               
+               __ccContentScaleFactor = 1;
+               isContentScaleSupported_ = NO;
+               
+               // running thread is main thread on iOS
+               runningThread_ = [NSThread currentThread];
+       }
+       
+       return self;
+}
+
+- (void) dealloc
+{      
+       [super dealloc];
+}
+
+//
+// Draw the Scene
+//
+- (void) drawScene
+{    
+       /* calculate "global" dt */
+       [self calculateDeltaTime];
+       
+       /* tick before glClear: issue #533 */
+       if( ! isPaused_ ) {
+               [[CCScheduler sharedScheduler] tick: dt];       
+       }
+       
+       glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+       
+       /* to avoid flickr, nextScene MUST be here: after tick and before draw.
+        XXX: Which bug is this one. It seems that it can't be reproduced with v0.9 */
+       if( nextScene_ )
+               [self setNextScene];
+       
+       glPushMatrix();
+       
+       [self applyOrientation];
+       
+       // By default enable VertexArray, ColorArray, TextureCoordArray and Texture2D
+       CC_ENABLE_DEFAULT_GL_STATES();
+       
+       /* draw the scene */
+       [runningScene_ visit];
+       
+       /* draw the notification node */
+       [notificationNode_ visit];
+
+       if( displayFPS_ )
+               [self showFPS];
+       
+#if CC_ENABLE_PROFILERS
+       [self showProfilers];
+#endif
+       
+       CC_DISABLE_DEFAULT_GL_STATES();
+       
+       glPopMatrix();
+       
+       [openGLView_ swapBuffers];
+}
+
+-(void) setProjection:(ccDirectorProjection)projection
+{
+       CGSize size = winSizeInPixels_;
+       
+       switch (projection) {
+               case kCCDirectorProjection2D:
+                       glViewport(0, 0, size.width, size.height);
+                       glMatrixMode(GL_PROJECTION);
+                       glLoadIdentity();
+                       ccglOrtho(0, size.width, 0, size.height, -1024 * CC_CONTENT_SCALE_FACTOR(), 1024 * CC_CONTENT_SCALE_FACTOR());
+                       glMatrixMode(GL_MODELVIEW);
+                       glLoadIdentity();
+                       break;
+                       
+               case kCCDirectorProjection3D:
+               {
+                       float zeye = [self getZEye];
+
+                       glViewport(0, 0, size.width, size.height);
+                       glMatrixMode(GL_PROJECTION);
+                       glLoadIdentity();
+//                     gluPerspective(60, (GLfloat)size.width/size.height, zeye-size.height/2, zeye+size.height/2 );
+                       gluPerspective(60, (GLfloat)size.width/size.height, 0.5f, 1500);
+
+                       glMatrixMode(GL_MODELVIEW);     
+                       glLoadIdentity();
+                       gluLookAt( size.width/2, size.height/2, zeye,
+                                         size.width/2, size.height/2, 0,
+                                         0.0f, 1.0f, 0.0f);
+                       break;
+               }
+                       
+               case kCCDirectorProjectionCustom:
+                       if( projectionDelegate_ )
+                               [projectionDelegate_ updateProjection];
+                       break;
+                       
+               default:
+                       CCLOG(@"cocos2d: Director: unrecognized projecgtion");
+                       break;
+       }
+       
+       projection_ = projection;
+}
+
+#pragma mark Director Integration with a UIKit view
+
+-(void) setOpenGLView:(EAGLView *)view
+{
+       if( view != openGLView_ ) {
+
+               [super setOpenGLView:view];
+
+               // set size
+               winSizeInPixels_ = CGSizeMake(winSizeInPoints_.width * __ccContentScaleFactor, winSizeInPoints_.height *__ccContentScaleFactor);
+               
+               if( __ccContentScaleFactor != 1 )
+                       [self updateContentScaleFactor];
+               
+               CCTouchDispatcher *touchDispatcher = [CCTouchDispatcher sharedDispatcher];
+               [openGLView_ setTouchDelegate: touchDispatcher];
+               [touchDispatcher setDispatchEvents: YES];
+       }
+}
+
+#pragma mark Director - Retina Display
+
+-(CGFloat) contentScaleFactor
+{
+       return __ccContentScaleFactor;
+}
+
+-(void) setContentScaleFactor:(CGFloat)scaleFactor
+{
+       if( scaleFactor != __ccContentScaleFactor ) {
+               
+               __ccContentScaleFactor = scaleFactor;
+               winSizeInPixels_ = CGSizeMake( winSizeInPoints_.width * scaleFactor, winSizeInPoints_.height * scaleFactor );
+               
+               if( openGLView_ )
+                       [self updateContentScaleFactor];
+               
+               // update projection
+               [self setProjection:projection_];
+       }
+}
+
+-(void) updateContentScaleFactor
+{
+       // Based on code snippet from: http://developer.apple.com/iphone/prerelease/library/snippets/sp2010/sp28.html
+       if ([openGLView_ respondsToSelector:@selector(setContentScaleFactor:)])
+       {                       
+               [openGLView_ setContentScaleFactor: __ccContentScaleFactor];
+               
+               isContentScaleSupported_ = YES;
+       }
+       else
+               CCLOG(@"cocos2d: 'setContentScaleFactor:' is not supported on this device");
+}
+
+-(BOOL) enableRetinaDisplay:(BOOL)enabled
+{
+       // Already enabled ?
+       if( enabled && __ccContentScaleFactor == 2 )
+               return YES;
+       
+       // Already disabled
+       if( ! enabled && __ccContentScaleFactor == 1 )
+               return YES;
+
+       // setContentScaleFactor is not supported
+       if (! [openGLView_ respondsToSelector:@selector(setContentScaleFactor:)])
+               return NO;
+
+       // SD device
+       if ([[UIScreen mainScreen] scale] == 1.0)
+               return NO;
+
+       float newScale = enabled ? 2 : 1;
+       [self setContentScaleFactor:newScale];
+       
+       return YES;
+}
+
+// overriden, don't call super
+-(void) reshapeProjection:(CGSize)size
+{
+       winSizeInPoints_ = [openGLView_ bounds].size;
+       winSizeInPixels_ = CGSizeMake(winSizeInPoints_.width * __ccContentScaleFactor, winSizeInPoints_.height *__ccContentScaleFactor);
+       
+       [self setProjection:projection_];
+}
+
+#pragma mark Director Scene Landscape
+
+-(CGPoint)convertToGL:(CGPoint)uiPoint
+{
+       CGSize s = winSizeInPoints_;
+       float newY = s.height - uiPoint.y;
+       float newX = s.width - uiPoint.x;
+       
+       CGPoint ret = CGPointZero;
+       switch ( deviceOrientation_) {
+               case CCDeviceOrientationPortrait:
+                       ret = ccp( uiPoint.x, newY );
+                       break;
+               case CCDeviceOrientationPortraitUpsideDown:
+                       ret = ccp(newX, uiPoint.y);
+                       break;
+               case CCDeviceOrientationLandscapeLeft:
+                       ret.x = uiPoint.y;
+                       ret.y = uiPoint.x;
+                       break;
+               case CCDeviceOrientationLandscapeRight:
+                       ret.x = newY;
+                       ret.y = newX;
+                       break;
+       }
+       return ret;
+}
+
+-(CGPoint)convertToUI:(CGPoint)glPoint
+{
+       CGSize winSize = winSizeInPoints_;
+       int oppositeX = winSize.width - glPoint.x;
+       int oppositeY = winSize.height - glPoint.y;
+       CGPoint uiPoint = CGPointZero;
+       switch ( deviceOrientation_) {
+               case CCDeviceOrientationPortrait:
+                       uiPoint = ccp(glPoint.x, oppositeY);
+                       break;
+               case CCDeviceOrientationPortraitUpsideDown:
+                       uiPoint = ccp(oppositeX, glPoint.y);
+                       break;
+               case CCDeviceOrientationLandscapeLeft:
+                       uiPoint = ccp(glPoint.y, glPoint.x);
+                       break;
+               case CCDeviceOrientationLandscapeRight:
+                       // Can't use oppositeX/Y because x/y are flipped
+                       uiPoint = ccp(winSize.width-glPoint.y, winSize.height-glPoint.x);
+                       break;
+       }
+       return uiPoint;
+}
+
+// get the current size of the glview
+-(CGSize) winSize
+{
+       CGSize s = winSizeInPoints_;
+       
+       if( deviceOrientation_ == CCDeviceOrientationLandscapeLeft || deviceOrientation_ == CCDeviceOrientationLandscapeRight ) {
+               // swap x,y in landscape mode
+               CGSize tmp = s;
+               s.width = tmp.height;
+               s.height = tmp.width;
+       }
+       return s;
+}
+
+-(CGSize) winSizeInPixels
+{
+       CGSize s = [self winSize];
+       
+       s.width *= CC_CONTENT_SCALE_FACTOR();
+       s.height *= CC_CONTENT_SCALE_FACTOR();
+       
+       return s;
+}
+
+-(ccDeviceOrientation) deviceOrientation
+{
+       return deviceOrientation_;
+}
+
+- (void) setDeviceOrientation:(ccDeviceOrientation) orientation
+{
+       if( deviceOrientation_ != orientation ) {
+               deviceOrientation_ = orientation;
+               switch( deviceOrientation_) {
+                       case CCDeviceOrientationPortrait:
+                               [[UIApplication sharedApplication] setStatusBarOrientation: UIInterfaceOrientationPortrait animated:NO];
+                               break;
+                       case CCDeviceOrientationPortraitUpsideDown:
+                               [[UIApplication sharedApplication] setStatusBarOrientation: UIDeviceOrientationPortraitUpsideDown animated:NO];
+                               break;
+                       case CCDeviceOrientationLandscapeLeft:
+                               [[UIApplication sharedApplication] setStatusBarOrientation: UIInterfaceOrientationLandscapeRight animated:NO];
+                               break;
+                       case CCDeviceOrientationLandscapeRight:
+                               [[UIApplication sharedApplication] setStatusBarOrientation: UIInterfaceOrientationLandscapeLeft animated:NO];
+                               break;
+                       default:
+                               NSLog(@"Director: Unknown device orientation");
+                               break;
+               }
+       }
+}
+
+-(void) applyOrientation
+{      
+       CGSize s = winSizeInPixels_;
+       float w = s.width / 2;
+       float h = s.height / 2;
+       
+       // XXX it's using hardcoded values.
+       // What if the the screen size changes in the future?
+       switch ( deviceOrientation_ ) {
+               case CCDeviceOrientationPortrait:
+                       // nothing
+                       break;
+               case CCDeviceOrientationPortraitUpsideDown:
+                       // upside down
+                       glTranslatef(w,h,0);
+                       glRotatef(180,0,0,1);
+                       glTranslatef(-w,-h,0);
+                       break;
+               case CCDeviceOrientationLandscapeRight:
+                       glTranslatef(w,h,0);
+                       glRotatef(90,0,0,1);
+                       glTranslatef(-h,-w,0);
+                       break;
+               case CCDeviceOrientationLandscapeLeft:
+                       glTranslatef(w,h,0);
+                       glRotatef(-90,0,0,1);
+                       glTranslatef(-h,-w,0);
+                       break;
+       }       
+}
+
+-(void) end
+{
+       // don't release the event handlers
+       // They are needed in case the director is run again
+       [[CCTouchDispatcher sharedDispatcher] removeAllDelegates];
+       
+       [super end];
+}
+
+@end
+
+
+#pragma mark -
+#pragma mark Director TimerDirector
+
+@implementation CCDirectorTimer
+- (void)startAnimation
+{
+       NSAssert( animationTimer == nil, @"animationTimer must be nil. Calling startAnimation twice?");
+       
+       if( gettimeofday( &lastUpdate_, NULL) != 0 ) {
+               CCLOG(@"cocos2d: Director: Error in gettimeofday");
+       }
+       
+       animationTimer = [NSTimer scheduledTimerWithTimeInterval:animationInterval_ target:self selector:@selector(mainLoop) userInfo:nil repeats:YES];
+       
+       //
+       //      If you want to attach the opengl view into UIScrollView
+       //  uncomment this line to prevent 'freezing'.
+       //      It doesn't work on with the Fast Director
+       //
+       //      [[NSRunLoop currentRunLoop] addTimer:animationTimer
+       //                                                               forMode:NSRunLoopCommonModes];
+}
+
+-(void) mainLoop
+{
+       [self drawScene];
+}
+
+- (void)stopAnimation
+{
+       [animationTimer invalidate];
+       animationTimer = nil;
+}
+
+- (void)setAnimationInterval:(NSTimeInterval)interval
+{
+       animationInterval_ = interval;
+       
+       if(animationTimer) {
+               [self stopAnimation];
+               [self startAnimation];
+       }
+}
+
+-(void) dealloc
+{
+       [animationTimer release];
+       [super dealloc];
+}
+@end
+
+
+#pragma mark -
+#pragma mark Director DirectorFast
+
+@implementation CCDirectorFast
+
+- (id) init
+{
+       if(( self = [super init] )) {
+               
+#if CC_DIRECTOR_DISPATCH_FAST_EVENTS
+               CCLOG(@"cocos2d: Fast Events enabled");
+#else
+               CCLOG(@"cocos2d: Fast Events disabled");
+#endif         
+               isRunning = NO;
+               
+               // XXX:
+               // XXX: Don't create any autorelease object before calling "fast director"
+               // XXX: else it will be leaked
+               // XXX:
+               autoreleasePool = [NSAutoreleasePool new];
+       }
+
+       return self;
+}
+
+- (void) startAnimation
+{
+       // XXX:
+       // XXX: release autorelease objects created
+       // XXX: between "use fast director" and "runWithScene"
+       // XXX:
+       [autoreleasePool release];
+       autoreleasePool = nil;
+
+       if ( gettimeofday( &lastUpdate_, NULL) != 0 ) {
+               CCLOG(@"cocos2d: Director: Error in gettimeofday");
+       }
+       
+
+       isRunning = YES;
+
+       SEL selector = @selector(mainLoop);
+       NSMethodSignature* sig = [[[CCDirector sharedDirector] class]
+                                                         instanceMethodSignatureForSelector:selector];
+       NSInvocation* invocation = [NSInvocation
+                                                               invocationWithMethodSignature:sig];
+       [invocation setTarget:[CCDirector sharedDirector]];
+       [invocation setSelector:selector];
+       [invocation performSelectorOnMainThread:@selector(invokeWithTarget:)
+                                                                withObject:[CCDirector sharedDirector] waitUntilDone:NO];
+       
+//     NSInvocationOperation *loopOperation = [[[NSInvocationOperation alloc]
+//                                                                                      initWithTarget:self selector:@selector(mainLoop) object:nil]
+//                                                                                     autorelease];
+//     
+//     [loopOperation performSelectorOnMainThread:@selector(start) withObject:nil
+//                                                              waitUntilDone:NO];
+}
+
+-(void) mainLoop
+{
+       while (isRunning) {
+       
+               NSAutoreleasePool *loopPool = [NSAutoreleasePool new];
+
+#if CC_DIRECTOR_DISPATCH_FAST_EVENTS
+               while( CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.004f, FALSE) == kCFRunLoopRunHandledSource);
+#else
+               while(CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, TRUE) == kCFRunLoopRunHandledSource);
+#endif
+
+               if (isPaused_) {
+                       usleep(250000); // Sleep for a quarter of a second (250,000 microseconds) so that the framerate is 4 fps.
+               }
+               
+               [self drawScene];               
+
+#if CC_DIRECTOR_DISPATCH_FAST_EVENTS
+               while( CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.004f, FALSE) == kCFRunLoopRunHandledSource);
+#else
+               while(CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, TRUE) == kCFRunLoopRunHandledSource);
+#endif
+
+               [loopPool release];
+       }       
+}
+- (void) stopAnimation
+{
+       isRunning = NO;
+}
+
+- (void)setAnimationInterval:(NSTimeInterval)interval
+{
+       NSLog(@"FastDirectory doesn't support setAnimationInterval, yet");
+}
+@end
+
+#pragma mark -
+#pragma mark Director DirectorThreadedFast
+
+@implementation CCDirectorFastThreaded
+
+- (id) init
+{
+       if(( self = [super init] )) {           
+               isRunning = NO;         
+       }
+       
+       return self;
+}
+
+- (void) startAnimation
+{
+       
+       if ( gettimeofday( &lastUpdate_, NULL) != 0 ) {
+               CCLOG(@"cocos2d: ThreadedFastDirector: Error on gettimeofday");
+       }
+
+       isRunning = YES;
+
+       NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(mainLoop) object:nil];
+       [thread start];
+       [thread release];
+}
+
+-(void) mainLoop
+{
+       while( ![[NSThread currentThread] isCancelled] ) {
+               if( isRunning )
+                       [self performSelectorOnMainThread:@selector(drawScene) withObject:nil waitUntilDone:YES];
+                               
+               if (isPaused_) {
+                       usleep(250000); // Sleep for a quarter of a second (250,000 microseconds) so that the framerate is 4 fps.
+               } else {
+//                     usleep(2000);
+               }
+       }       
+}
+- (void) stopAnimation
+{
+       isRunning = NO;
+}
+
+- (void)setAnimationInterval:(NSTimeInterval)interval
+{
+       NSLog(@"FastDirector doesn't support setAnimationInterval, yet");
+}
+@end
+
+#pragma mark -
+#pragma mark DirectorDisplayLink
+
+// Allows building DisplayLinkDirector for pre-3.1 SDKS
+// without getting compiler warnings.
+@interface NSObject(CADisplayLink)
++ (id) displayLinkWithTarget:(id)arg1 selector:(SEL)arg2;
+- (void) addToRunLoop:(id)arg1 forMode:(id)arg2;
+- (void) setFrameInterval:(int)interval;
+- (void) invalidate;
+@end
+
+@implementation CCDirectorDisplayLink
+
+- (void)setAnimationInterval:(NSTimeInterval)interval
+{
+       animationInterval_ = interval;
+       if(displayLink){
+               [self stopAnimation];
+               [self startAnimation];
+       }
+}
+
+- (void) startAnimation
+{
+       if ( gettimeofday( &lastUpdate_, NULL) != 0 ) {
+               CCLOG(@"cocos2d: DisplayLinkDirector: Error on gettimeofday");
+       }
+       
+       // approximate frame rate
+       // assumes device refreshes at 60 fps
+       int frameInterval = (int) floor(animationInterval_ * 60.0f);
+       
+       CCLOG(@"cocos2d: Frame interval: %d", frameInterval);
+
+       displayLink = [NSClassFromString(@"CADisplayLink") displayLinkWithTarget:self selector:@selector(mainLoop:)];
+       [displayLink setFrameInterval:frameInterval];
+       [displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
+}
+
+-(void) mainLoop:(id)sender
+{
+       [self drawScene];       
+}
+
+- (void) stopAnimation
+{
+       [displayLink invalidate];
+       displayLink = nil;
+}
+
+-(void) dealloc
+{
+       [displayLink release];
+       [super dealloc];
+}
+@end
+
+#endif // __IPHONE_OS_VERSION_MAX_ALLOWED
diff --git a/libs/cocos2d/Platforms/iOS/CCTouchDelegateProtocol.h b/libs/cocos2d/Platforms/iOS/CCTouchDelegateProtocol.h
new file mode 100644 (file)
index 0000000..20ba036
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2009 Valentin Milea
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+// Only compile this code on iOS. These files should NOT be included on your Mac project.
+// But in case they are included, it won't be compiled.
+#import <Availability.h>
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+
+#import <UIKit/UIKit.h>
+
+/**
+ CCTargetedTouchDelegate.
+ Using this type of delegate results in two benefits:
+ 1. You don't need to deal with NSSets, the dispatcher does the job of splitting
+ them. You get exactly one UITouch per call.
+ 2. You can *claim* a UITouch by returning YES in ccTouchBegan. Updates of claimed
+ touches are sent only to the delegate(s) that claimed them. So if you get a move/
+ ended/cancelled update you're sure it's your touch. This frees you from doing a
+ lot of checks when doing multi-touch.
+ (The name TargetedTouchDelegate relates to updates "targeting" their specific
+ handler, without bothering the other handlers.)
+ @since v0.8
+ */
+@protocol CCTargetedTouchDelegate <NSObject>
+
+/** Return YES to claim the touch.
+ @since v0.8
+ */
+- (BOOL)ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event;
+@optional
+// touch updates:
+- (void)ccTouchMoved:(UITouch *)touch withEvent:(UIEvent *)event;
+- (void)ccTouchEnded:(UITouch *)touch withEvent:(UIEvent *)event;
+- (void)ccTouchCancelled:(UITouch *)touch withEvent:(UIEvent *)event;
+@end
+
+/**
+ CCStandardTouchDelegate.
+ This type of delegate is the same one used by CocoaTouch. You will receive all the events (Began,Moved,Ended,Cancelled).
+ @since v0.8
+*/
+@protocol CCStandardTouchDelegate <NSObject>
+@optional
+- (void)ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event;
+- (void)ccTouchesMoved:(NSSet *)touches withEvent:(UIEvent *)event;
+- (void)ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event;
+- (void)ccTouchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event;
+@end
+
+#endif // __IPHONE_OS_VERSION_MAX_ALLOWED
diff --git a/libs/cocos2d/Platforms/iOS/CCTouchDispatcher.h b/libs/cocos2d/Platforms/iOS/CCTouchDispatcher.h
new file mode 100644 (file)
index 0000000..b692c6d
--- /dev/null
@@ -0,0 +1,122 @@
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2009 Valentin Milea
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+// Only compile this code on iOS. These files should NOT be included on your Mac project.
+// But in case they are included, it won't be compiled.
+#import <Availability.h>
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+
+#import "CCTouchDelegateProtocol.h"
+#import "EAGLView.h"
+
+
+typedef enum
+{
+       kCCTouchSelectorBeganBit = 1 << 0,
+       kCCTouchSelectorMovedBit = 1 << 1,
+       kCCTouchSelectorEndedBit = 1 << 2,
+       kCCTouchSelectorCancelledBit = 1 << 3,
+       kCCTouchSelectorAllBits = ( kCCTouchSelectorBeganBit | kCCTouchSelectorMovedBit | kCCTouchSelectorEndedBit | kCCTouchSelectorCancelledBit),
+} ccTouchSelectorFlag;
+
+
+enum {
+       kCCTouchBegan,
+       kCCTouchMoved,
+       kCCTouchEnded,
+       kCCTouchCancelled,
+       
+       kCCTouchMax,
+};
+
+struct ccTouchHandlerHelperData {
+       SEL                             touchesSel;
+       SEL                             touchSel;
+       ccTouchSelectorFlag  type;
+};
+
+/** CCTouchDispatcher.
+ Singleton that handles all the touch events.
+ The dispatcher dispatches events to the registered TouchHandlers.
+ There are 2 different type of touch handlers:
+   - Standard Touch Handlers
+   - Targeted Touch Handlers
+ The Standard Touch Handlers work like the CocoaTouch touch handler: a set of touches is passed to the delegate.
+ On the other hand, the Targeted Touch Handlers only receive 1 touch at the time, and they can "swallow" touches (avoid the propagation of the event).
+ Firstly, the dispatcher sends the received touches to the targeted touches.
+ These touches can be swallowed by the Targeted Touch Handlers. If there are still remaining touches, then the remaining touches will be sent
+ to the Standard Touch Handlers.
+
+ @since v0.8.0
+ */
+@interface CCTouchDispatcher : NSObject <EAGLTouchDelegate>
+{
+       NSMutableArray  *targetedHandlers;
+       NSMutableArray  *standardHandlers;
+
+       BOOL                    locked;
+       BOOL                    toAdd;
+       BOOL                    toRemove;
+       NSMutableArray  *handlersToAdd;
+       NSMutableArray  *handlersToRemove;
+       BOOL                    toQuit;
+
+       BOOL    dispatchEvents;
+       
+       // 4, 1 for each type of event
+       struct ccTouchHandlerHelperData handlerHelperData[kCCTouchMax];
+}
+
+/** singleton of the CCTouchDispatcher */
++ (CCTouchDispatcher*)sharedDispatcher;
+
+/** Whether or not the events are going to be dispatched. Default: YES */
+@property (nonatomic,readwrite, assign) BOOL dispatchEvents;
+
+/** Adds a standard touch delegate to the dispatcher's list.
+ See StandardTouchDelegate description.
+ IMPORTANT: The delegate will be retained.
+ */
+-(void) addStandardDelegate:(id<CCStandardTouchDelegate>) delegate priority:(int)priority;
+/** Adds a targeted touch delegate to the dispatcher's list.
+ See TargetedTouchDelegate description.
+ IMPORTANT: The delegate will be retained.
+ */
+-(void) addTargetedDelegate:(id<CCTargetedTouchDelegate>) delegate priority:(int)priority swallowsTouches:(BOOL)swallowsTouches;
+/** Removes a touch delegate.
+ The delegate will be released
+ */
+-(void) removeDelegate:(id) delegate;
+/** Removes all touch delegates, releasing all the delegates */
+-(void) removeAllDelegates;
+/** Changes the priority of a previously added delegate. The lower the number,
+ the higher the priority */
+-(void) setPriority:(int) priority forDelegate:(id) delegate;
+
+@end
+
+#endif // __IPHONE_OS_VERSION_MAX_ALLOWED
diff --git a/libs/cocos2d/Platforms/iOS/CCTouchDispatcher.m b/libs/cocos2d/Platforms/iOS/CCTouchDispatcher.m
new file mode 100644 (file)
index 0000000..7868a87
--- /dev/null
@@ -0,0 +1,326 @@
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2009 Valentin Milea
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+// Only compile this code on iOS. These files should NOT be included on your Mac project.
+// But in case they are included, it won't be compiled.
+#import <Availability.h>
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+
+
+#import "CCTouchDispatcher.h"
+#import "CCTouchHandler.h"
+
+
+@implementation CCTouchDispatcher
+
+@synthesize dispatchEvents;
+
+static CCTouchDispatcher *sharedDispatcher = nil;
+
++(CCTouchDispatcher*) sharedDispatcher
+{
+       @synchronized(self) {
+               if (sharedDispatcher == nil)
+                       sharedDispatcher = [[self alloc] init]; // assignment not done here
+       }
+       return sharedDispatcher;
+}
+
++(id) allocWithZone:(NSZone *)zone
+{
+       @synchronized(self) {
+               NSAssert(sharedDispatcher == nil, @"Attempted to allocate a second instance of a singleton.");
+               return [super allocWithZone:zone];
+       }
+       return nil; // on subsequent allocation attempts return nil
+}
+
+-(id) init
+{
+       if((self = [super init])) {
+       
+               dispatchEvents = YES;
+               targetedHandlers = [[NSMutableArray alloc] initWithCapacity:8];
+               standardHandlers = [[NSMutableArray alloc] initWithCapacity:4];
+               
+               handlersToAdd = [[NSMutableArray alloc] initWithCapacity:8];
+               handlersToRemove = [[NSMutableArray alloc] initWithCapacity:8];
+               
+               toRemove = NO;
+               toAdd = NO;
+               toQuit = NO;
+               locked = NO;
+
+               handlerHelperData[kCCTouchBegan] = (struct ccTouchHandlerHelperData) {@selector(ccTouchesBegan:withEvent:),@selector(ccTouchBegan:withEvent:),kCCTouchSelectorBeganBit};
+               handlerHelperData[kCCTouchMoved] = (struct ccTouchHandlerHelperData) {@selector(ccTouchesMoved:withEvent:),@selector(ccTouchMoved:withEvent:),kCCTouchSelectorMovedBit};
+               handlerHelperData[kCCTouchEnded] = (struct ccTouchHandlerHelperData) {@selector(ccTouchesEnded:withEvent:),@selector(ccTouchEnded:withEvent:),kCCTouchSelectorEndedBit};
+               handlerHelperData[kCCTouchCancelled] = (struct ccTouchHandlerHelperData) {@selector(ccTouchesCancelled:withEvent:),@selector(ccTouchCancelled:withEvent:),kCCTouchSelectorCancelledBit};
+               
+       }
+       
+       return self;
+}
+
+-(void) dealloc
+{
+       [targetedHandlers release];
+       [standardHandlers release];
+       [handlersToAdd release];
+       [handlersToRemove release];
+       [super dealloc];
+}
+
+//
+// handlers management
+//
+
+#pragma mark TouchDispatcher - Add Hanlder
+
+-(void) forceAddHandler:(CCTouchHandler*)handler array:(NSMutableArray*)array
+{
+       NSUInteger i = 0;
+       
+       for( CCTouchHandler *h in array ) {
+               if( h.priority < handler.priority )
+                       i++;
+               
+               NSAssert( h.delegate != handler.delegate, @"Delegate already added to touch dispatcher.");
+       }
+       [array insertObject:handler atIndex:i];         
+}
+
+-(void) addStandardDelegate:(id<CCStandardTouchDelegate>) delegate priority:(int)priority
+{
+       CCTouchHandler *handler = [CCStandardTouchHandler handlerWithDelegate:delegate priority:priority];
+       if( ! locked ) {
+               [self forceAddHandler:handler array:standardHandlers];
+       } else {
+               [handlersToAdd addObject:handler];
+               toAdd = YES;
+       }
+}
+
+-(void) addTargetedDelegate:(id<CCTargetedTouchDelegate>) delegate priority:(int)priority swallowsTouches:(BOOL)swallowsTouches
+{
+       CCTouchHandler *handler = [CCTargetedTouchHandler handlerWithDelegate:delegate priority:priority swallowsTouches:swallowsTouches];
+       if( ! locked ) {
+               [self forceAddHandler:handler array:targetedHandlers];
+       } else {
+               [handlersToAdd addObject:handler];
+               toAdd = YES;
+       }
+}
+
+#pragma mark TouchDispatcher - removeDelegate
+
+-(void) forceRemoveDelegate:(id)delegate
+{
+       // XXX: remove it from both handlers ???
+       
+       for( CCTouchHandler *handler in targetedHandlers ) {
+               if( handler.delegate == delegate ) {
+                       [targetedHandlers removeObject:handler];
+                       break;
+               }
+       }
+       
+       for( CCTouchHandler *handler in standardHandlers ) {
+               if( handler.delegate == delegate ) {
+                       [standardHandlers removeObject:handler];
+                       break;
+               }
+       }       
+}
+
+-(void) removeDelegate:(id) delegate
+{
+       if( delegate == nil )
+               return;
+       
+       if( ! locked ) {
+               [self forceRemoveDelegate:delegate];
+       } else {
+               [handlersToRemove addObject:delegate];
+               toRemove = YES;
+       }
+}
+
+#pragma mark TouchDispatcher  - removeAllDelegates
+
+-(void) forceRemoveAllDelegates
+{
+       [standardHandlers removeAllObjects];
+       [targetedHandlers removeAllObjects];
+}
+-(void) removeAllDelegates
+{
+       if( ! locked )
+               [self forceRemoveAllDelegates];
+       else
+               toQuit = YES;
+}
+
+#pragma mark Changing priority of added handlers
+
+-(void) setPriority:(int) priority forDelegate:(id) delegate
+{
+       NSAssert(NO, @"Set priority no implemented yet. Don't forget to report this bug!");
+//     if( delegate == nil )
+//             [NSException raise:NSInvalidArgumentException format:@"Got nil touch delegate"];
+//     
+//     CCTouchHandler *handler = nil;
+//     for( handler in touchHandlers )
+//             if( handler.delegate == delegate ) break;
+//     
+//     if( handler == nil )
+//             [NSException raise:NSInvalidArgumentException format:@"Touch delegate not found"];
+//     
+//     if( handler.priority != priority ) {
+//             handler.priority = priority;
+//             
+//             [handler retain];
+//             [touchHandlers removeObject:handler];
+//             [self addHandler:handler];
+//             [handler release];
+//     }
+}
+
+
+//
+// dispatch events
+//
+-(void) touches:(NSSet*)touches withEvent:(UIEvent*)event withTouchType:(unsigned int)idx
+{
+       NSAssert(idx < 4, @"Invalid idx value");
+
+       id mutableTouches;
+       locked = YES;
+       
+       // optimization to prevent a mutable copy when it is not necessary
+       unsigned int targetedHandlersCount = [targetedHandlers count];
+       unsigned int standardHandlersCount = [standardHandlers count];  
+       BOOL needsMutableSet = (targetedHandlersCount && standardHandlersCount);
+       
+       mutableTouches = (needsMutableSet ? [touches mutableCopy] : touches);
+
+       struct ccTouchHandlerHelperData helper = handlerHelperData[idx];
+       //
+       // process the target handlers 1st
+       //
+       if( targetedHandlersCount > 0 ) {
+               for( UITouch *touch in touches ) {
+                       for(CCTargetedTouchHandler *handler in targetedHandlers) {
+                               
+                               BOOL claimed = NO;
+                               if( idx == kCCTouchBegan ) {
+                                       claimed = [handler.delegate ccTouchBegan:touch withEvent:event];
+                                       if( claimed )
+                                               [handler.claimedTouches addObject:touch];
+                               } 
+                               
+                               // else (moved, ended, cancelled)
+                               else if( [handler.claimedTouches containsObject:touch] ) {
+                                       claimed = YES;
+                                       if( handler.enabledSelectors & helper.type )
+                                               [handler.delegate performSelector:helper.touchSel withObject:touch withObject:event];
+                                       
+                                       if( helper.type & (kCCTouchSelectorCancelledBit | kCCTouchSelectorEndedBit) )
+                                               [handler.claimedTouches removeObject:touch];
+                               }
+                                       
+                               if( claimed && handler.swallowsTouches ) {
+                                       if( needsMutableSet )
+                                               [mutableTouches removeObject:touch];
+                                       break;
+                               }
+                       }
+               }
+       }
+       
+       //
+       // process standard handlers 2nd
+       //
+       if( standardHandlersCount > 0 && [mutableTouches count]>0 ) {
+               for( CCTouchHandler *handler in standardHandlers ) {
+                       if( handler.enabledSelectors & helper.type )
+                               [handler.delegate performSelector:helper.touchesSel withObject:mutableTouches withObject:event];
+               }
+       }
+       if( needsMutableSet )
+               [mutableTouches release];
+       
+       //
+       // Optimization. To prevent a [handlers copy] which is expensive
+       // the add/removes/quit is done after the iterations
+       //
+       locked = NO;
+       if( toRemove ) {
+               toRemove = NO;
+               for( id delegate in handlersToRemove )
+                       [self forceRemoveDelegate:delegate];
+               [handlersToRemove removeAllObjects];
+       }
+       if( toAdd ) {
+               toAdd = NO;
+               for( CCTouchHandler *handler in handlersToAdd ) {
+                       Class targetedClass = [CCTargetedTouchHandler class];
+                       if( [handler isKindOfClass:targetedClass] )
+                               [self forceAddHandler:handler array:targetedHandlers];
+                       else
+                               [self forceAddHandler:handler array:standardHandlers];
+               }
+               [handlersToAdd removeAllObjects];
+       }
+       if( toQuit ) {
+               toQuit = NO;
+               [self forceRemoveAllDelegates];
+       }
+}
+
+- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
+{
+       if( dispatchEvents )
+               [self touches:touches withEvent:event withTouchType:kCCTouchBegan];
+}
+- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
+{
+       if( dispatchEvents ) 
+               [self touches:touches withEvent:event withTouchType:kCCTouchMoved];
+}
+
+- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
+{
+       if( dispatchEvents )
+               [self touches:touches withEvent:event withTouchType:kCCTouchEnded];
+}
+
+- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
+{
+       if( dispatchEvents )
+               [self touches:touches withEvent:event withTouchType:kCCTouchCancelled];
+}
+@end
+
+#endif // __IPHONE_OS_VERSION_MAX_ALLOWED
diff --git a/libs/cocos2d/Platforms/iOS/CCTouchHandler.h b/libs/cocos2d/Platforms/iOS/CCTouchHandler.h
new file mode 100644 (file)
index 0000000..31a3e36
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2009 Valentin Milea
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+// Only compile this code on iOS. These files should NOT be included on your Mac project.
+// But in case they are included, it won't be compiled.
+#import <Availability.h>
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+
+/*
+ * This file contains the delegates of the touches
+ * There are 2 possible delegates:
+ *   - CCStandardTouchHandler: propagates all the events at once
+ *   - CCTargetedTouchHandler: propagates 1 event at the time
+ */
+
+#import "CCTouchDelegateProtocol.h"
+#import "CCTouchDispatcher.h"
+
+/**
+ CCTouchHandler
+ Object than contains the delegate and priority of the event handler.
+*/
+@interface CCTouchHandler : NSObject {
+       id                              delegate;
+       int                             priority;
+       ccTouchSelectorFlag             enabledSelectors_;
+}
+
+/** delegate */
+@property(nonatomic, readwrite, retain) id delegate;
+/** priority */
+@property(nonatomic, readwrite) int priority; // default 0
+/** enabled selectors */
+@property(nonatomic,readwrite) ccTouchSelectorFlag enabledSelectors;
+
+/** allocates a TouchHandler with a delegate and a priority */
++ (id)handlerWithDelegate:(id)aDelegate priority:(int)priority;
+/** initializes a TouchHandler with a delegate and a priority */
+- (id)initWithDelegate:(id)aDelegate priority:(int)priority;
+@end
+
+/** CCStandardTouchHandler
+ It forwardes each event to the delegate.
+ */
+@interface CCStandardTouchHandler : CCTouchHandler
+{
+}
+@end
+
+/**
+ CCTargetedTouchHandler
+ Object than contains the claimed touches and if it swallos touches.
+ Used internally by TouchDispatcher
+ */
+@interface CCTargetedTouchHandler : CCTouchHandler {
+       BOOL swallowsTouches;
+       NSMutableSet *claimedTouches;
+}
+/** whether or not the touches are swallowed */
+@property(nonatomic, readwrite) BOOL swallowsTouches; // default NO
+/** MutableSet that contains the claimed touches */
+@property(nonatomic, readonly) NSMutableSet *claimedTouches;
+
+/** allocates a TargetedTouchHandler with a delegate, a priority and whether or not it swallows touches or not */
++ (id)handlerWithDelegate:(id) aDelegate priority:(int)priority swallowsTouches:(BOOL)swallowsTouches;
+/** initializes a TargetedTouchHandler with a delegate, a priority and whether or not it swallows touches or not */
+- (id)initWithDelegate:(id) aDelegate priority:(int)priority swallowsTouches:(BOOL)swallowsTouches;
+
+@end
+
+#endif // __IPHONE_OS_VERSION_MAX_ALLOWED
diff --git a/libs/cocos2d/Platforms/iOS/CCTouchHandler.m b/libs/cocos2d/Platforms/iOS/CCTouchHandler.m
new file mode 100644 (file)
index 0000000..a52103b
--- /dev/null
@@ -0,0 +1,135 @@
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2009 Valentin Milea
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+
+// Only compile this code on iOS. These files should NOT be included on your Mac project.
+// But in case they are included, it won't be compiled.
+#import <Availability.h>
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+
+/*
+ * This file contains the delegates of the touches
+ * There are 2 possible delegates:
+ *   - CCStandardTouchHandler: propagates all the events at once
+ *   - CCTargetedTouchHandler: propagates 1 event at the time
+ */
+
+#import "CCTouchHandler.h"
+#import "../../ccMacros.h"
+
+#pragma mark -
+#pragma mark TouchHandler
+@implementation CCTouchHandler
+
+@synthesize delegate, priority;
+@synthesize enabledSelectors=enabledSelectors_;
+
++ (id)handlerWithDelegate:(id) aDelegate priority:(int)aPriority
+{
+       return [[[self alloc] initWithDelegate:aDelegate priority:aPriority] autorelease];
+}
+
+- (id)initWithDelegate:(id) aDelegate priority:(int)aPriority
+{
+       NSAssert(aDelegate != nil, @"Touch delegate may not be nil");
+       
+       if ((self = [super init])) {
+               self.delegate = aDelegate;
+               priority = aPriority;
+               enabledSelectors_ = 0;
+       }
+       
+       return self;
+}
+
+- (void)dealloc {
+       CCLOGINFO(@"cocos2d: deallocing %@", self);
+       [delegate release];
+       [super dealloc];
+}
+@end
+
+#pragma mark -
+#pragma mark StandardTouchHandler
+@implementation CCStandardTouchHandler
+-(id) initWithDelegate:(id)del priority:(int)pri
+{
+       if( (self=[super initWithDelegate:del priority:pri]) ) {
+               if( [del respondsToSelector:@selector(ccTouchesBegan:withEvent:)] )
+                       enabledSelectors_ |= kCCTouchSelectorBeganBit;
+               if( [del respondsToSelector:@selector(ccTouchesMoved:withEvent:)] )
+                       enabledSelectors_ |= kCCTouchSelectorMovedBit;
+               if( [del respondsToSelector:@selector(ccTouchesEnded:withEvent:)] )
+                       enabledSelectors_ |= kCCTouchSelectorEndedBit;
+               if( [del respondsToSelector:@selector(ccTouchesCancelled:withEvent:)] )
+                       enabledSelectors_ |= kCCTouchSelectorCancelledBit;
+       }
+       return self;
+}
+@end
+
+#pragma mark -
+#pragma mark TargetedTouchHandler
+
+@interface CCTargetedTouchHandler (private)
+-(void) updateKnownTouches:(NSMutableSet *)touches withEvent:(UIEvent *)event selector:(SEL)selector unclaim:(BOOL)doUnclaim;
+@end
+
+@implementation CCTargetedTouchHandler
+
+@synthesize swallowsTouches, claimedTouches;
+
++ (id)handlerWithDelegate:(id)aDelegate priority:(int)priority swallowsTouches:(BOOL)swallow
+{
+       return [[[self alloc] initWithDelegate:aDelegate priority:priority swallowsTouches:swallow] autorelease];
+}
+
+- (id)initWithDelegate:(id)aDelegate priority:(int)aPriority swallowsTouches:(BOOL)swallow
+{
+       if ((self = [super initWithDelegate:aDelegate priority:aPriority])) {   
+               claimedTouches = [[NSMutableSet alloc] initWithCapacity:2];
+               swallowsTouches = swallow;
+               
+               if( [aDelegate respondsToSelector:@selector(ccTouchBegan:withEvent:)] )
+                       enabledSelectors_ |= kCCTouchSelectorBeganBit;
+               if( [aDelegate respondsToSelector:@selector(ccTouchMoved:withEvent:)] )
+                       enabledSelectors_ |= kCCTouchSelectorMovedBit;
+               if( [aDelegate respondsToSelector:@selector(ccTouchEnded:withEvent:)] )
+                       enabledSelectors_ |= kCCTouchSelectorEndedBit;
+               if( [aDelegate respondsToSelector:@selector(ccTouchCancelled:withEvent:)] )
+                       enabledSelectors_ |= kCCTouchSelectorCancelledBit;
+       }
+       
+       return self;
+}
+
+- (void)dealloc {
+       [claimedTouches release];
+       [super dealloc];
+}
+@end
+
+
+#endif // __IPHONE_OS_VERSION_MAX_ALLOWED
\ No newline at end of file
diff --git a/libs/cocos2d/Platforms/iOS/EAGLView.h b/libs/cocos2d/Platforms/iOS/EAGLView.h
new file mode 100644 (file)
index 0000000..fd41c5e
--- /dev/null
@@ -0,0 +1,155 @@
+/*
+
+===== IMPORTANT =====
+
+This is sample code demonstrating API, technology or techniques in development.
+Although this sample code has been reviewed for technical accuracy, it is not
+final. Apple is supplying this information to help you plan for the adoption of
+the technologies and programming interfaces described herein. This information
+is subject to change, and software implemented based on this sample code should
+be tested with final operating system software and final documentation. Newer
+versions of this sample code may be provided with future seeds of the API or
+technology. For information about updates to this and other developer
+documentation, view the New & Updated sidebars in subsequent documentation
+seeds.
+
+=====================
+
+File: EAGLView.h
+Abstract: Convenience class that wraps the CAEAGLLayer from CoreAnimation into a
+UIView subclass.
+
+Version: 1.3
+
+Disclaimer: IMPORTANT:  This Apple software is supplied to you by Apple Inc.
+("Apple") in consideration of your agreement to the following terms, and your
+use, installation, modification or redistribution of this Apple software
+constitutes acceptance of these terms.  If you do not agree with these terms,
+please do not use, install, modify or redistribute this Apple software.
+
+In consideration of your agreement to abide by the following terms, and subject
+to these terms, Apple grants you a personal, non-exclusive license, under
+Apple's copyrights in this original Apple software (the "Apple Software"), to
+use, reproduce, modify and redistribute the Apple Software, with or without
+modifications, in source and/or binary forms; provided that if you redistribute
+the Apple Software in its entirety and without modifications, you must retain
+this notice and the following text and disclaimers in all such redistributions
+of the Apple Software.
+Neither the name, trademarks, service marks or logos of Apple Inc. may be used
+to endorse or promote products derived from the Apple Software without specific
+prior written permission from Apple.  Except as expressly stated in this notice,
+no other rights or licenses, express or implied, are granted by Apple herein,
+including but not limited to any patent rights that may be infringed by your
+derivative works or by other works in which the Apple Software may be
+incorporated.
+
+The Apple Software is provided by Apple on an "AS IS" basis.  APPLE MAKES NO
+WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED
+WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN
+COMBINATION WITH YOUR PRODUCTS.
+
+IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR
+DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF
+CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF
+APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+Copyright (C) 2008 Apple Inc. All Rights Reserved.
+
+*/
+
+// Only compile this code on iOS. These files should NOT be included on your Mac project.
+// But in case they are included, it won't be compiled.
+#import <Availability.h>
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+
+#import <UIKit/UIKit.h>
+#import <OpenGLES/EAGL.h>
+#import <OpenGLES/EAGLDrawable.h>
+#import <OpenGLES/ES1/gl.h>
+#import <OpenGLES/ES1/glext.h>
+
+#import "ESRenderer.h"
+
+//CLASSES:
+
+@class EAGLView;
+@class EAGLSharegroup;
+
+//PROTOCOLS:
+
+@protocol EAGLTouchDelegate <NSObject>
+- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event;
+- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event;
+- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event;
+- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event;
+@end
+
+//CLASS INTERFACE:
+
+/** EAGLView Class.
+ * This class wraps the CAEAGLLayer from CoreAnimation into a convenient UIView subclass.
+ * The view content is basically an EAGL surface you render your OpenGL scene into.
+ * Note that setting the view non-opaque will only work if the EAGL surface has an alpha channel.
+ */
+@interface EAGLView : UIView
+{
+    id<ESRenderer>                     renderer_;      
+       EAGLContext                             *context_; // weak ref
+
+       NSString                                *pixelformat_;
+       GLuint                                  depthFormat_;
+       BOOL                                    preserveBackbuffer_;
+
+       CGSize                                  size_;
+       BOOL                                    discardFramebufferSupported_;
+       id<EAGLTouchDelegate>   touchDelegate_;
+
+       //fsaa addition
+       BOOL                                    multisampling_;
+       unsigned int                    requestedSamples_;
+}
+
+/** creates an initializes an EAGLView with a frame and 0-bit depth buffer, and a RGB565 color buffer. */
++ (id) viewWithFrame:(CGRect)frame;
+/** creates an initializes an EAGLView with a frame, a color buffer format, and 0-bit depth buffer. */
++ (id) viewWithFrame:(CGRect)frame pixelFormat:(NSString*)format;
+/** creates an initializes an EAGLView with a frame, a color buffer format, and a depth buffer. */
++ (id) viewWithFrame:(CGRect)frame pixelFormat:(NSString*)format depthFormat:(GLuint)depth;
+/** creates an initializes an EAGLView with a frame, a color buffer format, a depth buffer format, a sharegroup, and multisamping */
++ (id) viewWithFrame:(CGRect)frame pixelFormat:(NSString*)format depthFormat:(GLuint)depth preserveBackbuffer:(BOOL)retained sharegroup:(EAGLSharegroup*)sharegroup multiSampling:(BOOL)multisampling numberOfSamples:(unsigned int)samples;
+
+/** Initializes an EAGLView with a frame and 0-bit depth buffer, and a RGB565 color buffer */
+- (id) initWithFrame:(CGRect)frame; //These also set the current context
+/** Initializes an EAGLView with a frame, a color buffer format, and 0-bit depth buffer */
+- (id) initWithFrame:(CGRect)frame pixelFormat:(NSString*)format;
+/** Initializes an EAGLView with a frame, a color buffer format, a depth buffer format, a sharegroup and multisampling support */
+- (id) initWithFrame:(CGRect)frame pixelFormat:(NSString*)format depthFormat:(GLuint)depth preserveBackbuffer:(BOOL)retained sharegroup:(EAGLSharegroup*)sharegroup multiSampling:(BOOL)sampling numberOfSamples:(unsigned int)nSamples;
+
+/** pixel format: it could be RGBA8 (32-bit) or RGB565 (16-bit) */
+@property(nonatomic,readonly) NSString* pixelFormat;
+/** depth format of the render buffer: 0, 16 or 24 bits*/
+@property(nonatomic,readonly) GLuint depthFormat;
+
+/** returns surface size in pixels */
+@property(nonatomic,readonly) CGSize surfaceSize;
+
+/** OpenGL context */
+@property(nonatomic,readonly) EAGLContext *context;
+
+@property(nonatomic,readwrite) BOOL multiSampling;
+
+/** touch delegate */
+@property(nonatomic,readwrite,assign) id<EAGLTouchDelegate> touchDelegate;
+
+/** EAGLView uses double-buffer. This method swaps the buffers */
+-(void) swapBuffers;
+
+- (CGPoint) convertPointFromViewToSurface:(CGPoint)point;
+- (CGRect) convertRectFromViewToSurface:(CGRect)rect;
+@end
+
+#endif // __IPHONE_OS_VERSION_MAX_ALLOWED
\ No newline at end of file
diff --git a/libs/cocos2d/Platforms/iOS/EAGLView.m b/libs/cocos2d/Platforms/iOS/EAGLView.m
new file mode 100644 (file)
index 0000000..39dcdb6
--- /dev/null
@@ -0,0 +1,342 @@
+/*
+
+===== IMPORTANT =====
+
+This is sample code demonstrating API, technology or techniques in development.
+Although this sample code has been reviewed for technical accuracy, it is not
+final. Apple is supplying this information to help you plan for the adoption of
+the technologies and programming interfaces described herein. This information
+is subject to change, and software implemented based on this sample code should
+be tested with final operating system software and final documentation. Newer
+versions of this sample code may be provided with future seeds of the API or
+technology. For information about updates to this and other developer
+documentation, view the New & Updated sidebars in subsequent documentation
+seeds.
+
+=====================
+
+File: EAGLView.m
+Abstract: Convenience class that wraps the CAEAGLLayer from CoreAnimation into a
+UIView subclass.
+
+Version: 1.3
+
+Disclaimer: IMPORTANT:  This Apple software is supplied to you by Apple Inc.
+("Apple") in consideration of your agreement to the following terms, and your
+use, installation, modification or redistribution of this Apple software
+constitutes acceptance of these terms.  If you do not agree with these terms,
+please do not use, install, modify or redistribute this Apple software.
+
+In consideration of your agreement to abide by the following terms, and subject
+to these terms, Apple grants you a personal, non-exclusive license, under
+Apple's copyrights in this original Apple software (the "Apple Software"), to
+use, reproduce, modify and redistribute the Apple Software, with or without
+modifications, in source and/or binary forms; provided that if you redistribute
+the Apple Software in its entirety and without modifications, you must retain
+this notice and the following text and disclaimers in all such redistributions
+of the Apple Software.
+Neither the name, trademarks, service marks or logos of Apple Inc. may be used
+to endorse or promote products derived from the Apple Software without specific
+prior written permission from Apple.  Except as expressly stated in this notice,
+no other rights or licenses, express or implied, are granted by Apple herein,
+including but not limited to any patent rights that may be infringed by your
+derivative works or by other works in which the Apple Software may be
+incorporated.
+
+The Apple Software is provided by Apple on an "AS IS" basis.  APPLE MAKES NO
+WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED
+WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN
+COMBINATION WITH YOUR PRODUCTS.
+
+IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR
+DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF
+CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF
+APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+Copyright (C) 2008 Apple Inc. All Rights Reserved.
+
+*/
+
+// Only compile this code on iOS. These files should NOT be included on your Mac project.
+// But in case they are included, it won't be compiled.
+#import <Availability.h>
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+
+#import <QuartzCore/QuartzCore.h>
+
+#import "EAGLView.h"
+#import "ES1Renderer.h"
+#import "../../CCDirector.h"
+#import "../../ccMacros.h"
+#import "../../CCConfiguration.h"
+#import "../../Support/OpenGL_Internal.h"
+
+
+//CLASS IMPLEMENTATIONS:
+
+@interface EAGLView (Private)
+- (BOOL) setupSurfaceWithSharegroup:(EAGLSharegroup*)sharegroup;
+- (unsigned int) convertPixelFormat:(NSString*) pixelFormat;
+@end
+
+@implementation EAGLView
+
+@synthesize surfaceSize=size_;
+@synthesize pixelFormat=pixelformat_, depthFormat=depthFormat_;
+@synthesize touchDelegate=touchDelegate_;
+@synthesize context=context_;
+@synthesize multiSampling=multiSampling_;
+
++ (Class) layerClass
+{
+       return [CAEAGLLayer class];
+}
+
++ (id) viewWithFrame:(CGRect)frame
+{
+       return [[[self alloc] initWithFrame:frame] autorelease];
+}
+
++ (id) viewWithFrame:(CGRect)frame pixelFormat:(NSString*)format
+{
+       return [[[self alloc] initWithFrame:frame pixelFormat:format] autorelease];
+}
+
++ (id) viewWithFrame:(CGRect)frame pixelFormat:(NSString*)format depthFormat:(GLuint)depth
+{
+       return [[[self alloc] initWithFrame:frame pixelFormat:format depthFormat:depth preserveBackbuffer:NO sharegroup:nil multiSampling:NO numberOfSamples:0] autorelease];
+}
+
++ (id) viewWithFrame:(CGRect)frame pixelFormat:(NSString*)format depthFormat:(GLuint)depth preserveBackbuffer:(BOOL)retained sharegroup:(EAGLSharegroup*)sharegroup multiSampling:(BOOL)multisampling numberOfSamples:(unsigned int)samples
+{
+       return [[[self alloc] initWithFrame:frame pixelFormat:format depthFormat:depth preserveBackbuffer:retained sharegroup:sharegroup multiSampling:multisampling numberOfSamples:samples] autorelease];
+}
+
+- (id) initWithFrame:(CGRect)frame
+{
+       return [self initWithFrame:frame pixelFormat:kEAGLColorFormatRGB565 depthFormat:0 preserveBackbuffer:NO sharegroup:nil multiSampling:NO numberOfSamples:0];
+}
+
+- (id) initWithFrame:(CGRect)frame pixelFormat:(NSString*)format 
+{
+       return [self initWithFrame:frame pixelFormat:format depthFormat:0 preserveBackbuffer:NO sharegroup:nil multiSampling:NO numberOfSamples:0];
+}
+
+- (id) initWithFrame:(CGRect)frame pixelFormat:(NSString*)format depthFormat:(GLuint)depth preserveBackbuffer:(BOOL)retained sharegroup:(EAGLSharegroup*)sharegroup multiSampling:(BOOL)sampling numberOfSamples:(unsigned int)nSamples
+{
+       if((self = [super initWithFrame:frame]))
+       {
+               pixelformat_ = format;
+               depthFormat_ = depth;
+               multiSampling_ = sampling;
+               requestedSamples_ = nSamples;
+               preserveBackbuffer_ = retained;
+               
+               if( ! [self setupSurfaceWithSharegroup:sharegroup] ) {
+                       [self release];
+                       return nil;
+               }
+       }
+
+       return self;
+}
+
+-(id) initWithCoder:(NSCoder *)aDecoder
+{
+       if( (self = [super initWithCoder:aDecoder]) ) {
+               
+               CAEAGLLayer*                    eaglLayer = (CAEAGLLayer*)[self layer];
+               
+               pixelformat_ = kEAGLColorFormatRGB565;
+               depthFormat_ = 0; // GL_DEPTH_COMPONENT24_OES;
+               multiSampling_= NO;
+               requestedSamples_ = 0;
+               size_ = [eaglLayer bounds].size;
+
+               if( ! [self setupSurfaceWithSharegroup:nil] ) {
+                       [self release];
+                       return nil;
+               }
+    }
+       
+    return self;
+}
+
+-(BOOL) setupSurfaceWithSharegroup:(EAGLSharegroup*)sharegroup
+{
+       CAEAGLLayer *eaglLayer = (CAEAGLLayer *)self.layer;
+       
+       eaglLayer.opaque = YES;
+       eaglLayer.drawableProperties = [NSDictionary dictionaryWithObjectsAndKeys:
+                                                                       [NSNumber numberWithBool:preserveBackbuffer_], kEAGLDrawablePropertyRetainedBacking,
+                                                                       pixelformat_, kEAGLDrawablePropertyColorFormat, nil];
+       
+       
+       renderer_ = [[ES1Renderer alloc] initWithDepthFormat:depthFormat_
+                                                                                withPixelFormat:[self convertPixelFormat:pixelformat_]
+                                                                                 withSharegroup:sharegroup
+                                                                          withMultiSampling:multiSampling_
+                                                                        withNumberOfSamples:requestedSamples_];
+       if (!renderer_)
+               return NO;
+       
+       context_ = [renderer_ context];
+       [context_ renderbufferStorage:GL_RENDERBUFFER_OES fromDrawable:eaglLayer];
+
+       discardFramebufferSupported_ = [[CCConfiguration sharedConfiguration] supportsDiscardFramebuffer];
+       
+       return YES;
+}
+
+- (void) dealloc
+{
+       CCLOGINFO(@"cocos2d: deallocing %@", self);
+
+
+       [renderer_ release];
+       [super dealloc];
+}
+
+- (void) layoutSubviews
+{
+    [renderer_ resizeFromLayer:(CAEAGLLayer*)self.layer];
+       size_ = [renderer_ backingSize];
+
+       // Issue #914 #924
+       CCDirector *director = [CCDirector sharedDirector];
+       [director reshapeProjection:size_];
+
+       // Avoid flicker. Issue #350
+       [director performSelectorOnMainThread:@selector(drawScene) withObject:nil waitUntilDone:YES];
+}
+
+- (void) swapBuffers
+{
+       // IMPORTANT:
+       // - preconditions
+       //      -> context_ MUST be the OpenGL context
+       //      -> renderbuffer_ must be the the RENDER BUFFER
+
+#ifdef __IPHONE_4_0
+       
+       if (multiSampling_)
+       {
+               /* Resolve from msaaFramebuffer to resolveFramebuffer */
+               //glDisable(GL_SCISSOR_TEST);     
+               glBindFramebufferOES(GL_READ_FRAMEBUFFER_APPLE, [renderer_ msaaFrameBuffer]);
+               glBindFramebufferOES(GL_DRAW_FRAMEBUFFER_APPLE, [renderer_ defaultFrameBuffer]);
+               glResolveMultisampleFramebufferAPPLE();
+       }
+       
+       if( discardFramebufferSupported_)
+       {       
+               if (multiSampling_)
+               {
+                       if (depthFormat_)
+                       {
+                               GLenum attachments[] = {GL_COLOR_ATTACHMENT0_OES, GL_DEPTH_ATTACHMENT_OES};
+                               glDiscardFramebufferEXT(GL_READ_FRAMEBUFFER_APPLE, 2, attachments);
+                       }
+                       else
+                       {
+                               GLenum attachments[] = {GL_COLOR_ATTACHMENT0_OES};
+                               glDiscardFramebufferEXT(GL_READ_FRAMEBUFFER_APPLE, 1, attachments);
+                       }
+                       
+                       glBindRenderbufferOES(GL_RENDERBUFFER_OES, [renderer_ colorRenderBuffer]);
+       
+               }       
+               
+               // not MSAA
+               else if (depthFormat_ ) {
+                       GLenum attachments[] = { GL_DEPTH_ATTACHMENT_OES};
+                       glDiscardFramebufferEXT(GL_FRAMEBUFFER_OES, 1, attachments);
+               }
+       }
+       
+#endif // __IPHONE_4_0
+       
+       if(![context_ presentRenderbuffer:GL_RENDERBUFFER_OES])
+               CCLOG(@"cocos2d: Failed to swap renderbuffer in %s\n", __FUNCTION__);
+
+#if COCOS2D_DEBUG
+       CHECK_GL_ERROR();
+#endif
+       
+       // We can safely re-bind the framebuffer here, since this will be the
+       // 1st instruction of the new main loop
+       if( multiSampling_ )
+               glBindFramebufferOES(GL_FRAMEBUFFER_OES, [renderer_ msaaFrameBuffer]);
+}
+
+- (unsigned int) convertPixelFormat:(NSString*) pixelFormat
+{
+       // define the pixel format
+       GLenum pFormat;
+       
+       
+       if([pixelFormat isEqualToString:@"EAGLColorFormat565"]) 
+               pFormat = GL_RGB565_OES;
+       else 
+               pFormat = GL_RGBA8_OES;
+       
+       return pFormat;
+}
+
+#pragma mark EAGLView - Point conversion
+
+- (CGPoint) convertPointFromViewToSurface:(CGPoint)point
+{
+       CGRect bounds = [self bounds];
+       
+       return CGPointMake((point.x - bounds.origin.x) / bounds.size.width * size_.width, (point.y - bounds.origin.y) / bounds.size.height * size_.height);
+}
+
+- (CGRect) convertRectFromViewToSurface:(CGRect)rect
+{
+       CGRect bounds = [self bounds];
+       
+       return CGRectMake((rect.origin.x - bounds.origin.x) / bounds.size.width * size_.width, (rect.origin.y - bounds.origin.y) / bounds.size.height * size_.height, rect.size.width / bounds.size.width * size_.width, rect.size.height / bounds.size.height * size_.height);
+}
+
+// Pass the touches to the superview
+#pragma mark EAGLView - Touch Delegate
+
+- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
+{
+       if(touchDelegate_)
+       {
+               [touchDelegate_ touchesBegan:touches withEvent:event];
+       }
+}
+
+- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
+{
+       if(touchDelegate_)
+       {
+               [touchDelegate_ touchesMoved:touches withEvent:event];
+       }
+}
+
+- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
+{
+       if(touchDelegate_)
+       {
+               [touchDelegate_ touchesEnded:touches withEvent:event];
+       }
+}
+- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
+{
+       if(touchDelegate_)
+       {
+               [touchDelegate_ touchesCancelled:touches withEvent:event];
+       }
+}
+
+@end
+
+#endif // __IPHONE_OS_VERSION_MAX_ALLOWED
\ No newline at end of file
diff --git a/libs/cocos2d/Platforms/iOS/ES1Renderer.h b/libs/cocos2d/Platforms/iOS/ES1Renderer.h
new file mode 100644 (file)
index 0000000..fd946a7
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ *
+ * File autogenerated with Xcode. Adapted for cocos2d needs.
+ */
+
+// Only compile this code on iOS. These files should NOT be included on your Mac project.
+// But in case they are included, it won't be compiled.
+#import <Availability.h>
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+
+
+#import "ESRenderer.h"
+
+#import <OpenGLES/ES1/gl.h>
+#import <OpenGLES/ES1/glext.h>
+
+@interface ES1Renderer : NSObject <ESRenderer>
+{
+    // The pixel dimensions of the CAEAGLLayer
+    GLint backingWidth_;
+    GLint backingHeight_;
+
+       unsigned int    samplesToUse_;
+       BOOL                    multiSampling_;
+       
+       unsigned int    depthFormat_;
+       unsigned int    pixelFormat_;
+
+       // The OpenGL ES names for the framebuffer and renderbuffer used to render to this view
+    GLuint defaultFramebuffer_;
+       GLuint colorRenderbuffer_;
+       GLuint depthBuffer_;
+       
+       
+       //buffers for MSAA
+       GLuint msaaFramebuffer_;
+       GLuint msaaColorbuffer_;
+       
+       EAGLContext *context_;
+}
+
+/** EAGLContext */
+@property (nonatomic,readonly) EAGLContext* context;
+
+- (BOOL)resizeFromLayer:(CAEAGLLayer *)layer;
+
+@end
+
+#endif // __IPHONE_OS_VERSION_MAX_ALLOWED
diff --git a/libs/cocos2d/Platforms/iOS/ES1Renderer.m b/libs/cocos2d/Platforms/iOS/ES1Renderer.m
new file mode 100644 (file)
index 0000000..d981df7
--- /dev/null
@@ -0,0 +1,253 @@
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ *
+ * File autogenerated with Xcode. Adapted for cocos2d needs.
+ */
+
+// Only compile this code on iOS. These files should NOT be included on your Mac project.
+// But in case they are included, it won't be compiled.
+#import <Availability.h>
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+
+#import "ES1Renderer.h"
+#import "../../Support/OpenGL_Internal.h"
+#import "../../ccMacros.h"
+
+
+@interface ES1Renderer (private)
+
+- (GLenum) convertPixelFormat:(int) pixelFormat;
+
+@end
+
+
+@implementation ES1Renderer
+
+@synthesize context=context_;
+
+- (id) initWithDepthFormat:(unsigned int)depthFormat withPixelFormat:(unsigned int)pixelFormat withSharegroup:(EAGLSharegroup*)sharegroup withMultiSampling:(BOOL) multiSampling withNumberOfSamples:(unsigned int) requestedSamples
+{
+    if ((self = [super init]))
+    {
+               if ( sharegroup == nil )
+               {
+                       context_ = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES1];
+               }
+               else
+               {
+                       context_ = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES1 sharegroup:sharegroup];
+               }
+
+        if (!context_ || ![EAGLContext setCurrentContext:context_])
+        {
+            [self release];
+            return nil;
+        }
+
+        // Create default framebuffer object. The backing will be allocated for the current layer in -resizeFromLayer
+        glGenFramebuffersOES(1, &defaultFramebuffer_);
+               NSAssert( defaultFramebuffer_, @"Can't create default frame buffer");
+        glGenRenderbuffersOES(1, &colorRenderbuffer_);
+               NSAssert( colorRenderbuffer_, @"Can't create default render buffer");
+
+        glBindFramebufferOES(GL_FRAMEBUFFER_OES, defaultFramebuffer_);
+        glBindRenderbufferOES(GL_RENDERBUFFER_OES, colorRenderbuffer_);
+        glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_COLOR_ATTACHMENT0_OES, GL_RENDERBUFFER_OES, colorRenderbuffer_);
+
+               depthFormat_ = depthFormat;
+               
+               if( depthFormat_ ) {
+//                     glGenRenderbuffersOES(1, &depthBuffer_);
+//                     glBindRenderbufferOES(GL_RENDERBUFFER_OES, depthBuffer_);
+//                     glRenderbufferStorageOES(GL_RENDERBUFFER_OES, depthFormat_, 100, 100);
+//                     glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_DEPTH_ATTACHMENT_OES, GL_RENDERBUFFER_OES, depthBuffer_);
+
+                       // default buffer
+//                     glBindRenderbufferOES(GL_RENDERBUFFER_OES, colorRenderbuffer_);
+               }
+               
+               pixelFormat_ = pixelFormat;
+               multiSampling_ = multiSampling; 
+               if (multiSampling_)
+               {
+                       GLint maxSamplesAllowed;
+                       glGetIntegerv(GL_MAX_SAMPLES_APPLE, &maxSamplesAllowed);
+                       samplesToUse_ = MIN(maxSamplesAllowed,requestedSamples);
+                       
+                       /* Create the MSAA framebuffer (offscreen) */
+                       glGenFramebuffersOES(1, &msaaFramebuffer_);
+                       glBindFramebufferOES(GL_FRAMEBUFFER_OES, msaaFramebuffer_);
+                       
+               }
+
+               CHECK_GL_ERROR();
+    }
+
+    return self;
+}
+
+- (BOOL)resizeFromLayer:(CAEAGLLayer *)layer
+{      
+    // Allocate color buffer backing based on the current layer size
+
+    glBindRenderbufferOES(GL_RENDERBUFFER_OES, colorRenderbuffer_);
+
+       if (![context_ renderbufferStorage:GL_RENDERBUFFER_OES fromDrawable:layer])
+       {
+               CCLOG(@"failed to call context");       
+       }
+
+    glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_WIDTH_OES, &backingWidth_);
+    glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_HEIGHT_OES, &backingHeight_);
+
+       CCLOG(@"cocos2d: surface size: %dx%d", (int)backingWidth_, (int)backingHeight_);
+
+       if (multiSampling_)
+       {
+               /* Create the offscreen MSAA color buffer.
+                After rendering, the contents of this will be blitted into ColorRenderbuffer */
+               
+               //msaaFrameBuffer needs to be binded
+               glBindFramebufferOES(GL_FRAMEBUFFER_OES, msaaFramebuffer_);
+               glGenRenderbuffersOES(1, &msaaColorbuffer_);
+               glBindRenderbufferOES(GL_RENDERBUFFER_OES, msaaColorbuffer_);
+               glRenderbufferStorageMultisampleAPPLE(GL_RENDERBUFFER_OES, samplesToUse_,pixelFormat_ , backingWidth_, backingHeight_);
+               glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_COLOR_ATTACHMENT0_OES, GL_RENDERBUFFER_OES, msaaColorbuffer_);
+
+               if (glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES) != GL_FRAMEBUFFER_COMPLETE_OES)
+               {
+                       CCLOG(@"Failed to make complete framebuffer object %x", glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES));
+                       return NO;
+               }
+       }
+       
+       if (depthFormat_) 
+       {
+               if( ! depthBuffer_ )
+                       glGenRenderbuffersOES(1, &depthBuffer_);
+               
+               glBindRenderbufferOES(GL_RENDERBUFFER_OES, depthBuffer_);
+               if( multiSampling_ )
+                       glRenderbufferStorageMultisampleAPPLE(GL_RENDERBUFFER_OES, samplesToUse_, depthFormat_,backingWidth_, backingHeight_);
+               else
+                       glRenderbufferStorageOES(GL_RENDERBUFFER_OES, depthFormat_, backingWidth_, backingHeight_);
+               glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_DEPTH_ATTACHMENT_OES, GL_RENDERBUFFER_OES, depthBuffer_);
+               
+               // bind color buffer
+               glBindRenderbufferOES(GL_RENDERBUFFER_OES, colorRenderbuffer_);
+       }
+       
+       glBindFramebufferOES(GL_FRAMEBUFFER_OES, defaultFramebuffer_);
+       
+       if (glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES) != GL_FRAMEBUFFER_COMPLETE_OES)
+       {
+               CCLOG(@"Failed to make complete framebuffer object %x", glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES));
+               return NO;
+       }
+
+       CHECK_GL_ERROR();
+
+    return YES;
+}
+
+-(CGSize) backingSize
+{
+       return CGSizeMake( backingWidth_, backingHeight_);
+}
+
+- (NSString*) description
+{
+       return [NSString stringWithFormat:@"<%@ = %08X | size = %ix%i>", [self class], self, backingWidth_, backingHeight_];
+}
+
+
+- (void)dealloc
+{
+       CCLOGINFO(@"cocos2d: deallocing %@", self);
+
+    // Tear down GL
+    if(defaultFramebuffer_)
+    {
+        glDeleteFramebuffersOES(1, &defaultFramebuffer_);
+        defaultFramebuffer_ = 0;
+    }
+
+    if(colorRenderbuffer_)
+    {
+        glDeleteRenderbuffersOES(1, &colorRenderbuffer_);
+        colorRenderbuffer_ = 0;
+    }
+
+       if( depthBuffer_ )
+       {
+               glDeleteRenderbuffersOES(1, &depthBuffer_);
+               depthBuffer_ = 0;
+       }
+
+       if ( msaaColorbuffer_)
+       {
+               glDeleteRenderbuffersOES(1, &msaaColorbuffer_);
+               msaaColorbuffer_ = 0;
+       }
+       
+       if ( msaaFramebuffer_)
+       {
+               glDeleteRenderbuffersOES(1, &msaaFramebuffer_);
+               msaaFramebuffer_ = 0;
+       }
+       
+    // Tear down context
+    if ([EAGLContext currentContext] == context_)
+        [EAGLContext setCurrentContext:nil];
+
+    [context_ release];
+    context_ = nil;
+
+    [super dealloc];
+}
+
+- (unsigned int) colorRenderBuffer
+{
+       return colorRenderbuffer_;
+}
+
+- (unsigned int) defaultFrameBuffer
+{
+       return defaultFramebuffer_;
+}
+
+- (unsigned int) msaaFrameBuffer
+{
+       return msaaFramebuffer_;        
+}
+
+- (unsigned int) msaaColorBuffer
+{
+       return msaaColorbuffer_;        
+}
+
+@end
+
+#endif // __IPHONE_OS_VERSION_MAX_ALLOWED
diff --git a/libs/cocos2d/Platforms/iOS/ESRenderer.h b/libs/cocos2d/Platforms/iOS/ESRenderer.h
new file mode 100644 (file)
index 0000000..e612eee
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ *
+ * File autogenerated with Xcode. Adapted for cocos2d needs.
+ */
+
+// Only compile this code on iOS. These files should NOT be included on your Mac project.
+// But in case they are included, it won't be compiled.
+#import <Availability.h>
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+
+#import <QuartzCore/QuartzCore.h>
+
+#import <OpenGLES/EAGL.h>
+#import <OpenGLES/EAGLDrawable.h>
+
+@protocol ESRenderer <NSObject>
+
+- (id) initWithDepthFormat:(unsigned int)depthFormat withPixelFormat:(unsigned int)pixelFormat withSharegroup:(EAGLSharegroup*)sharegroup withMultiSampling:(BOOL) multiSampling withNumberOfSamples:(unsigned int) requestedSamples;
+
+- (BOOL) resizeFromLayer:(CAEAGLLayer *)layer;
+
+- (EAGLContext*) context;
+- (CGSize) backingSize;
+
+- (unsigned int) colorRenderBuffer;
+- (unsigned int) defaultFrameBuffer;
+- (unsigned int) msaaFrameBuffer;
+- (unsigned int) msaaColorBuffer;
+@end
+
+#endif // __IPHONE_OS_VERSION_MAX_ALLOWED
diff --git a/libs/cocos2d/Platforms/iOS/glu.c b/libs/cocos2d/Platforms/iOS/glu.c
new file mode 100644 (file)
index 0000000..2e00d5f
--- /dev/null
@@ -0,0 +1,113 @@
+//
+// cocos2d (incomplete) GLU implementation
+//
+// gluLookAt and gluPerspective from:
+// http://jet.ro/creations (San Angeles Observation)
+// 
+// 
+
+// Only compile this code on iOS. These files should NOT be included on your Mac project.
+// But in case they are included, it won't be compiled.
+#import <Availability.h>
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+
+#import <OpenGLES/ES1/gl.h>
+#import <math.h>
+#import "../../Support/OpenGL_Internal.h"
+#include "glu.h"
+
+void gluPerspective(GLfloat fovy, GLfloat aspect, GLfloat zNear, GLfloat zFar)
+{      
+       GLfloat xmin, xmax, ymin, ymax;
+               
+       ymax = zNear * (GLfloat)tanf(fovy * (float)M_PI / 360);
+       ymin = -ymax;
+       xmin = ymin * aspect;
+       xmax = ymax * aspect;
+               
+       glFrustumf(xmin, xmax,
+                               ymin, ymax,
+                               zNear, zFar);   
+}
+
+void gluLookAt(GLfloat eyex, GLfloat eyey, GLfloat eyez,
+                                         GLfloat centerx, GLfloat centery, GLfloat centerz,
+                                         GLfloat upx, GLfloat upy, GLfloat upz)
+{
+    GLfloat m[16];
+    GLfloat x[3], y[3], z[3];
+    GLfloat mag;
+       
+    /* Make rotation matrix */
+       
+    /* Z vector */
+    z[0] = eyex - centerx;
+    z[1] = eyey - centery;
+    z[2] = eyez - centerz;
+    mag = (float)sqrtf(z[0] * z[0] + z[1] * z[1] + z[2] * z[2]);
+    if (mag) {
+        z[0] /= mag;
+        z[1] /= mag;
+        z[2] /= mag;
+    }
+       
+    /* Y vector */
+    y[0] = upx;
+    y[1] = upy;
+    y[2] = upz;
+       
+    /* X vector = Y cross Z */
+    x[0] = y[1] * z[2] - y[2] * z[1];
+    x[1] = -y[0] * z[2] + y[2] * z[0];
+    x[2] = y[0] * z[1] - y[1] * z[0];
+       
+    /* Recompute Y = Z cross X */
+    y[0] = z[1] * x[2] - z[2] * x[1];
+    y[1] = -z[0] * x[2] + z[2] * x[0];
+    y[2] = z[0] * x[1] - z[1] * x[0];
+       
+    /* cross product gives area of parallelogram, which is < 1.0 for
+     * non-perpendicular unit-length vectors; so normalize x, y here
+     */
+       
+    mag = (float)sqrtf(x[0] * x[0] + x[1] * x[1] + x[2] * x[2]);
+    if (mag) {
+        x[0] /= mag;
+        x[1] /= mag;
+        x[2] /= mag;
+    }
+       
+    mag = (float)sqrtf(y[0] * y[0] + y[1] * y[1] + y[2] * y[2]);
+    if (mag) {
+        y[0] /= mag;
+        y[1] /= mag;
+        y[2] /= mag;
+    }
+       
+#define M(row,col)  m[col*4+row]
+    M(0, 0) = x[0];
+    M(0, 1) = x[1];
+    M(0, 2) = x[2];
+    M(0, 3) = 0.0f;
+    M(1, 0) = y[0];
+    M(1, 1) = y[1];
+    M(1, 2) = y[2];
+    M(1, 3) = 0.0f;
+    M(2, 0) = z[0];
+    M(2, 1) = z[1];
+    M(2, 2) = z[2];
+    M(2, 3) = 0.0f;
+    M(3, 0) = 0.0f;
+    M(3, 1) = 0.0f;
+    M(3, 2) = 0.0f;
+    M(3, 3) = 1.0f;
+#undef M
+       
+       glMultMatrixf(m);
+
+       
+    /* Translate Eye to Origin */
+    glTranslatef(-eyex, -eyey, -eyez);
+}
+
+#endif // __IPHONE_OS_VERSION_MAX_ALLOWED
diff --git a/libs/cocos2d/Platforms/iOS/glu.h b/libs/cocos2d/Platforms/iOS/glu.h
new file mode 100644 (file)
index 0000000..86dcac7
--- /dev/null
@@ -0,0 +1,29 @@
+//
+// cocos2d GLU implementation
+//
+// implementation of GLU functions
+//
+#ifndef __COCOS2D_GLU_H
+#define __COCOS2D_GLU_H
+
+// Only compile this code on iOS. These files should NOT be included on your Mac project.
+// But in case they are included, it won't be compiled.
+#import <Availability.h>
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+
+#import <OpenGLES/ES1/gl.h>
+
+/**
+ @file
+ cocos2d OpenGL GLU implementation
+ */
+
+/** OpenGL gluLookAt implementation */
+void gluLookAt(float eyeX, float eyeY, float eyeZ, float lookAtX, float lookAtY, float lookAtZ, float upX, float upY, float upZ);
+/** OpenGL gluPerspective implementation */
+void gluPerspective(GLfloat fovy, GLfloat aspect, GLfloat zNear, GLfloat zFar);
+
+#endif // __IPHONE_OS_VERSION_MAX_ALLOWED
+
+#endif /* __COCOS2D_GLU_H */
+
diff --git a/libs/cocos2d/Support/CCArray.h b/libs/cocos2d/Support/CCArray.h
new file mode 100644 (file)
index 0000000..f26e184
--- /dev/null
@@ -0,0 +1,106 @@
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2010 ForzeField Studios S.L. http://forzefield.com
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+
+#import "ccCArray.h"
+
+
+/** A faster alternative of NSArray.
+ CCArray uses internally a c-array.
+ @since v0.99.4
+ */
+
+
+/** @def CCARRAY_FOREACH
+ A convience macro to iterate over a CCArray using. It is faster than the "fast enumeration" interface.
+ @since v0.99.4
+ */
+
+#define CCARRAY_FOREACH(__array__, __object__)                                                                                         \
+if (__array__ && __array__->data->num > 0)                                                                                                     \
+for(id *arr = __array__->data->arr, *end = __array__->data->arr + __array__->data->num-1;      \
+       arr <= end && ((__object__ = *arr) != nil || true);                                                                             \
+       arr++)
+
+@interface CCArray : NSObject <NSFastEnumeration, NSCoding, NSCopying>
+{
+       @public ccArray *data;
+}
+
++ (id) array;
++ (id) arrayWithCapacity:(NSUInteger)capacity;
++ (id) arrayWithArray:(CCArray*)otherArray;
++ (id) arrayWithNSArray:(NSArray*)otherArray;
+
+
+- (id) initWithCapacity:(NSUInteger)capacity;
+- (id) initWithArray:(CCArray*)otherArray;
+- (id) initWithNSArray:(NSArray*)otherArray;
+
+
+// Querying an Array
+
+- (NSUInteger) count;
+- (NSUInteger) capacity;
+- (NSUInteger) indexOfObject:(id)object;
+- (id) objectAtIndex:(NSUInteger)index;
+- (BOOL) containsObject:(id)object;
+- (id) randomObject;
+- (id) lastObject;
+- (NSArray*) getNSArray;
+
+
+// Adding Objects
+
+- (void) addObject:(id)object;
+- (void) addObjectsFromArray:(CCArray*)otherArray;
+- (void) addObjectsFromNSArray:(NSArray*)otherArray;
+- (void) insertObject:(id)object atIndex:(NSUInteger)index;
+
+
+// Removing Objects
+
+- (void) removeLastObject;
+- (void) removeObject:(id)object;
+- (void) removeObjectAtIndex:(NSUInteger)index;
+- (void) removeObjectsInArray:(CCArray*)otherArray;
+- (void) removeAllObjects;
+- (void) fastRemoveObject:(id)object;
+- (void) fastRemoveObjectAtIndex:(NSUInteger)index;
+
+
+// Rearranging Content
+
+- (void) exchangeObject:(id)object1 withObject:(id)object2;
+- (void) exchangeObjectAtIndex:(NSUInteger)index1 withObjectAtIndex:(NSUInteger)index2;
+- (void) reverseObjects;
+- (void) reduceMemoryFootprint;
+
+// Sending Messages to Elements
+
+- (void) makeObjectsPerformSelector:(SEL)aSelector;
+- (void) makeObjectsPerformSelector:(SEL)aSelector withObject:(id)object;
+
+
+@end
diff --git a/libs/cocos2d/Support/CCArray.m b/libs/cocos2d/Support/CCArray.m
new file mode 100644 (file)
index 0000000..a48a5f3
--- /dev/null
@@ -0,0 +1,290 @@
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2010 ForzeField Studios S.L. http://forzefield.com
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#import "CCArray.h"
+#import "../ccMacros.h"
+
+
+@implementation CCArray
+
++ (id) array
+{
+       return [[[self alloc] init] autorelease];
+}
+
++ (id) arrayWithCapacity:(NSUInteger)capacity
+{
+       return [[[self alloc] initWithCapacity:capacity] autorelease];
+}
+
++ (id) arrayWithArray:(CCArray*)otherArray
+{
+       return [[(CCArray*)[self alloc] initWithArray:otherArray] autorelease];
+}
+
++ (id) arrayWithNSArray:(NSArray*)otherArray
+{
+       return [[(CCArray*)[self alloc] initWithNSArray:otherArray] autorelease];
+}
+
+- (id) init
+{
+       self = [self initWithCapacity:2];
+       return self;
+}
+
+- (id) initWithCapacity:(NSUInteger)capacity
+{
+       self = [super init];
+       if (self != nil) {
+               data = ccArrayNew(capacity);
+       }
+       return self;
+}
+
+- (id) initWithArray:(CCArray*)otherArray
+{
+       self = [self initWithCapacity:otherArray->data->num];
+       if (self != nil) {
+               [self addObjectsFromArray:otherArray];
+       }
+       return self;
+}
+
+- (id) initWithNSArray:(NSArray*)otherArray
+{
+       self = [self initWithCapacity:otherArray.count];
+       if (self != nil) {
+               [self addObjectsFromNSArray:otherArray];
+       }
+       return self;
+}
+
+- (id) initWithCoder:(NSCoder*)coder
+{
+       self = [self initWithNSArray:[coder decodeObjectForKey:@"nsarray"]];
+       return self;
+}
+
+
+#pragma mark Querying an Array
+
+- (NSUInteger) count
+{
+       return data->num;
+}
+
+- (NSUInteger) capacity
+{
+       return data->max;
+}
+
+- (NSUInteger) indexOfObject:(id)object
+{
+       return ccArrayGetIndexOfObject(data, object);
+}
+
+- (id) objectAtIndex:(NSUInteger)index
+{
+       NSAssert2( index < data->num, @"index out of range in objectAtIndex(%d), index %i", data->num, index );
+       
+       return data->arr[index];
+}
+
+- (BOOL) containsObject:(id)object
+{
+       return ccArrayContainsObject(data, object);
+}
+
+- (id) lastObject
+{
+       if( data->num > 0 )
+               return data->arr[data->num-1];
+       return nil;
+}
+
+- (id) randomObject
+{
+       if(data->num==0) return nil;
+       return data->arr[(int)(data->num*CCRANDOM_0_1())];
+}
+
+- (NSArray*) getNSArray
+{
+       return [NSArray arrayWithObjects:data->arr count:data->num];
+}
+
+
+#pragma mark Adding Objects
+
+- (void) addObject:(id)object
+{
+       ccArrayAppendObjectWithResize(data, object);
+}
+
+- (void) addObjectsFromArray:(CCArray*)otherArray
+{
+       ccArrayAppendArrayWithResize(data, otherArray->data);
+}
+
+- (void) addObjectsFromNSArray:(NSArray*)otherArray
+{
+       ccArrayEnsureExtraCapacity(data, otherArray.count);
+       for(id object in otherArray)
+               ccArrayAppendObject(data, object);
+}
+
+- (void) insertObject:(id)object atIndex:(NSUInteger)index
+{
+       ccArrayInsertObjectAtIndex(data, object, index);
+}
+
+
+#pragma mark Removing Objects
+
+- (void) removeObject:(id)object
+{
+       ccArrayRemoveObject(data, object);
+}
+
+- (void) removeObjectAtIndex:(NSUInteger)index
+{
+       ccArrayRemoveObjectAtIndex(data, index);
+}
+
+- (void) fastRemoveObject:(id)object
+{
+       ccArrayFastRemoveObject(data, object);
+}
+
+- (void) fastRemoveObjectAtIndex:(NSUInteger)index
+{
+       ccArrayFastRemoveObjectAtIndex(data, index);
+}
+
+- (void) removeObjectsInArray:(CCArray*)otherArray
+{
+       ccArrayRemoveArray(data, otherArray->data);
+}
+
+- (void) removeLastObject
+{
+       NSAssert( data->num > 0, @"no objects added" );
+    
+       ccArrayRemoveObjectAtIndex(data, data->num-1);
+}
+
+- (void) removeAllObjects
+{
+       ccArrayRemoveAllObjects(data);
+}
+
+
+#pragma mark Rearranging Content
+
+- (void) exchangeObject:(id)object1 withObject:(id)object2
+{
+    NSUInteger index1 = ccArrayGetIndexOfObject(data, object1);
+    if(index1 == NSNotFound) return;
+    NSUInteger index2 = ccArrayGetIndexOfObject(data, object2);
+    if(index2 == NSNotFound) return;
+    
+    ccArraySwapObjectsAtIndexes(data, index1, index2);
+}
+
+- (void) exchangeObjectAtIndex:(NSUInteger)index1 withObjectAtIndex:(NSUInteger)index2
+{
+       ccArraySwapObjectsAtIndexes(data, index1, index2);
+}
+
+- (void) reverseObjects
+{
+       if (data->num > 1)
+       {
+               //floor it since in case of a oneven number the number of swaps stays the same
+               int count = (int) floorf(data->num/2.f); 
+               NSUInteger maxIndex = data->num - 1;
+               
+               for (int i = 0; i < count ; i++)
+               {
+                       ccArraySwapObjectsAtIndexes(data, i, maxIndex);
+                       maxIndex--;
+               }
+       }
+}
+
+- (void) reduceMemoryFootprint
+{
+       ccArrayShrink(data);
+}
+
+#pragma mark Sending Messages to Elements
+
+- (void) makeObjectsPerformSelector:(SEL)aSelector
+{
+       ccArrayMakeObjectsPerformSelector(data, aSelector);
+}
+
+- (void) makeObjectsPerformSelector:(SEL)aSelector withObject:(id)object
+{
+       ccArrayMakeObjectsPerformSelectorWithObject(data, aSelector, object);
+}
+
+
+#pragma mark CCArray - NSFastEnumeration protocol
+
+- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(id *)stackbuf count:(NSUInteger)len
+{
+       if(state->state == 1) return 0;
+       
+       state->mutationsPtr = (unsigned long *)self;
+       state->itemsPtr = &data->arr[0];
+       state->state = 1;
+       return data->num;
+}
+
+
+#pragma mark CCArray - NSCopying protocol
+
+- (id)copyWithZone:(NSZone *)zone
+{
+       NSArray *nsArray = [self getNSArray];
+       CCArray *newArray = [[[self class] allocWithZone:zone] initWithNSArray:nsArray];
+       return newArray;
+}
+
+- (void) encodeWithCoder:(NSCoder *)coder
+{
+       [coder encodeObject:[self getNSArray] forKey:@"nsarray"];
+}
+
+#pragma mark
+
+- (void) dealloc
+{
+       ccArrayFree(data);
+       [super dealloc];
+}
+
+@end
diff --git a/libs/cocos2d/Support/CCFileUtils.h b/libs/cocos2d/Support/CCFileUtils.h
new file mode 100644 (file)
index 0000000..0455202
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2008-2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+
+#import <Foundation/Foundation.h>
+
+
+/** Helper class to handle file operations */
+@interface CCFileUtils : NSObject
+{
+}
+
+/** Returns the fullpath of an filename.
+ If this method is when Retina Display is enabled, then the
+ Retina Display suffix will be appended to the file (See ccConfig.h).
+ If the Retina Display image doesn't exist, then it will return the "non-Retina Display" image
+ */
++(NSString*) fullPathFromRelativePath:(NSString*) relPath;
+@end
+
+/** loads a file into memory.
+ the caller should release the allocated buffer.
+ @returns the size of the allocated buffer
+ @since v0.99.5
+ */
+NSInteger ccLoadFileIntoMemory(const char *filename, unsigned char **out);
+
+
+/** removes the HD suffix from a path
+ @returns NSString * without the HD suffix
+ @since v0.99.5
+ */
+NSString *ccRemoveHDSuffixFromFile( NSString *path );
+
diff --git a/libs/cocos2d/Support/CCFileUtils.m b/libs/cocos2d/Support/CCFileUtils.m
new file mode 100644 (file)
index 0000000..6d33799
--- /dev/null
@@ -0,0 +1,169 @@
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2008-2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+
+#import <Availability.h>
+#import "CCFileUtils.h"
+#import "../CCConfiguration.h"
+#import "../ccMacros.h"
+#import "../ccConfig.h"
+
+static NSFileManager *__localFileManager=nil;
+
+// 
+NSInteger ccLoadFileIntoMemory(const char *filename, unsigned char **out) 
+{ 
+       NSCAssert( out, @"ccLoadFileIntoMemory: invalid 'out' parameter");
+       NSCAssert( &*out, @"ccLoadFileIntoMemory: invalid 'out' parameter");
+
+       size_t size = 0;
+       FILE *f = fopen(filename, "rb");
+       if( !f ) { 
+               *out = NULL;
+               return -1;
+       } 
+       
+       fseek(f, 0, SEEK_END);
+       size = ftell(f);
+       fseek(f, 0, SEEK_SET);
+       
+       *out = malloc(size);
+       size_t read = fread(*out, 1, size, f);
+       if( read != size ) { 
+               free(*out);
+               *out = NULL;
+               return -1;
+       }
+       
+       fclose(f);
+       
+       return size;
+}
+
+NSString *ccRemoveHDSuffixFromFile( NSString *path )
+{
+#if CC_IS_RETINA_DISPLAY_SUPPORTED
+
+       if( CC_CONTENT_SCALE_FACTOR() == 2 ) {
+                               
+               NSString *name = [path lastPathComponent];
+               
+               // check if path already has the suffix.
+               if( [name rangeOfString:CC_RETINA_DISPLAY_FILENAME_SUFFIX].location != NSNotFound ) {
+                       
+                       CCLOG(@"cocos2d: Filename(%@) contains %@ suffix. Removing it. See cocos2d issue #1040", path, CC_RETINA_DISPLAY_FILENAME_SUFFIX);
+
+                       NSString *newLastname = [name stringByReplacingOccurrencesOfString:CC_RETINA_DISPLAY_FILENAME_SUFFIX withString:@""];
+                       
+                       NSString *pathWithoutLastname = [path stringByDeletingLastPathComponent];
+                       return [pathWithoutLastname stringByAppendingPathComponent:newLastname];
+               }               
+       }
+
+#endif // CC_IS_RETINA_DISPLAY_SUPPORTED
+
+       return path;
+
+}
+
+
+@implementation CCFileUtils
+
++(void) initialize
+{
+       if( self == [CCFileUtils class] )
+               __localFileManager = [[NSFileManager alloc] init];
+}
+
++(NSString*) getDoubleResolutionImage:(NSString*)path
+{
+#if CC_IS_RETINA_DISPLAY_SUPPORTED
+
+       if( CC_CONTENT_SCALE_FACTOR() == 2 )
+       {
+               
+               NSString *pathWithoutExtension = [path stringByDeletingPathExtension];
+               NSString *name = [pathWithoutExtension lastPathComponent];
+               
+               // check if path already has the suffix.
+               if( [name rangeOfString:CC_RETINA_DISPLAY_FILENAME_SUFFIX].location != NSNotFound ) {
+               
+                       CCLOG(@"cocos2d: WARNING Filename(%@) already has the suffix %@. Using it.", name, CC_RETINA_DISPLAY_FILENAME_SUFFIX);                  
+                       return path;
+               }
+
+               
+               NSString *extension = [path pathExtension];
+               
+               if( [extension isEqualToString:@"ccz"] || [extension isEqualToString:@"gz"] )
+               {
+                       // All ccz / gz files should be in the format filename.xxx.ccz
+                       // so we need to pull off the .xxx part of the extension as well
+                       extension = [NSString stringWithFormat:@"%@.%@", [pathWithoutExtension pathExtension], extension];
+                       pathWithoutExtension = [pathWithoutExtension stringByDeletingPathExtension];
+               }
+               
+               
+               NSString *retinaName = [pathWithoutExtension stringByAppendingString:CC_RETINA_DISPLAY_FILENAME_SUFFIX];
+               retinaName = [retinaName stringByAppendingPathExtension:extension];
+
+               if( [__localFileManager fileExistsAtPath:retinaName] )
+                       return retinaName;
+
+               CCLOG(@"cocos2d: CCFileUtils: Warning HD file not found: %@", [retinaName lastPathComponent] );
+       }
+       
+#endif // CC_IS_RETINA_DISPLAY_SUPPORTED
+       
+       return path;
+}
+
++(NSString*) fullPathFromRelativePath:(NSString*) relPath
+{
+       NSAssert(relPath != nil, @"CCFileUtils: Invalid path");
+
+       NSString *fullpath = nil;
+       
+       // only if it is not an absolute path
+       if( ! [relPath isAbsolutePath] )
+       {
+               NSString *file = [relPath lastPathComponent];
+               NSString *imageDirectory = [relPath stringByDeletingLastPathComponent];
+               
+               fullpath = [[NSBundle mainBundle] pathForResource:file
+                                                                                                  ofType:nil
+                                                                                         inDirectory:imageDirectory];
+       }
+       
+       if (fullpath == nil)
+               fullpath = relPath;
+       
+       fullpath = [self getDoubleResolutionImage:fullpath];
+       
+       return fullpath;        
+}
+
+@end
diff --git a/libs/cocos2d/Support/CCProfiling.h b/libs/cocos2d/Support/CCProfiling.h
new file mode 100644 (file)
index 0000000..b241fb9
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2010 Stuart Carnie
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+
+#import <Foundation/Foundation.h>
+#import <sys/time.h>
+
+@class CCProfilingTimer;
+
+@interface CCProfiler : NSObject {
+       NSMutableArray* activeTimers;
+}
+
++ (CCProfiler*)sharedProfiler;
++ (CCProfilingTimer*)timerWithName:(NSString*)timerName andInstance:(id)instance;
++ (void)releaseTimer:(CCProfilingTimer*)timer;
+- (void)displayTimers;
+
+@end
+
+
+@interface CCProfilingTimer : NSObject {
+       NSString* name;
+       struct timeval startTime;
+       double averageTime;
+}
+
+@end
+
+extern void CCProfilingBeginTimingBlock(CCProfilingTimer* timer);
+extern void CCProfilingEndTimingBlock(CCProfilingTimer* timer);
diff --git a/libs/cocos2d/Support/CCProfiling.m b/libs/cocos2d/Support/CCProfiling.m
new file mode 100644 (file)
index 0000000..13c8c81
--- /dev/null
@@ -0,0 +1,117 @@
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2010 Stuart Carnie
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+#import "../ccConfig.h"
+
+#if CC_ENABLE_PROFILERS
+
+#import "CCProfiling.h"
+
+@interface CCProfilingTimer()
+- (id)initWithName:(NSString*)timerName andInstance:(id)instance;
+@end
+
+@implementation CCProfiler
+
+static CCProfiler* g_sharedProfiler;
+
++ (CCProfiler*)sharedProfiler {
+       if (!g_sharedProfiler)
+               g_sharedProfiler = [[CCProfiler alloc] init];
+       
+       return g_sharedProfiler;
+}
+
++ (CCProfilingTimer*)timerWithName:(NSString*)timerName andInstance:(id)instance {
+       CCProfiler* p = [CCProfiler sharedProfiler];
+       CCProfilingTimer* t = [[CCProfilingTimer alloc] initWithName:timerName andInstance:instance];
+       [p->activeTimers addObject:t];
+       [t release];
+       return t;
+}
+
++ (void)releaseTimer:(CCProfilingTimer*)timer {
+       CCProfiler* p = [CCProfiler sharedProfiler];
+       [p->activeTimers removeObject:timer];
+}
+
+- (id)init {
+       if (!(self = [super init])) return nil;
+       
+       activeTimers = [[NSMutableArray alloc] init];
+       
+       return self;
+}
+
+- (void)dealloc {
+       [activeTimers release];
+       [super dealloc];
+}
+
+- (void)displayTimers {        
+       for (id timer in activeTimers) {
+               printf("%s\n", [[timer description] cStringUsingEncoding:[NSString defaultCStringEncoding]]);
+       }
+}
+
+@end
+
+@implementation CCProfilingTimer
+
+- (id)initWithName:(NSString*)timerName andInstance:(id)instance {
+       if (!(self = [super init])) return nil;
+       
+       name = [[NSString stringWithFormat:@"%@ (0x%.8x)", timerName, instance] retain];
+       
+       return self;
+}
+
+- (void)dealloc {
+       [name release];
+       [super dealloc];
+}
+
+- (NSString*)description {
+       return [NSString stringWithFormat:@"%@ : avg time, %fms", name, averageTime];
+}
+
+void CCProfilingBeginTimingBlock(CCProfilingTimer* timer) {
+       gettimeofday(&timer->startTime, NULL);
+}
+
+typedef unsigned int uint32;
+void CCProfilingEndTimingBlock(CCProfilingTimer* timer) {
+       struct timeval currentTime;
+       gettimeofday(&currentTime, NULL);
+       timersub(&currentTime, &timer->startTime, &currentTime);
+       double duration = currentTime.tv_sec * 1000.0 + currentTime.tv_usec / 1000.0;
+       
+       // return in milliseconds
+       timer->averageTime = (timer->averageTime + duration) / 2.0f;
+}
+
+@end
+
+#endif
diff --git a/libs/cocos2d/Support/CGPointExtension.h b/libs/cocos2d/Support/CGPointExtension.h
new file mode 100644 (file)
index 0000000..40dd256
--- /dev/null
@@ -0,0 +1,322 @@
+/* cocos2d for iPhone
+ * http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2007 Scott Lembcke
+ *
+ * Copyright (c) 2010 Lam Pham
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+/*
+ * Some of the functions were based on Chipmunk's cpVect.h.
+ */
+
+/**
+ @file
+ CGPoint extensions based on Chipmunk's cpVect file.
+ These extensions work both with CGPoint and cpVect.
+ The "ccp" prefix means: "CoCos2d Point"
+ Examples:
+  - ccpAdd( ccp(1,1), ccp(2,2) ); // preferred cocos2d way
+  - ccpAdd( CGPointMake(1,1), CGPointMake(2,2) ); // also ok but more verbose
+  
+  - cpvadd( cpv(1,1), cpv(2,2) ); // way of the chipmunk
+  - ccpAdd( cpv(1,1), cpv(2,2) ); // mixing chipmunk and cocos2d (avoid)
+  - cpvadd( CGPointMake(1,1), CGPointMake(2,2) ); // mixing chipmunk and CG (avoid)
+ */
+
+#import <Availability.h>
+
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+#import <CoreGraphics/CGGeometry.h>
+#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED)
+#import <Foundation/Foundation.h>
+#endif
+
+#import <math.h>
+#import <objc/objc.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif 
+
+/** Helper macro that creates a CGPoint
+ @return CGPoint
+ @since v0.7.2
+ */
+#define ccp(__X__,__Y__) CGPointMake(__X__,__Y__)
+
+
+/** Returns opposite of point.
+ @return CGPoint
+ @since v0.7.2
+ */
+static inline CGPoint
+ccpNeg(const CGPoint v)
+{
+       return ccp(-v.x, -v.y);
+}
+
+/** Calculates sum of two points.
+ @return CGPoint
+ @since v0.7.2
+ */
+static inline CGPoint
+ccpAdd(const CGPoint v1, const CGPoint v2)
+{
+       return ccp(v1.x + v2.x, v1.y + v2.y);
+}
+
+/** Calculates difference of two points.
+ @return CGPoint
+ @since v0.7.2
+ */
+static inline CGPoint
+ccpSub(const CGPoint v1, const CGPoint v2)
+{
+       return ccp(v1.x - v2.x, v1.y - v2.y);
+}
+
+/** Returns point multiplied by given factor.
+ @return CGPoint
+ @since v0.7.2
+ */
+static inline CGPoint
+ccpMult(const CGPoint v, const CGFloat s)
+{
+       return ccp(v.x*s, v.y*s);
+}
+
+/** Calculates midpoint between two points.
+ @return CGPoint
+ @since v0.7.2
+ */
+static inline CGPoint
+ccpMidpoint(const CGPoint v1, const CGPoint v2)
+{
+       return ccpMult(ccpAdd(v1, v2), 0.5f);
+}
+
+/** Calculates dot product of two points.
+ @return CGFloat
+ @since v0.7.2
+ */
+static inline CGFloat
+ccpDot(const CGPoint v1, const CGPoint v2)
+{
+       return v1.x*v2.x + v1.y*v2.y;
+}
+
+/** Calculates cross product of two points.
+ @return CGFloat
+ @since v0.7.2
+ */
+static inline CGFloat
+ccpCross(const CGPoint v1, const CGPoint v2)
+{
+       return v1.x*v2.y - v1.y*v2.x;
+}
+
+/** Calculates perpendicular of v, rotated 90 degrees counter-clockwise -- cross(v, perp(v)) >= 0
+ @return CGPoint
+ @since v0.7.2
+ */
+static inline CGPoint
+ccpPerp(const CGPoint v)
+{
+       return ccp(-v.y, v.x);
+}
+
+/** Calculates perpendicular of v, rotated 90 degrees clockwise -- cross(v, rperp(v)) <= 0
+ @return CGPoint
+ @since v0.7.2
+ */
+static inline CGPoint
+ccpRPerp(const CGPoint v)
+{
+       return ccp(v.y, -v.x);
+}
+
+/** Calculates the projection of v1 over v2.
+ @return CGPoint
+ @since v0.7.2
+ */
+static inline CGPoint
+ccpProject(const CGPoint v1, const CGPoint v2)
+{
+       return ccpMult(v2, ccpDot(v1, v2)/ccpDot(v2, v2));
+}
+
+/** Rotates two points.
+ @return CGPoint
+ @since v0.7.2
+ */
+static inline CGPoint
+ccpRotate(const CGPoint v1, const CGPoint v2)
+{
+       return ccp(v1.x*v2.x - v1.y*v2.y, v1.x*v2.y + v1.y*v2.x);
+}
+
+/** Unrotates two points.
+ @return CGPoint
+ @since v0.7.2
+ */
+static inline CGPoint
+ccpUnrotate(const CGPoint v1, const CGPoint v2)
+{
+       return ccp(v1.x*v2.x + v1.y*v2.y, v1.y*v2.x - v1.x*v2.y);
+}
+
+/** Calculates the square length of a CGPoint (not calling sqrt() )
+ @return CGFloat
+ @since v0.7.2
+ */
+static inline CGFloat
+ccpLengthSQ(const CGPoint v)
+{
+       return ccpDot(v, v);
+}
+
+/** Calculates distance between point an origin
+ @return CGFloat
+ @since v0.7.2
+ */
+CGFloat ccpLength(const CGPoint v);
+
+/** Calculates the distance between two points
+ @return CGFloat
+ @since v0.7.2
+ */
+CGFloat ccpDistance(const CGPoint v1, const CGPoint v2);
+
+/** Returns point multiplied to a length of 1.
+ @return CGPoint
+ @since v0.7.2
+ */
+CGPoint ccpNormalize(const CGPoint v);
+
+/** Converts radians to a normalized vector.
+ @return CGPoint
+ @since v0.7.2
+ */
+CGPoint ccpForAngle(const CGFloat a);
+
+/** Converts a vector to radians.
+ @return CGFloat
+ @since v0.7.2
+ */
+CGFloat ccpToAngle(const CGPoint v);
+
+
+/** Clamp a value between from and to.
+ @since v0.99.1
+ */
+float clampf(float value, float min_inclusive, float max_inclusive);
+
+/** Clamp a point between from and to.
+ @since v0.99.1
+ */
+CGPoint ccpClamp(CGPoint p, CGPoint from, CGPoint to);
+
+/** Quickly convert CGSize to a CGPoint
+ @since v0.99.1
+ */
+CGPoint ccpFromSize(CGSize s);
+
+/** Run a math operation function on each point component
+ * absf, fllorf, ceilf, roundf
+ * any function that has the signature: float func(float);
+ * For example: let's try to take the floor of x,y
+ * ccpCompOp(p,floorf);
+ @since v0.99.1
+ */
+CGPoint ccpCompOp(CGPoint p, float (*opFunc)(float));
+
+/** Linear Interpolation between two points a and b
+ @returns
+       alpha == 0 ? a
+       alpha == 1 ? b
+       otherwise a value between a..b
+ @since v0.99.1
+ */
+CGPoint ccpLerp(CGPoint a, CGPoint b, float alpha);
+
+
+/** @returns if points have fuzzy equality which means equal with some degree of variance.
+ @since v0.99.1
+ */
+BOOL ccpFuzzyEqual(CGPoint a, CGPoint b, float variance);
+
+
+/** Multiplies a nd b components, a.x*b.x, a.y*b.y
+ @returns a component-wise multiplication
+ @since v0.99.1
+ */
+CGPoint ccpCompMult(CGPoint a, CGPoint b);
+
+/** @returns the signed angle in radians between two vector directions
+ @since v0.99.1
+ */
+float ccpAngleSigned(CGPoint a, CGPoint b);
+
+/** @returns the angle in radians between two vector directions
+ @since v0.99.1
+*/
+float ccpAngle(CGPoint a, CGPoint b);
+
+/** Rotates a point counter clockwise by the angle around a pivot
+ @param v is the point to rotate
+ @param pivot is the pivot, naturally
+ @param angle is the angle of rotation cw in radians
+ @returns the rotated point
+ @since v0.99.1
+ */
+CGPoint ccpRotateByAngle(CGPoint v, CGPoint pivot, float angle);
+
+/** A general line-line intersection test
+ @param p1 
+       is the startpoint for the first line P1 = (p1 - p2)
+ @param p2 
+       is the endpoint for the first line P1 = (p1 - p2)
+ @param p3 
+       is the startpoint for the second line P2 = (p3 - p4)
+ @param p4 
+       is the endpoint for the second line P2 = (p3 - p4)
+ @param s 
+       is the range for a hitpoint in P1 (pa = p1 + s*(p2 - p1))
+ @param t
+       is the range for a hitpoint in P3 (pa = p2 + t*(p4 - p3))
+ @return bool 
+       indicating successful intersection of a line
+       note that to truly test intersection for segments we have to make 
+       sure that s & t lie within [0..1] and for rays, make sure s & t > 0
+       the hit point is                p3 + t * (p4 - p3);
+       the hit point also is   p1 + s * (p2 - p1);
+ @since v0.99.1
+ */
+BOOL ccpLineIntersect(CGPoint p1, CGPoint p2, 
+                                         CGPoint p3, CGPoint p4,
+                                         float *s, float *t);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/libs/cocos2d/Support/CGPointExtension.m b/libs/cocos2d/Support/CGPointExtension.m
new file mode 100644 (file)
index 0000000..3330541
--- /dev/null
@@ -0,0 +1,181 @@
+/* cocos2d for iPhone
+ * http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2007 Scott Lembcke
+ *
+ * Copyright (c) 2010 Lam Pham
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "stdio.h"
+#include "math.h"
+
+#import "../ccMacros.h"                // CC_SWAP
+#include "CGPointExtension.h"
+
+#define kCGPointEpsilon FLT_EPSILON
+
+CGFloat
+ccpLength(const CGPoint v)
+{
+       return sqrtf(ccpLengthSQ(v));
+}
+
+CGFloat
+ccpDistance(const CGPoint v1, const CGPoint v2)
+{
+       return ccpLength(ccpSub(v1, v2));
+}
+
+CGPoint
+ccpNormalize(const CGPoint v)
+{
+       return ccpMult(v, 1.0f/ccpLength(v));
+}
+
+CGPoint
+ccpForAngle(const CGFloat a)
+{
+       return ccp(cosf(a), sinf(a));
+}
+
+CGFloat
+ccpToAngle(const CGPoint v)
+{
+       return atan2f(v.y, v.x);
+}
+
+CGPoint ccpLerp(CGPoint a, CGPoint b, float alpha)
+{
+       return ccpAdd(ccpMult(a, 1.f - alpha), ccpMult(b, alpha));
+}
+
+float clampf(float value, float min_inclusive, float max_inclusive)
+{
+       if (min_inclusive > max_inclusive) {
+               CC_SWAP(min_inclusive,max_inclusive);
+       }
+       return value < min_inclusive ? min_inclusive : value < max_inclusive? value : max_inclusive;
+}
+
+CGPoint ccpClamp(CGPoint p, CGPoint min_inclusive, CGPoint max_inclusive)
+{
+       return ccp(clampf(p.x,min_inclusive.x,max_inclusive.x), clampf(p.y, min_inclusive.y, max_inclusive.y));
+}
+
+CGPoint ccpFromSize(CGSize s)
+{
+       return ccp(s.width, s.height);
+}
+
+CGPoint ccpCompOp(CGPoint p, float (*opFunc)(float))
+{
+       return ccp(opFunc(p.x), opFunc(p.y));
+}
+
+BOOL ccpFuzzyEqual(CGPoint a, CGPoint b, float var)
+{
+       if(a.x - var <= b.x && b.x <= a.x + var)
+               if(a.y - var <= b.y && b.y <= a.y + var)
+                       return true;
+       return false;
+}
+
+CGPoint ccpCompMult(CGPoint a, CGPoint b)
+{
+       return ccp(a.x * b.x, a.y * b.y);
+}
+
+float ccpAngleSigned(CGPoint a, CGPoint b)
+{
+       CGPoint a2 = ccpNormalize(a);
+       CGPoint b2 = ccpNormalize(b);
+       float angle = atan2f(a2.x * b2.y - a2.y * b2.x, ccpDot(a2, b2));
+       if( fabs(angle) < kCGPointEpsilon ) return 0.f;
+       return angle;
+}
+
+CGPoint ccpRotateByAngle(CGPoint v, CGPoint pivot, float angle)
+{
+       CGPoint r = ccpSub(v, pivot);
+       float cosa = cosf(angle), sina = sinf(angle);
+       float t = r.x;
+       r.x = t*cosa - r.y*sina + pivot.x;
+       r.y = t*sina + r.y*cosa + pivot.y;
+       return r;
+}
+
+BOOL ccpLineIntersect(CGPoint A, CGPoint B,
+                                         CGPoint C, CGPoint D,
+                                         float *S, float *T)
+{    
+       // FAIL: Line undefined
+       if ( (A.x==B.x && A.y==B.y) || (C.x==D.x && C.y==D.y) ) return NO;
+    
+       //  Translate system to make A the origin
+       B.x-=A.x; B.y-=A.y;
+       C.x-=A.x; C.y-=A.y;
+       D.x-=A.x; D.y-=A.y;
+
+       // Cache
+       CGPoint C2 = C, D2 = D;
+
+       // Length of segment AB
+       float distAB = sqrtf(B.x*B.x+B.y*B.y);
+
+       // Rotate the system so that point B is on the positive X axis.
+       float theCos = B.x/distAB;
+       float theSin = B.y/distAB;
+       float newX = C.x*theCos+C.y*theSin;
+       C.y  = C.y*theCos-C.x*theSin; C.x = newX;
+       newX = D.x*theCos+D.y*theSin;
+       D.y  = D.y*theCos-D.x*theSin; D.x = newX;
+
+       // FAIL: Lines are parallel.
+       if (C.y == D.y) return NO;
+
+       // Discover position of the intersection in the line AB
+       float ABpos = D.x+(C.x-D.x)*D.y/(D.y-C.y);
+
+       // Vector CD
+       C.x = D2.x-C2.x;
+       C.y = D2.y-C2.y;
+
+       // Vector between intersection and point C
+       A.x = ABpos*theCos-C2.x;
+       A.y = ABpos*theSin-C2.y;
+
+       newX = sqrtf((A.x*A.x+A.y*A.y)/(C.x*C.x+C.y*C.y));
+       if(((A.y<0) != (C.y<0)) || ((A.x<0) != (C.x<0)))
+               newX *= -1.0f;
+
+       *S = ABpos/distAB;
+       *T = newX;
+
+       // Success.
+       return YES;
+}
+
+float ccpAngle(CGPoint a, CGPoint b)
+{
+       float angle = acosf(ccpDot(ccpNormalize(a), ccpNormalize(b)));
+       if( fabs(angle) < kCGPointEpsilon ) return 0.f;
+       return angle;
+}
diff --git a/libs/cocos2d/Support/OpenGL_Internal.h b/libs/cocos2d/Support/OpenGL_Internal.h
new file mode 100644 (file)
index 0000000..4789683
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+
+===== IMPORTANT =====
+
+This is sample code demonstrating API, technology or techniques in development.
+Although this sample code has been reviewed for technical accuracy, it is not
+final. Apple is supplying this information to help you plan for the adoption of
+the technologies and programming interfaces described herein. This information
+is subject to change, and software implemented based on this sample code should
+be tested with final operating system software and final documentation. Newer
+versions of this sample code may be provided with future seeds of the API or
+technology. For information about updates to this and other developer
+documentation, view the New & Updated sidebars in subsequent documentation
+seeds.
+
+=====================
+
+File: OpenGL_Internal.h
+Abstract: This file is included for support purposes and isn't necessary for
+understanding this sample.
+
+Version: 1.0
+
+Disclaimer: IMPORTANT:  This Apple software is supplied to you by Apple Inc.
+("Apple") in consideration of your agreement to the following terms, and your
+use, installation, modification or redistribution of this Apple software
+constitutes acceptance of these terms.  If you do not agree with these terms,
+please do not use, install, modify or redistribute this Apple software.
+
+In consideration of your agreement to abide by the following terms, and subject
+to these terms, Apple grants you a personal, non-exclusive license, under
+Apple's copyrights in this original Apple software (the "Apple Software"), to
+use, reproduce, modify and redistribute the Apple Software, with or without
+modifications, in source and/or binary forms; provided that if you redistribute
+the Apple Software in its entirety and without modifications, you must retain
+this notice and the following text and disclaimers in all such redistributions
+of the Apple Software.
+Neither the name, trademarks, service marks or logos of Apple Inc. may be used
+to endorse or promote products derived from the Apple Software without specific
+prior written permission from Apple.  Except as expressly stated in this notice,
+no other rights or licenses, express or implied, are granted by Apple herein,
+including but not limited to any patent rights that may be infringed by your
+derivative works or by other works in which the Apple Software may be
+incorporated.
+
+The Apple Software is provided by Apple on an "AS IS" basis.  APPLE MAKES NO
+WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED
+WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN
+COMBINATION WITH YOUR PRODUCTS.
+
+IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR
+DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF
+CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF
+APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+Copyright (C) 2008 Apple Inc. All Rights Reserved.
+
+*/
+
+/* Generic error reporting */
+#define REPORT_ERROR(__FORMAT__, ...) printf("%s: %s\n", __FUNCTION__, [[NSString stringWithFormat:__FORMAT__, __VA_ARGS__] UTF8String])
+
+/* EAGL and GL functions calling wrappers that log on error */
+#define CALL_EAGL_FUNCTION(__FUNC__, ...) ({ EAGLError __error = __FUNC__( __VA_ARGS__ ); if(__error != kEAGLErrorSuccess) printf("%s() called from %s returned error %i\n", #__FUNC__, __FUNCTION__, __error); (__error ? NO : YES); })
+//#define CHECK_GL_ERROR() ({ GLenum __error = glGetError(); if(__error) printf("OpenGL error 0x%04X in %s\n", __error, __FUNCTION__); (__error ? NO : YES); })
+#define CHECK_GL_ERROR() ({ GLenum __error = glGetError(); if(__error) printf("OpenGL error 0x%04X in %s\n", __error, __FUNCTION__); })
+
+/* Optional delegate methods support */
+#ifndef __DELEGATE_IVAR__
+#define __DELEGATE_IVAR__ _delegate
+#endif
+#ifndef __DELEGATE_METHODS_IVAR__
+#define __DELEGATE_METHODS_IVAR__ _delegateMethods
+#endif
+#define TEST_DELEGATE_METHOD_BIT(__BIT__) (self->__DELEGATE_METHODS_IVAR__ & (1 << __BIT__))
+#define SET_DELEGATE_METHOD_BIT(__BIT__, __NAME__) { if([self->__DELEGATE_IVAR__ respondsToSelector:@selector(__NAME__)]) self->__DELEGATE_METHODS_IVAR__ |= (1 << __BIT__); else self->__DELEGATE_METHODS_IVAR__ &= ~(1 << __BIT__); }
diff --git a/libs/cocos2d/Support/TGAlib.h b/libs/cocos2d/Support/TGAlib.h
new file mode 100644 (file)
index 0000000..247084e
--- /dev/null
@@ -0,0 +1,55 @@
+//
+// TGA lib for cocos2d-iphone
+//
+// sources from: http://www.lighthouse3d.com/opengl/terrain/index.php3?tgasource
+//
+
+//#ifndef TGA_LIB
+//#define TGA_LIB
+
+/**
+ @file
+ TGA image support
+ */
+
+enum {
+       TGA_OK,
+       TGA_ERROR_FILE_OPEN,
+       TGA_ERROR_READING_FILE,
+       TGA_ERROR_INDEXED_COLOR,
+       TGA_ERROR_MEMORY,
+       TGA_ERROR_COMPRESSED_FILE,
+};
+
+/** TGA format */
+typedef struct sImageTGA {
+       int status;
+       unsigned char type, pixelDepth;
+       
+       /** map width */
+       short int width;
+       
+       /** map height */
+       short int height;
+       
+       /** raw data */
+       unsigned char *imageData;
+       int flipped;
+} tImageTGA;
+
+/// load the image header fields. We only keep those that matter!
+void tgaLoadHeader(FILE *file, tImageTGA *info);
+
+/// loads the image pixels. You shouldn't call this function directly
+void tgaLoadImageData(FILE *file, tImageTGA *info);
+
+/// this is the function to call when we want to load an image
+tImageTGA * tgaLoad(const char *filename);
+
+// /converts RGB to greyscale
+void tgaRGBtogreyscale(tImageTGA *info);
+
+/// releases the memory used for the image
+void tgaDestroy(tImageTGA *info);
+
+//#endif // TGA_LIB
diff --git a/libs/cocos2d/Support/TGAlib.m b/libs/cocos2d/Support/TGAlib.m
new file mode 100644 (file)
index 0000000..b574d59
--- /dev/null
@@ -0,0 +1,272 @@
+//
+// TGA lib for cocos2d-iphone
+//
+// sources from: http://www.lighthouse3d.com/opengl/terrain/index.php3?tgasource
+//
+// TGA RLE compression support by Ernesto Corvi
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#import "TGAlib.h"
+
+
+// load the image header fields. We only keep those that matter!
+void tgaLoadHeader(FILE *file, tImageTGA *info) {
+       unsigned char cGarbage;
+       short int iGarbage;
+
+       fread(&cGarbage, sizeof(unsigned char), 1, file);
+       fread(&cGarbage, sizeof(unsigned char), 1, file);
+
+       // type must be 2 or 3
+       fread(&info->type, sizeof(unsigned char), 1, file);
+
+       fread(&iGarbage, sizeof(short int), 1, file);
+       fread(&iGarbage, sizeof(short int), 1, file);
+       fread(&cGarbage, sizeof(unsigned char), 1, file);
+       fread(&iGarbage, sizeof(short int), 1, file);
+       fread(&iGarbage, sizeof(short int), 1, file);
+
+       fread(&info->width, sizeof(short int), 1, file);
+       fread(&info->height, sizeof(short int), 1, file);
+       fread(&info->pixelDepth, sizeof(unsigned char), 1, file);
+
+       fread(&cGarbage, sizeof(unsigned char), 1, file);
+       
+       info->flipped = 0;
+       if ( cGarbage & 0x20 ) info->flipped = 1;
+}
+
+// loads the image pixels. You shouldn't call this function directly
+void tgaLoadImageData(FILE *file, tImageTGA *info) {
+       
+       int mode,total,i;
+       unsigned char aux;
+       
+       // mode equal the number of components for each pixel
+       mode = info->pixelDepth / 8;
+       // total is the number of unsigned chars we'll have to read
+       total = info->height * info->width * mode;
+       
+       fread(info->imageData,sizeof(unsigned char),total,file);
+       
+       // mode=3 or 4 implies that the image is RGB(A). However TGA
+       // stores it as BGR(A) so we'll have to swap R and B.
+       if (mode >= 3)
+               for (i=0; i < total; i+= mode) {
+                       aux = info->imageData[i];
+                       info->imageData[i] = info->imageData[i+2];
+                       info->imageData[i+2] = aux;
+               }
+}
+
+// loads the RLE encoded image pixels. You shouldn't call this function directly
+void tgaLoadRLEImageData(FILE *file, tImageTGA *info)
+{
+       unsigned int mode,total,i, index = 0;
+       unsigned char aux[4], runlength = 0;
+       unsigned int skip = 0, flag = 0;
+       
+       // mode equal the number of components for each pixel
+       mode = info->pixelDepth / 8;
+       // total is the number of unsigned chars we'll have to read
+       total = info->height * info->width;
+       
+       for( i = 0; i < total; i++ )
+       {
+               // if we have a run length pending, run it
+               if ( runlength != 0 )
+               {
+                       // we do, update the run length count
+                       runlength--;
+                       skip = (flag != 0);
+               }
+               else
+               {
+                       // otherwise, read in the run length token
+                       if ( fread(&runlength,sizeof(unsigned char),1,file) != 1 )
+                               return;
+                       
+                       // see if it's a RLE encoded sequence
+                       flag = runlength & 0x80;
+                       if ( flag ) runlength -= 128;
+                       skip = 0;
+               }
+               
+               // do we need to skip reading this pixel?
+               if ( !skip )
+               {
+                       // no, read in the pixel data
+                       if ( fread(aux,sizeof(unsigned char),mode,file) != mode )
+                               return;
+                       
+                       // mode=3 or 4 implies that the image is RGB(A). However TGA
+                       // stores it as BGR(A) so we'll have to swap R and B.
+                       if ( mode >= 3 )
+                       {
+                               unsigned char tmp;
+                               
+                               tmp = aux[0];
+                               aux[0] = aux[2];
+                               aux[2] = tmp;
+                       }
+               }
+               
+               // add the pixel to our image
+               memcpy(&info->imageData[index], aux, mode);
+               index += mode;
+       }
+}
+
+void tgaFlipImage( tImageTGA *info )
+{
+       // mode equal the number of components for each pixel
+       int mode = info->pixelDepth / 8;
+       int rowbytes = info->width*mode;
+       unsigned char *row = (unsigned char *)malloc(rowbytes);
+       int y;
+       
+       if (row == NULL) return;
+       
+       for( y = 0; y < (info->height/2); y++ )
+       {
+               memcpy(row, &info->imageData[y*rowbytes],rowbytes);
+               memcpy(&info->imageData[y*rowbytes], &info->imageData[(info->height-(y+1))*rowbytes], rowbytes);
+               memcpy(&info->imageData[(info->height-(y+1))*rowbytes], row, rowbytes);
+       }
+       
+       free(row);
+       info->flipped = 0;
+}
+
+// this is the function to call when we want to load an image
+tImageTGA * tgaLoad(const char *filename) {
+       
+       FILE *file;
+       tImageTGA *info;
+       int mode,total;
+       
+       // allocate memory for the info struct and check!
+       info = (tImageTGA *)malloc(sizeof(tImageTGA));
+       if (info == NULL)
+               return(NULL);
+       
+       
+       // open the file for reading (binary mode)
+       file = fopen(filename, "rb");
+       if (file == NULL) {
+               info->status = TGA_ERROR_FILE_OPEN;
+               return(info);
+       }
+       
+       // load the header
+       tgaLoadHeader(file,info);
+       
+       // check for errors when loading the header
+       if (ferror(file)) {
+               info->status = TGA_ERROR_READING_FILE;
+               fclose(file);
+               return(info);
+       }
+       
+       // check if the image is color indexed
+       if (info->type == 1) {
+               info->status = TGA_ERROR_INDEXED_COLOR;
+               fclose(file);
+               return(info);
+       }
+       // check for other types (compressed images)
+       if ((info->type != 2) && (info->type !=3) && (info->type !=10) ) {
+               info->status = TGA_ERROR_COMPRESSED_FILE;
+               fclose(file);
+               return(info);
+       }
+       
+       // mode equals the number of image components
+       mode = info->pixelDepth / 8;
+       // total is the number of unsigned chars to read
+       total = info->height * info->width * mode;
+       // allocate memory for image pixels
+       info->imageData = (unsigned char *)malloc(sizeof(unsigned char) *
+                                                                                         total);
+       
+       // check to make sure we have the memory required
+       if (info->imageData == NULL) {
+               info->status = TGA_ERROR_MEMORY;
+               fclose(file);
+               return(info);
+       }
+       // finally load the image pixels
+       if ( info->type == 10 )
+               tgaLoadRLEImageData(file, info);
+       else
+               tgaLoadImageData(file,info);
+       
+       // check for errors when reading the pixels
+       if (ferror(file)) {
+               info->status = TGA_ERROR_READING_FILE;
+               fclose(file);
+               return(info);
+       }
+       fclose(file);
+       info->status = TGA_OK;
+       
+       if ( info->flipped )
+       {
+               tgaFlipImage( info );
+               if ( info->flipped ) info->status = TGA_ERROR_MEMORY;
+       }
+       
+       return(info);
+}
+
+// converts RGB to greyscale
+void tgaRGBtogreyscale(tImageTGA *info) {
+       
+       int mode,i,j;
+       
+       unsigned char *newImageData;
+       
+       // if the image is already greyscale do nothing
+       if (info->pixelDepth == 8)
+               return;
+       
+       // compute the number of actual components
+       mode = info->pixelDepth / 8;
+       
+       // allocate an array for the new image data
+       newImageData = (unsigned char *)malloc(sizeof(unsigned char) * 
+                                                                                  info->height * info->width);
+       if (newImageData == NULL) {
+               return;
+       }
+       
+       // convert pixels: greyscale = o.30 * R + 0.59 * G + 0.11 * B
+       for (i = 0,j = 0; j < info->width * info->height; i +=mode, j++)
+               newImageData[j] =       
+               (unsigned char)(0.30 * info->imageData[i] + 
+                                               0.59 * info->imageData[i+1] +
+                                               0.11 * info->imageData[i+2]);
+       
+       
+       //free old image data
+       free(info->imageData);
+       
+       // reassign pixelDepth and type according to the new image type
+       info->pixelDepth = 8;
+       info->type = 3;
+       // reassing imageData to the new array.
+       info->imageData = newImageData;
+}
+
+// releases the memory used for the image
+void tgaDestroy(tImageTGA *info) {
+       
+       if (info != NULL) {
+               if (info->imageData != NULL)
+                       free(info->imageData);
+               free(info);
+       }
+}
diff --git a/libs/cocos2d/Support/TransformUtils.h b/libs/cocos2d/Support/TransformUtils.h
new file mode 100644 (file)
index 0000000..49fde35
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2009 Valentin Milea
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+#import <Availability.h>
+
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+#import <UIKit/UIKit.h>
+#import <OpenGLES/ES1/gl.h>
+#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED)
+#import <OpenGL/gl.h>
+#import <Foundation/Foundation.h>
+#endif
+
+void CGAffineToGL(const CGAffineTransform *t, GLfloat *m);
+void GLToCGAffine(const GLfloat *m, CGAffineTransform *t);
diff --git a/libs/cocos2d/Support/TransformUtils.m b/libs/cocos2d/Support/TransformUtils.m
new file mode 100644 (file)
index 0000000..9caecf0
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2009 Valentin Milea
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+
+#import "TransformUtils.h"
+
+void CGAffineToGL(const CGAffineTransform *t, GLfloat *m)
+{
+       // | m[0] m[4] m[8]  m[12] |     | m11 m21 m31 m41 |     | a c 0 tx |
+       // | m[1] m[5] m[9]  m[13] |     | m12 m22 m32 m42 |     | b d 0 ty |
+       // | m[2] m[6] m[10] m[14] | <=> | m13 m23 m33 m43 | <=> | 0 0 1  0 |
+       // | m[3] m[7] m[11] m[15] |     | m14 m24 m34 m44 |     | 0 0 0  1 |
+       
+       m[2] = m[3] = m[6] = m[7] = m[8] = m[9] = m[11] = m[14] = 0.0f;
+       m[10] = m[15] = 1.0f;
+       m[0] = t->a; m[4] = t->c; m[12] = t->tx;
+       m[1] = t->b; m[5] = t->d; m[13] = t->ty;
+}
+
+void GLToCGAffine(const GLfloat *m, CGAffineTransform *t)
+{
+       t->a = m[0]; t->c = m[4]; t->tx = m[12];
+       t->b = m[1]; t->d = m[5]; t->ty = m[13];
+}
diff --git a/libs/cocos2d/Support/ZipUtils.h b/libs/cocos2d/Support/ZipUtils.h
new file mode 100644 (file)
index 0000000..363f911
--- /dev/null
@@ -0,0 +1,91 @@
+/* cocos2d for iPhone
+ *
+ * http://www.cocos2d-iphone.org
+ *
+ *
+ * inflateMemory_ based on zlib example code
+ *             http://www.zlib.net
+ *
+ * Some ideas were taken from:
+ *             http://themanaworld.org/
+ *             from the mapreader.cpp file 
+ *
+ */
+
+#ifndef __CC_ZIP_UTILS_H
+#define __CC_ZIP_UTILS_H
+
+#import <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif 
+       
+       /* XXX: pragma pack ??? */
+       /** @struct CCZHeader
+        */
+       struct CCZHeader {
+               uint8_t                 sig[4];                         // signature. Should be 'CCZ!' 4 bytes
+               uint16_t                compression_type;       // should 0
+               uint16_t                version;                        // should be 2 (although version type==1 is also supported)
+               uint32_t                reserved;                       // Reserverd for users.
+               uint32_t                len;                            // size of the uncompressed file
+       };
+       
+       enum {
+               CCZ_COMPRESSION_ZLIB,                           // zlib format.
+               CCZ_COMPRESSION_BZIP2,                          // bzip2 format (not supported yet)
+               CCZ_COMPRESSION_GZIP,                           // gzip format (not supported yet)
+               CCZ_COMPRESSION_NONE,                           // plain (not supported yet)
+       };
+       
+/** @file
+ * Zip helper functions
+ */
+
+/** 
+ * Inflates either zlib or gzip deflated memory. The inflated memory is
+ * expected to be freed by the caller.
+ *
+ * It will allocate 256k for the destination buffer. If it is not enought it will multiply the previous buffer size per 2, until there is enough memory.
+ * @returns the length of the deflated buffer
+ *
+ @since v0.8.1
+ */
+int ccInflateMemory(unsigned char *in, unsigned int inLength, unsigned char **out);
+
+/** 
+ * Inflates either zlib or gzip deflated memory. The inflated memory is
+ * expected to be freed by the caller.
+ *
+ * outLenghtHint is assumed to be the needed room to allocate the inflated buffer.
+ *
+ * @returns the length of the deflated buffer
+ *
+ @since v1.0.0
+ */
+int ccInflateMemoryWithHint(unsigned char *in, unsigned int inLength, unsigned char **out, unsigned int outLenghtHint );
+
+       
+/** inflates a GZip file into memory
+ *
+ * @returns the length of the deflated buffer
+ *
+ * @since v0.99.5
+ */
+int ccInflateGZipFile(const char *filename, unsigned char **out);
+
+/** inflates a CCZ file into memory
+ *
+ * @returns the length of the deflated buffer
+ *
+ * @since v0.99.5
+ */
+int ccInflateCCZFile(const char *filename, unsigned char **out);
+       
+
+#ifdef __cplusplus
+}
+#endif 
+               
+#endif // __CC_ZIP_UTILS_H
diff --git a/libs/cocos2d/Support/ZipUtils.m b/libs/cocos2d/Support/ZipUtils.m
new file mode 100644 (file)
index 0000000..39f217e
--- /dev/null
@@ -0,0 +1,251 @@
+/* cocos2d for iPhone
+ *
+ * http://www.cocos2d-iphone.org
+ *
+ *
+ * Inflates either zlib or gzip deflated memory. The inflated memory is
+ * expected to be freed by the caller.
+ *
+ * inflateMemory_ based on zlib example code
+ *             http://www.zlib.net
+ *
+ * Some ideas were taken from:
+ *             http://themanaworld.org/
+ *             from the mapreader.cpp file 
+ */
+
+#import <Availability.h>
+
+#import <zlib.h>
+#import <stdlib.h>
+#import <assert.h>
+#import <stdio.h>
+
+#import "ZipUtils.h"
+#import "CCFileUtils.h"
+#import "../ccMacros.h"
+
+// memory in iPhone is precious
+// Should buffer factor be 1.5 instead of 2 ?
+#define BUFFER_INC_FACTOR (2)
+
+static int inflateMemoryWithHint(unsigned char *in, unsigned int inLength, unsigned char **out, unsigned int *outLength, unsigned int outLenghtHint )
+{
+       /* ret value */
+       int err = Z_OK;
+       
+       int bufferSize = outLenghtHint;
+       *out = (unsigned char*) malloc(bufferSize);
+       
+    z_stream d_stream; /* decompression stream */      
+    d_stream.zalloc = (alloc_func)0;
+    d_stream.zfree = (free_func)0;
+    d_stream.opaque = (voidpf)0;
+       
+    d_stream.next_in  = in;
+    d_stream.avail_in = inLength;
+       d_stream.next_out = *out;
+       d_stream.avail_out = bufferSize;
+       
+       /* window size to hold 256k */
+       if( (err = inflateInit2(&d_stream, 15 + 32)) != Z_OK )
+               return err;
+       
+    for (;;) {
+        err = inflate(&d_stream, Z_NO_FLUSH);
+        
+               if (err == Z_STREAM_END)
+                       break;
+               
+               switch (err) {
+                       case Z_NEED_DICT:
+                               err = Z_DATA_ERROR;
+                       case Z_DATA_ERROR:
+                       case Z_MEM_ERROR:
+                               inflateEnd(&d_stream);
+                               return err;
+               }
+               
+               // not enough memory ?
+               if (err != Z_STREAM_END) {
+                       
+                       unsigned char *tmp = realloc(*out, bufferSize * BUFFER_INC_FACTOR);
+                       
+                       /* not enough memory, ouch */
+                       if (! tmp ) {
+                               CCLOG(@"cocos2d: ZipUtils: realloc failed");
+                               inflateEnd(&d_stream);
+                               return Z_MEM_ERROR;
+                       }
+                       /* only assign to *out if tmp is valid. it's not guaranteed that realloc will reuse the memory */
+                       *out = tmp;
+                       
+                       d_stream.next_out = *out + bufferSize;
+                       d_stream.avail_out = bufferSize;
+                       bufferSize *= BUFFER_INC_FACTOR;
+               }
+    }
+       
+       
+       *outLength = bufferSize - d_stream.avail_out;
+    err = inflateEnd(&d_stream);
+       return err;
+}
+
+int ccInflateMemoryWithHint(unsigned char *in, unsigned int inLength, unsigned char **out, unsigned int outLengthHint )
+{
+       unsigned int outLength = 0;
+       int err = inflateMemoryWithHint(in, inLength, out, &outLength, outLengthHint );
+       
+       if (err != Z_OK || *out == NULL) {
+               if (err == Z_MEM_ERROR)
+                       CCLOG(@"cocos2d: ZipUtils: Out of memory while decompressing map data!");
+               
+               else if (err == Z_VERSION_ERROR)
+                       CCLOG(@"cocos2d: ZipUtils: Incompatible zlib version!");
+               
+               else if (err == Z_DATA_ERROR)
+                       CCLOG(@"cocos2d: ZipUtils: Incorrect zlib compressed data!");
+               
+               else
+                       CCLOG(@"cocos2d: ZipUtils: Unknown error while decompressing map data!");
+               
+               free(*out);
+               *out = NULL;
+               outLength = 0;
+       }
+       
+       return outLength;
+}
+
+int ccInflateMemory(unsigned char *in, unsigned int inLength, unsigned char **out)
+{
+       // 256k for hint
+       return ccInflateMemoryWithHint(in, inLength, out, 256 * 1024 );
+}
+
+int ccInflateGZipFile(const char *path, unsigned char **out)
+{
+       int len;
+       unsigned int offset = 0;
+       
+       NSCAssert( out, @"ccInflateGZipFile: invalid 'out' parameter");
+       NSCAssert( &*out, @"ccInflateGZipFile: invalid 'out' parameter");
+
+       gzFile inFile = gzopen(path, "rb");
+       if( inFile == NULL ) {
+               CCLOG(@"cocos2d: ZipUtils: error open gzip file: %s", path);
+               return -1;
+       }
+       
+       /* 512k initial decompress buffer */
+       unsigned int bufferSize = 512 * 1024;
+       unsigned int totalBufferSize = bufferSize;
+       
+       *out = malloc( bufferSize );
+       if( ! out ) {
+               CCLOG(@"cocos2d: ZipUtils: out of memory");
+               return -1;
+       }
+               
+       for (;;) {
+               len = gzread(inFile, *out + offset, bufferSize);
+               if (len < 0) {
+                       CCLOG(@"cocos2d: ZipUtils: error in gzread");
+                       free( *out );
+                       *out = NULL;
+                       return -1;
+               }
+               if (len == 0)
+                       break;
+               
+               offset += len;
+               
+               // finish reading the file
+               if( len < bufferSize )
+                       break;
+
+               bufferSize *= BUFFER_INC_FACTOR;
+               totalBufferSize += bufferSize;
+               unsigned char *tmp = realloc(*out, totalBufferSize );
+
+               if( ! tmp ) {
+                       CCLOG(@"cocos2d: ZipUtils: out of memory");
+                       free( *out );
+                       *out = NULL;
+                       return -1;
+               }
+               
+               *out = tmp;
+       }
+                       
+       if (gzclose(inFile) != Z_OK)
+               CCLOG(@"cocos2d: ZipUtils: gzclose failed");
+
+       return offset;
+}
+
+int ccInflateCCZFile(const char *path, unsigned char **out)
+{
+       NSCAssert( out, @"ccInflateCCZFile: invalid 'out' parameter");
+       NSCAssert( &*out, @"ccInflateCCZFile: invalid 'out' parameter");
+
+       // load file into memory
+       unsigned char *compressed = NULL;
+       NSInteger fileLen  = ccLoadFileIntoMemory( path, &compressed );
+       if( fileLen < 0 ) {
+               CCLOG(@"cocos2d: Error loading CCZ compressed file");
+       }
+       
+       struct CCZHeader *header = (struct CCZHeader*) compressed;
+
+       // verify header
+       if( header->sig[0] != 'C' || header->sig[1] != 'C' || header->sig[2] != 'Z' || header->sig[3] != '!' ) {
+               CCLOG(@"cocos2d: Invalid CCZ file");
+               free(compressed);
+               return -1;
+       }
+       
+       // verify header version
+       uint16_t version = CFSwapInt16BigToHost( header->version );
+       if( version > 2 ) {
+               CCLOG(@"cocos2d: Unsupported CCZ header format");
+               free(compressed);
+               return -1;
+       }
+
+       // verify compression format
+       if( CFSwapInt16BigToHost(header->compression_type) != CCZ_COMPRESSION_ZLIB ) {
+               CCLOG(@"cocos2d: CCZ Unsupported compression method");
+               free(compressed);
+               return -1;
+       }
+       
+       uint32_t len = CFSwapInt32BigToHost( header->len );
+       
+       *out = malloc( len );
+       if(! *out )
+       {
+               CCLOG(@"cocos2d: CCZ: Failed to allocate memory for texture");
+               free(compressed);
+               return -1;
+       }
+       
+       
+       uLongf destlen = len;
+       uLongf source = (uLongf) compressed + sizeof(*header);
+       int ret = uncompress(*out, &destlen, (Bytef*)source, fileLen - sizeof(*header) );
+
+       free( compressed );
+       
+       if( ret != Z_OK )
+       {
+               CCLOG(@"cocos2d: CCZ: Failed to uncompress data");
+               free( *out );
+               *out = NULL;
+               return -1;
+       }
+       
+       
+       return len;
+}
\ No newline at end of file
diff --git a/libs/cocos2d/Support/base64.c b/libs/cocos2d/Support/base64.c
new file mode 100644 (file)
index 0000000..7a8f65a
--- /dev/null
@@ -0,0 +1,89 @@
+/* 
+ public domain BASE64 code
+ modified for cocos2d-iphone: http://www.cocos2d-iphone.org
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+unsigned char alphabet[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+int _base64Decode( unsigned char *input, unsigned int input_len, unsigned char *output, unsigned int *output_len )
+{
+    static char inalphabet[256], decoder[256];
+    int i, bits, c, char_count, errors = 0;
+       unsigned int input_idx = 0;
+       unsigned int output_idx = 0;
+
+    for (i = (sizeof alphabet) - 1; i >= 0 ; i--) {
+               inalphabet[alphabet[i]] = 1;
+               decoder[alphabet[i]] = i;
+    }
+
+    char_count = 0;
+    bits = 0;
+       for( input_idx=0; input_idx < input_len ; input_idx++ ) {
+               c = input[ input_idx ];
+               if (c == '=')
+                       break;
+               if (c > 255 || ! inalphabet[c])
+                       continue;
+               bits += decoder[c];
+               char_count++;
+               if (char_count == 4) {
+                       output[ output_idx++ ] = (bits >> 16);
+                       output[ output_idx++ ] = ((bits >> 8) & 0xff);
+                       output[ output_idx++ ] = ( bits & 0xff);
+                       bits = 0;
+                       char_count = 0;
+               } else {
+                       bits <<= 6;
+               }
+    }
+       
+       if( c == '=' ) {
+               switch (char_count) {
+                       case 1:
+                               fprintf(stderr, "base64Decode: encoding incomplete: at least 2 bits missing");
+                               errors++;
+                               break;
+                       case 2:
+                               output[ output_idx++ ] = ( bits >> 10 );
+                               break;
+                       case 3:
+                               output[ output_idx++ ] = ( bits >> 16 );
+                               output[ output_idx++ ] = (( bits >> 8 ) & 0xff);
+                               break;
+                       }
+       } else if ( input_idx < input_len ) {
+               if (char_count) {
+                       fprintf(stderr, "base64 encoding incomplete: at least %d bits truncated",
+                                       ((4 - char_count) * 6));
+                       errors++;
+               }
+    }
+       
+       *output_len = output_idx;
+       return errors;
+}
+
+int base64Decode(unsigned char *in, unsigned int inLength, unsigned char **out)
+{
+       unsigned int outLength = 0;
+       
+       //should be enough to store 6-bit buffers in 8-bit buffers
+       *out = malloc( inLength * 3.0f / 4.0f + 1 );
+       if( *out ) {
+               int ret = _base64Decode(in, inLength, *out, &outLength);
+               
+               if (ret > 0 )
+               {
+                       printf("Base64Utils: error decoding");
+                       free(*out);
+                       *out = NULL;                    
+                       outLength = 0;
+               }
+       }
+    return outLength;
+}
diff --git a/libs/cocos2d/Support/base64.h b/libs/cocos2d/Support/base64.h
new file mode 100644 (file)
index 0000000..d30878e
--- /dev/null
@@ -0,0 +1,33 @@
+/* 
+ public domain BASE64 code
+ modified for cocos2d-iphone: http://www.cocos2d-iphone.org
+ */
+
+#ifndef __CC_BASE64_DECODE_H
+#define __CC_BASE64_DECODE_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif 
+       
+
+/** @file
+ base64 helper functions
+ */
+
+/** 
+ * Decodes a 64base encoded memory. The decoded memory is
+ * expected to be freed by the caller.
+ *
+ * @returns the length of the out buffer
+ *
+ @since v0.8.1
+ */
+int base64Decode(unsigned char *in, unsigned int inLength, unsigned char **out);
+
+#ifdef __cplusplus
+}
+#endif 
+
+#endif // __CC_BASE64_DECODE_H
diff --git a/libs/cocos2d/Support/ccCArray.h b/libs/cocos2d/Support/ccCArray.h
new file mode 100644 (file)
index 0000000..13206a5
--- /dev/null
@@ -0,0 +1,447 @@
+/* Copyright (c) 2007 Scott Lembcke
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+/** 
+ @file
+ Based on Chipmunk cpArray.
+ ccArray is a faster alternative to NSMutableArray, it does pretty much the
+ same thing (stores NSObjects and retains/releases them appropriately). It's
+ faster because:
+ - it uses a plain C interface so it doesn't incur Objective-c messaging overhead 
+ - it assumes you know what you're doing, so it doesn't spend time on safety checks
+ (index out of bounds, required capacity etc.)
+ - comparisons are done using pointer equality instead of isEqual
+ There are 2 kind of functions:
+ - ccArray functions that manipulates objective-c objects (retain and release are performanced)
+ - ccCArray functions that manipulates values like if they were standard C structures (no retain/release is performed)
+ */
+
+#ifndef CC_ARRAY_H
+#define CC_ARRAY_H
+
+#import <Foundation/Foundation.h>
+
+#import <stdlib.h>
+#import <string.h>
+
+
+#pragma mark -
+#pragma mark ccArray for Objects
+
+// Easy integration    
+#define CCARRAYDATA_FOREACH(__array__, __object__)                                                                                                                     \
+__object__=__array__->arr[0]; for(NSUInteger i=0, num=__array__->num; i<num; i++, __object__=__array__->arr[i])        \
+
+
+typedef struct ccArray {
+       NSUInteger num, max;
+       id *arr;
+} ccArray;
+
+/** Allocates and initializes a new array with specified capacity */
+static inline ccArray* ccArrayNew(NSUInteger capacity) {
+       if (capacity == 0)
+               capacity = 1; 
+       
+       ccArray *arr = (ccArray*)malloc( sizeof(ccArray) );
+       arr->num = 0;
+       arr->arr =  (id*) malloc( capacity * sizeof(id) );
+       arr->max = capacity;
+       
+       return arr;
+}
+
+static inline void ccArrayRemoveAllObjects(ccArray *arr);
+
+/** Frees array after removing all remaining objects. Silently ignores nil arr. */
+static inline void ccArrayFree(ccArray *arr)
+{
+       if( arr == nil ) return;
+       
+       ccArrayRemoveAllObjects(arr);
+       
+       free(arr->arr);
+       free(arr);
+}
+
+/** Doubles array capacity */
+static inline void ccArrayDoubleCapacity(ccArray *arr)
+{
+       arr->max *= 2;
+       id *newArr = (id *)realloc( arr->arr, arr->max * sizeof(id) );
+       // will fail when there's not enough memory
+    NSCAssert(newArr != NULL, @"ccArrayDoubleCapacity failed. Not enough memory");
+       arr->arr = newArr;
+}
+
+/** Increases array capacity such that max >= num + extra. */
+static inline void ccArrayEnsureExtraCapacity(ccArray *arr, NSUInteger extra)
+{
+       while (arr->max < arr->num + extra)
+               ccArrayDoubleCapacity(arr);
+}
+
+/** shrinks the array so the memory footprint corresponds with the number of items */
+static inline void ccArrayShrink(ccArray *arr)
+{
+    NSUInteger newSize;
+       
+       //only resize when necessary
+       if (arr->max > arr->num && !(arr->num==0 && arr->max==1))
+       {
+               if (arr->num!=0) 
+               {
+                       newSize=arr->num;
+                       arr->max=arr->num; 
+               }
+               else 
+               {//minimum capacity of 1, with 0 elements the array would be free'd by realloc
+                       newSize=1;
+                       arr->max=1;
+               }
+               
+               arr->arr = (id*) realloc(arr->arr,newSize * sizeof(id) );
+               NSCAssert(arr->arr!=NULL,@"could not reallocate the memory");
+       }
+} 
+
+/** Returns index of first occurence of object, NSNotFound if object not found. */
+static inline NSUInteger ccArrayGetIndexOfObject(ccArray *arr, id object)
+{
+       for( NSUInteger i = 0; i < arr->num; i++)
+               if( arr->arr[i] == object ) return i;
+    
+       return NSNotFound;
+}
+
+/** Returns a Boolean value that indicates whether object is present in array. */
+static inline BOOL ccArrayContainsObject(ccArray *arr, id object)
+{
+       return ccArrayGetIndexOfObject(arr, object) != NSNotFound;
+}
+
+/** Appends an object. Bahaviour undefined if array doesn't have enough capacity. */
+static inline void ccArrayAppendObject(ccArray *arr, id object)
+{
+       arr->arr[arr->num] = [object retain];
+       arr->num++;
+}
+
+/** Appends an object. Capacity of arr is increased if needed. */
+static inline void ccArrayAppendObjectWithResize(ccArray *arr, id object)
+{
+       ccArrayEnsureExtraCapacity(arr, 1);
+       ccArrayAppendObject(arr, object);
+}
+
+/** Appends objects from plusArr to arr. Behaviour undefined if arr doesn't have
+ enough capacity. */
+static inline void ccArrayAppendArray(ccArray *arr, ccArray *plusArr)
+{
+       for( NSUInteger i = 0; i < plusArr->num; i++)
+               ccArrayAppendObject(arr, plusArr->arr[i]);
+}
+
+/** Appends objects from plusArr to arr. Capacity of arr is increased if needed. */
+static inline void ccArrayAppendArrayWithResize(ccArray *arr, ccArray *plusArr)
+{
+       ccArrayEnsureExtraCapacity(arr, plusArr->num);
+       ccArrayAppendArray(arr, plusArr);
+}
+
+/** Inserts an object at index */
+static inline void ccArrayInsertObjectAtIndex(ccArray *arr, id object, NSUInteger index)
+{
+       NSCAssert(index<=arr->num, @"Invalid index. Out of bounds");
+       
+       ccArrayEnsureExtraCapacity(arr, 1);
+       
+       NSUInteger remaining = arr->num - index;
+       if( remaining > 0)
+               memmove(&arr->arr[index+1], &arr->arr[index], sizeof(id) * remaining );
+       
+       arr->arr[index] = [object retain];
+       arr->num++;
+}
+
+/** Swaps two objects */
+static inline void ccArraySwapObjectsAtIndexes(ccArray *arr, NSUInteger index1, NSUInteger index2)
+{
+       NSCAssert(index1 < arr->num, @"(1) Invalid index. Out of bounds");
+       NSCAssert(index2 < arr->num, @"(2) Invalid index. Out of bounds");
+       
+       id object1 = arr->arr[index1];
+    
+       arr->arr[index1] = arr->arr[index2];
+       arr->arr[index2] = object1;
+}
+
+/** Removes all objects from arr */
+static inline void ccArrayRemoveAllObjects(ccArray *arr)
+{
+       while( arr->num > 0 )
+               [arr->arr[--arr->num] release]; 
+}
+
+/** Removes object at specified index and pushes back all subsequent objects.
+ Behaviour undefined if index outside [0, num-1]. */
+static inline void ccArrayRemoveObjectAtIndex(ccArray *arr, NSUInteger index)
+{
+       [arr->arr[index] release];
+       arr->num--;
+       
+       NSUInteger remaining = arr->num - index;
+       if(remaining>0)
+               memmove(&arr->arr[index], &arr->arr[index+1], remaining * sizeof(id));
+}
+
+/** Removes object at specified index and fills the gap with the last object,
+ thereby avoiding the need to push back subsequent objects.
+ Behaviour undefined if index outside [0, num-1]. */
+static inline void ccArrayFastRemoveObjectAtIndex(ccArray *arr, NSUInteger index)
+{
+       [arr->arr[index] release];
+       NSUInteger last = --arr->num;
+       arr->arr[index] = arr->arr[last];
+}
+
+static inline void ccArrayFastRemoveObject(ccArray *arr, id object)
+{
+       NSUInteger index = ccArrayGetIndexOfObject(arr, object);
+       if (index != NSNotFound)
+               ccArrayFastRemoveObjectAtIndex(arr, index);
+}
+
+/** Searches for the first occurance of object and removes it. If object is not
+ found the function has no effect. */
+static inline void ccArrayRemoveObject(ccArray *arr, id object)
+{
+       NSUInteger index = ccArrayGetIndexOfObject(arr, object);
+       if (index != NSNotFound)
+               ccArrayRemoveObjectAtIndex(arr, index);
+}
+
+/** Removes from arr all objects in minusArr. For each object in minusArr, the
+ first matching instance in arr will be removed. */
+static inline void ccArrayRemoveArray(ccArray *arr, ccArray *minusArr)
+{
+       for( NSUInteger i = 0; i < minusArr->num; i++)
+               ccArrayRemoveObject(arr, minusArr->arr[i]);
+}
+
+/** Removes from arr all objects in minusArr. For each object in minusArr, all
+ matching instances in arr will be removed. */
+static inline void ccArrayFullRemoveArray(ccArray *arr, ccArray *minusArr)
+{
+       NSUInteger back = 0;
+       
+       for( NSUInteger i = 0; i < arr->num; i++) {
+               if( ccArrayContainsObject(minusArr, arr->arr[i]) ) {
+                       [arr->arr[i] release];
+                       back++;
+               } else
+                       arr->arr[i - back] = arr->arr[i];
+       }
+       
+       arr->num -= back;
+}
+
+/** Sends to each object in arr the message identified by given selector. */
+static inline void ccArrayMakeObjectsPerformSelector(ccArray *arr, SEL sel)
+{
+       for( NSUInteger i = 0; i < arr->num; i++)
+               [arr->arr[i] performSelector:sel];
+}
+
+static inline void ccArrayMakeObjectsPerformSelectorWithObject(ccArray *arr, SEL sel, id object)
+{
+       for( NSUInteger i = 0; i < arr->num; i++)
+               [arr->arr[i] performSelector:sel withObject:object];
+}
+
+
+#pragma mark -
+#pragma mark ccCArray for Values (c structures)
+
+typedef ccArray ccCArray;
+
+static inline void ccCArrayRemoveAllValues(ccCArray *arr);
+
+/** Allocates and initializes a new C array with specified capacity */
+static inline ccCArray* ccCArrayNew(NSUInteger capacity) {
+       if (capacity == 0)
+               capacity = 1; 
+       
+       ccCArray *arr = (ccCArray*)malloc( sizeof(ccCArray) );
+       arr->num = 0;
+       arr->arr =  (id*) malloc( capacity * sizeof(id) );
+       arr->max = capacity;
+       
+       return arr;
+}
+
+/** Frees C array after removing all remaining values. Silently ignores nil arr. */
+static inline void ccCArrayFree(ccCArray *arr)
+{
+       if( arr == nil ) return;
+       
+       ccCArrayRemoveAllValues(arr);
+       
+       free(arr->arr);
+       free(arr);
+}
+
+/** Doubles C array capacity */
+static inline void ccCArrayDoubleCapacity(ccCArray *arr)
+{
+       return ccArrayDoubleCapacity(arr);
+}
+
+/** Increases array capacity such that max >= num + extra. */
+static inline void ccCArrayEnsureExtraCapacity(ccCArray *arr, NSUInteger extra)
+{
+       return ccArrayEnsureExtraCapacity(arr,extra);
+}
+
+/** Returns index of first occurence of value, NSNotFound if value not found. */
+static inline NSUInteger ccCArrayGetIndexOfValue(ccCArray *arr, void* value)
+{
+       for( NSUInteger i = 0; i < arr->num; i++)
+               if( arr->arr[i] == value ) return i;
+       return NSNotFound;
+}
+
+/** Returns a Boolean value that indicates whether value is present in the C array. */
+static inline BOOL ccCArrayContainsValue(ccCArray *arr, void* value)
+{
+       return ccCArrayGetIndexOfValue(arr, value) != NSNotFound;
+}
+
+/** Inserts a value at a certain position. Behaviour undefined if aray doesn't have enough capacity */
+static inline void ccCArrayInsertValueAtIndex( ccCArray *arr, void *value, NSUInteger index)
+{
+       NSCAssert( index < arr->max, @"ccCArrayInsertValueAtIndex: invalid index");
+       
+       NSUInteger remaining = arr->num - index;
+       
+       // last Value doesn't need to be moved
+       if( remaining > 0) {
+               // tex coordinates
+               memmove( &arr->arr[index+1],&arr->arr[index], sizeof(void*) * remaining );
+       }
+       
+       arr->num++;     
+       arr->arr[index] = (id) value;
+}
+
+/** Appends an value. Bahaviour undefined if array doesn't have enough capacity. */
+static inline void ccCArrayAppendValue(ccCArray *arr, void* value)
+{
+       arr->arr[arr->num] = (id) value;
+       arr->num++;
+}
+
+/** Appends an value. Capacity of arr is increased if needed. */
+static inline void ccCArrayAppendValueWithResize(ccCArray *arr, void* value)
+{
+       ccCArrayEnsureExtraCapacity(arr, 1);
+       ccCArrayAppendValue(arr, value);
+}
+
+/** Appends values from plusArr to arr. Behaviour undefined if arr doesn't have
+ enough capacity. */
+static inline void ccCArrayAppendArray(ccCArray *arr, ccCArray *plusArr)
+{
+       for( NSUInteger i = 0; i < plusArr->num; i++)
+               ccCArrayAppendValue(arr, plusArr->arr[i]);
+}
+
+/** Appends values from plusArr to arr. Capacity of arr is increased if needed. */
+static inline void ccCArrayAppendArrayWithResize(ccCArray *arr, ccCArray *plusArr)
+{
+       ccCArrayEnsureExtraCapacity(arr, plusArr->num);
+       ccCArrayAppendArray(arr, plusArr);
+}
+
+/** Removes all values from arr */
+static inline void ccCArrayRemoveAllValues(ccCArray *arr)
+{
+       arr->num = 0;
+}
+
+/** Removes value at specified index and pushes back all subsequent values.
+ Behaviour undefined if index outside [0, num-1].
+ @since v0.99.4
+ */
+static inline void ccCArrayRemoveValueAtIndex(ccCArray *arr, NSUInteger index)
+{      
+       for( NSUInteger last = --arr->num; index < last; index++)
+               arr->arr[index] = arr->arr[index + 1];
+}
+
+/** Removes value at specified index and fills the gap with the last value,
+ thereby avoiding the need to push back subsequent values.
+ Behaviour undefined if index outside [0, num-1].
+ @since v0.99.4
+ */
+static inline void ccCArrayFastRemoveValueAtIndex(ccCArray *arr, NSUInteger index)
+{
+       NSUInteger last = --arr->num;
+       arr->arr[index] = arr->arr[last];
+}
+
+/** Searches for the first occurance of value and removes it. If value is not found the function has no effect.
+ @since v0.99.4
+ */
+static inline void ccCArrayRemoveValue(ccCArray *arr, void* value)
+{
+       NSUInteger index = ccCArrayGetIndexOfValue(arr, value);
+       if (index != NSNotFound)
+               ccCArrayRemoveValueAtIndex(arr, index);
+}
+
+/** Removes from arr all values in minusArr. For each Value in minusArr, the first matching instance in arr will be removed.
+ @since v0.99.4
+ */
+static inline void ccCArrayRemoveArray(ccCArray *arr, ccCArray *minusArr)
+{
+       for( NSUInteger i = 0; i < minusArr->num; i++)
+               ccCArrayRemoveValue(arr, minusArr->arr[i]);
+}
+
+/** Removes from arr all values in minusArr. For each value in minusArr, all matching instances in arr will be removed.
+ @since v0.99.4
+ */
+static inline void ccCArrayFullRemoveArray(ccCArray *arr, ccCArray *minusArr)
+{
+       NSUInteger back = 0;
+       
+       for( NSUInteger i = 0; i < arr->num; i++) {
+               if( ccCArrayContainsValue(minusArr, arr->arr[i]) ) {
+                       back++;
+               } else
+                       arr->arr[i - back] = arr->arr[i];
+       }
+       
+       arr->num -= back;
+}
+#endif // CC_ARRAY_H
\ No newline at end of file
diff --git a/libs/cocos2d/Support/ccUtils.c b/libs/cocos2d/Support/ccUtils.c
new file mode 100644 (file)
index 0000000..39786ec
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ */
+
+/*
+ ccNextPOT function is licensed under the same license that is used in CCTexture2D.m.
+ */
+#include "ccUtils.h"
+
+unsigned long ccNextPOT(unsigned long x)
+{
+    x = x - 1;
+    x = x | (x >> 1);
+    x = x | (x >> 2);
+    x = x | (x >> 4);
+    x = x | (x >> 8);
+    x = x | (x >>16);
+    return x + 1;
+}
\ No newline at end of file
diff --git a/libs/cocos2d/Support/ccUtils.h b/libs/cocos2d/Support/ccUtils.h
new file mode 100644 (file)
index 0000000..783fc54
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ */
+
+#ifndef __CC_UTILS_H
+#define __CC_UTILS_H
+
+/** @file ccUtils.h
+ Misc free functions
+ */
+
+/*
+ ccNextPOT function is licensed under the same license that is used in CCTexture2D.m.
+ */
+
+/** returns the Next Power of Two value.
+ Examples:
+       - If "value" is 15, it will return 16.
+       - If "value" is 16, it will return 16.
+       - If "value" is 17, it will return 32.
+ @since v0.99.5
+ */
+
+unsigned long ccNextPOT( unsigned long value );
+
+#endif // ! __CC_UTILS_H
diff --git a/libs/cocos2d/Support/uthash.h b/libs/cocos2d/Support/uthash.h
new file mode 100644 (file)
index 0000000..a4bdc18
--- /dev/null
@@ -0,0 +1,972 @@
+/*
+Copyright (c) 2003-2010, Troy D. Hanson     http://uthash.sourceforge.net
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
+OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef UTHASH_H
+#define UTHASH_H 
+
+#include <string.h>   /* memcmp,strlen */
+#include <stddef.h>   /* ptrdiff_t */
+
+/* These macros use decltype or the earlier __typeof GNU extension.
+   As decltype is only available in newer compilers (VS2010 or gcc 4.3+
+   when compiling c++ source) this code uses whatever method is needed
+   or, for VS2008 where neither is available, uses casting workarounds. */
+#ifdef _MSC_VER         /* MS compiler */
+#if _MSC_VER >= 1600 && defined(__cplusplus)  /* VS2010 or newer in C++ mode */
+#define DECLTYPE(x) (decltype(x))
+#else                   /* VS2008 or older (or VS2010 in C mode) */
+#define NO_DECLTYPE
+#define DECLTYPE(x)
+#endif
+#else                   /* GNU, Sun and other compilers */
+#define DECLTYPE(x) (__typeof(x))
+#endif
+
+#ifdef NO_DECLTYPE
+#define DECLTYPE_ASSIGN(dst,src)                                                 \
+do {                                                                             \
+  char **_da_dst = (char**)(&(dst));                                             \
+  *_da_dst = (char*)(src);                                                       \
+} while(0)
+#else 
+#define DECLTYPE_ASSIGN(dst,src)                                                 \
+do {                                                                             \
+  (dst) = DECLTYPE(dst)(src);                                                    \
+} while(0)
+#endif
+
+/* a number of the hash function use uint32_t which isn't defined on win32 */
+#ifdef _MSC_VER
+typedef unsigned int uint32_t;
+#else
+#include <inttypes.h>   /* uint32_t */
+#endif
+
+#define UTHASH_VERSION 1.9.3
+
+#define uthash_fatal(msg) exit(-1)        /* fatal error (out of memory,etc) */
+#define uthash_malloc(sz) malloc(sz)      /* malloc fcn                      */
+#define uthash_free(ptr,sz) free(ptr)     /* free fcn                        */
+
+#define uthash_noexpand_fyi(tbl)          /* can be defined to log noexpand  */
+#define uthash_expand_fyi(tbl)            /* can be defined to log expands   */
+
+/* initial number of buckets */
+#define HASH_INITIAL_NUM_BUCKETS 32      /* initial number of buckets        */
+#define HASH_INITIAL_NUM_BUCKETS_LOG2 5  /* lg2 of initial number of buckets */
+#define HASH_BKT_CAPACITY_THRESH 10      /* expand when bucket count reaches */
+
+/* calculate the element whose hash handle address is hhe */
+#define ELMT_FROM_HH(tbl,hhp) ((void*)(((char*)(hhp)) - ((tbl)->hho)))
+
+#define HASH_FIND(hh,head,keyptr,keylen,out)                                     \
+do {                                                                             \
+  unsigned _hf_bkt,_hf_hashv;                                                    \
+  out=NULL;                                                                      \
+  if (head) {                                                                    \
+     HASH_FCN(keyptr,keylen, (head)->hh.tbl->num_buckets, _hf_hashv, _hf_bkt);   \
+     if (HASH_BLOOM_TEST((head)->hh.tbl, _hf_hashv)) {                           \
+       HASH_FIND_IN_BKT((head)->hh.tbl, hh, (head)->hh.tbl->buckets[ _hf_bkt ],  \
+                        keyptr,keylen,out);                                      \
+     }                                                                           \
+  }                                                                              \
+} while (0)
+
+#ifdef HASH_BLOOM
+#define HASH_BLOOM_BITLEN (1ULL << HASH_BLOOM)
+#define HASH_BLOOM_BYTELEN (HASH_BLOOM_BITLEN/8) + ((HASH_BLOOM_BITLEN%8) ? 1:0)
+#define HASH_BLOOM_MAKE(tbl)                                                     \
+do {                                                                             \
+  (tbl)->bloom_nbits = HASH_BLOOM;                                               \
+  (tbl)->bloom_bv = (uint8_t*)uthash_malloc(HASH_BLOOM_BYTELEN);                 \
+  if (!((tbl)->bloom_bv))  { uthash_fatal( "out of memory"); }                   \
+  memset((tbl)->bloom_bv, 0, HASH_BLOOM_BYTELEN);                                \
+  (tbl)->bloom_sig = HASH_BLOOM_SIGNATURE;                                       \
+} while (0);
+
+#define HASH_BLOOM_FREE(tbl)                                                     \
+do {                                                                             \
+  uthash_free((tbl)->bloom_bv, HASH_BLOOM_BYTELEN);                              \
+} while (0);
+
+#define HASH_BLOOM_BITSET(bv,idx) (bv[(idx)/8] |= (1U << ((idx)%8)))
+#define HASH_BLOOM_BITTEST(bv,idx) (bv[(idx)/8] & (1U << ((idx)%8)))
+
+#define HASH_BLOOM_ADD(tbl,hashv)                                                \
+  HASH_BLOOM_BITSET((tbl)->bloom_bv, (hashv & (uint32_t)((1ULL << (tbl)->bloom_nbits) - 1)))
+
+#define HASH_BLOOM_TEST(tbl,hashv)                                               \
+  HASH_BLOOM_BITTEST((tbl)->bloom_bv, (hashv & (uint32_t)((1ULL << (tbl)->bloom_nbits) - 1)))
+
+#else
+#define HASH_BLOOM_MAKE(tbl) 
+#define HASH_BLOOM_FREE(tbl) 
+#define HASH_BLOOM_ADD(tbl,hashv) 
+#define HASH_BLOOM_TEST(tbl,hashv) (1)
+#endif
+
+#define HASH_MAKE_TABLE(hh,head)                                                 \
+do {                                                                             \
+  (head)->hh.tbl = (UT_hash_table*)uthash_malloc(                                \
+                  sizeof(UT_hash_table));                                        \
+  if (!((head)->hh.tbl))  { uthash_fatal( "out of memory"); }                    \
+  memset((head)->hh.tbl, 0, sizeof(UT_hash_table));                              \
+  (head)->hh.tbl->tail = &((head)->hh);                                          \
+  (head)->hh.tbl->num_buckets = HASH_INITIAL_NUM_BUCKETS;                        \
+  (head)->hh.tbl->log2_num_buckets = HASH_INITIAL_NUM_BUCKETS_LOG2;              \
+  (head)->hh.tbl->hho = (char*)(&(head)->hh) - (char*)(head);                    \
+  (head)->hh.tbl->buckets = (UT_hash_bucket*)uthash_malloc(                      \
+          HASH_INITIAL_NUM_BUCKETS*sizeof(struct UT_hash_bucket));               \
+  if (! (head)->hh.tbl->buckets) { uthash_fatal( "out of memory"); }             \
+  memset((head)->hh.tbl->buckets, 0,                                             \
+          HASH_INITIAL_NUM_BUCKETS*sizeof(struct UT_hash_bucket));               \
+  HASH_BLOOM_MAKE((head)->hh.tbl);                                               \
+  (head)->hh.tbl->signature = HASH_SIGNATURE;                                    \
+} while(0)
+
+#define HASH_ADD(hh,head,fieldname,keylen_in,add)                                \
+        HASH_ADD_KEYPTR(hh,head,&add->fieldname,keylen_in,add)
+#define HASH_ADD_KEYPTR(hh,head,keyptr,keylen_in,add)                            \
+do {                                                                             \
+ unsigned _ha_bkt;                                                               \
+ (add)->hh.next = NULL;                                                          \
+ (add)->hh.key = (char*)keyptr;                                                  \
+ (add)->hh.keylen = keylen_in;                                                   \
+ if (!(head)) {                                                                  \
+    head = (add);                                                                \
+    (head)->hh.prev = NULL;                                                      \
+    HASH_MAKE_TABLE(hh,head);                                                    \
+ } else {                                                                        \
+    (head)->hh.tbl->tail->next = (add);                                          \
+    (add)->hh.prev = ELMT_FROM_HH((head)->hh.tbl, (head)->hh.tbl->tail);         \
+    (head)->hh.tbl->tail = &((add)->hh);                                         \
+ }                                                                               \
+ (head)->hh.tbl->num_items++;                                                    \
+ (add)->hh.tbl = (head)->hh.tbl;                                                 \
+ HASH_FCN(keyptr,keylen_in, (head)->hh.tbl->num_buckets,                         \
+         (add)->hh.hashv, _ha_bkt);                                              \
+ HASH_ADD_TO_BKT((head)->hh.tbl->buckets[_ha_bkt],&(add)->hh);                   \
+ HASH_BLOOM_ADD((head)->hh.tbl,(add)->hh.hashv);                                 \
+ HASH_EMIT_KEY(hh,head,keyptr,keylen_in);                                        \
+ HASH_FSCK(hh,head);                                                             \
+} while(0)
+
+#define HASH_TO_BKT( hashv, num_bkts, bkt )                                      \
+do {                                                                             \
+  bkt = ((hashv) & ((num_bkts) - 1));                                            \
+} while(0)
+
+/* delete "delptr" from the hash table.
+ * "the usual" patch-up process for the app-order doubly-linked-list.
+ * The use of _hd_hh_del below deserves special explanation.
+ * These used to be expressed using (delptr) but that led to a bug
+ * if someone used the same symbol for the head and deletee, like
+ *  HASH_DELETE(hh,users,users);
+ * We want that to work, but by changing the head (users) below
+ * we were forfeiting our ability to further refer to the deletee (users)
+ * in the patch-up process. Solution: use scratch space to
+ * copy the deletee pointer, then the latter references are via that
+ * scratch pointer rather than through the repointed (users) symbol.
+ */
+#define HASH_DELETE(hh,head,delptr)                                              \
+do {                                                                             \
+    unsigned _hd_bkt;                                                            \
+    struct UT_hash_handle *_hd_hh_del;                                           \
+    if ( ((delptr)->hh.prev == NULL) && ((delptr)->hh.next == NULL) )  {         \
+        uthash_free((head)->hh.tbl->buckets,                                     \
+                    (head)->hh.tbl->num_buckets*sizeof(struct UT_hash_bucket) ); \
+        HASH_BLOOM_FREE((head)->hh.tbl);                                         \
+        uthash_free((head)->hh.tbl, sizeof(UT_hash_table));                      \
+        head = NULL;                                                             \
+    } else {                                                                     \
+        _hd_hh_del = &((delptr)->hh);                                            \
+        if ((delptr) == ELMT_FROM_HH((head)->hh.tbl,(head)->hh.tbl->tail)) {     \
+            (head)->hh.tbl->tail =                                               \
+                (UT_hash_handle*)((char*)((delptr)->hh.prev) +                   \
+                (head)->hh.tbl->hho);                                            \
+        }                                                                        \
+        if ((delptr)->hh.prev) {                                                 \
+            ((UT_hash_handle*)((char*)((delptr)->hh.prev) +                      \
+                    (head)->hh.tbl->hho))->next = (delptr)->hh.next;             \
+        } else {                                                                 \
+            DECLTYPE_ASSIGN(head,(delptr)->hh.next);                             \
+        }                                                                        \
+        if (_hd_hh_del->next) {                                                  \
+            ((UT_hash_handle*)((char*)_hd_hh_del->next +                         \
+                    (head)->hh.tbl->hho))->prev =                                \
+                    _hd_hh_del->prev;                                            \
+        }                                                                        \
+        HASH_TO_BKT( _hd_hh_del->hashv, (head)->hh.tbl->num_buckets, _hd_bkt);   \
+        HASH_DEL_IN_BKT(hh,(head)->hh.tbl->buckets[_hd_bkt], _hd_hh_del);        \
+        (head)->hh.tbl->num_items--;                                             \
+    }                                                                            \
+    HASH_FSCK(hh,head);                                                          \
+} while (0)
+
+
+/* convenience forms of HASH_FIND/HASH_ADD/HASH_DEL */
+#define HASH_FIND_STR(head,findstr,out)                                          \
+    HASH_FIND(hh,head,findstr,strlen(findstr),out)
+#define HASH_ADD_STR(head,strfield,add)                                          \
+    HASH_ADD(hh,head,strfield,strlen(add->strfield),add)
+#define HASH_FIND_INT(head,findint,out)                                          \
+    HASH_FIND(hh,head,findint,sizeof(int),out)
+#define HASH_ADD_INT(head,intfield,add)                                          \
+    HASH_ADD(hh,head,intfield,sizeof(int),add)
+#define HASH_FIND_PTR(head,findptr,out)                                          \
+    HASH_FIND(hh,head,findptr,sizeof(void *),out)
+#define HASH_ADD_PTR(head,ptrfield,add)                                          \
+    HASH_ADD(hh,head,ptrfield,sizeof(void *),add)
+#define HASH_DEL(head,delptr)                                                    \
+    HASH_DELETE(hh,head,delptr)
+
+/* HASH_FSCK checks hash integrity on every add/delete when HASH_DEBUG is defined.
+ * This is for uthash developer only; it compiles away if HASH_DEBUG isn't defined.
+ */
+#ifdef HASH_DEBUG
+#define HASH_OOPS(...) do { fprintf(stderr,__VA_ARGS__); exit(-1); } while (0)
+#define HASH_FSCK(hh,head)                                                       \
+do {                                                                             \
+    unsigned _bkt_i;                                                             \
+    unsigned _count, _bkt_count;                                                 \
+    char *_prev;                                                                 \
+    struct UT_hash_handle *_thh;                                                 \
+    if (head) {                                                                  \
+        _count = 0;                                                              \
+        for( _bkt_i = 0; _bkt_i < (head)->hh.tbl->num_buckets; _bkt_i++) {       \
+            _bkt_count = 0;                                                      \
+            _thh = (head)->hh.tbl->buckets[_bkt_i].hh_head;                      \
+            _prev = NULL;                                                        \
+            while (_thh) {                                                       \
+               if (_prev != (char*)(_thh->hh_prev)) {                            \
+                   HASH_OOPS("invalid hh_prev %p, actual %p\n",                  \
+                    _thh->hh_prev, _prev );                                      \
+               }                                                                 \
+               _bkt_count++;                                                     \
+               _prev = (char*)(_thh);                                            \
+               _thh = _thh->hh_next;                                             \
+            }                                                                    \
+            _count += _bkt_count;                                                \
+            if ((head)->hh.tbl->buckets[_bkt_i].count !=  _bkt_count) {          \
+               HASH_OOPS("invalid bucket count %d, actual %d\n",                 \
+                (head)->hh.tbl->buckets[_bkt_i].count, _bkt_count);              \
+            }                                                                    \
+        }                                                                        \
+        if (_count != (head)->hh.tbl->num_items) {                               \
+            HASH_OOPS("invalid hh item count %d, actual %d\n",                   \
+                (head)->hh.tbl->num_items, _count );                             \
+        }                                                                        \
+        /* traverse hh in app order; check next/prev integrity, count */         \
+        _count = 0;                                                              \
+        _prev = NULL;                                                            \
+        _thh =  &(head)->hh;                                                     \
+        while (_thh) {                                                           \
+           _count++;                                                             \
+           if (_prev !=(char*)(_thh->prev)) {                                    \
+              HASH_OOPS("invalid prev %p, actual %p\n",                          \
+                    _thh->prev, _prev );                                         \
+           }                                                                     \
+           _prev = (char*)ELMT_FROM_HH((head)->hh.tbl, _thh);                    \
+           _thh = ( _thh->next ?  (UT_hash_handle*)((char*)(_thh->next) +        \
+                                  (head)->hh.tbl->hho) : NULL );                 \
+        }                                                                        \
+        if (_count != (head)->hh.tbl->num_items) {                               \
+            HASH_OOPS("invalid app item count %d, actual %d\n",                  \
+                (head)->hh.tbl->num_items, _count );                             \
+        }                                                                        \
+    }                                                                            \
+} while (0)
+#else
+#define HASH_FSCK(hh,head) 
+#endif
+
+/* When compiled with -DHASH_EMIT_KEYS, length-prefixed keys are emitted to 
+ * the descriptor to which this macro is defined for tuning the hash function.
+ * The app can #include <unistd.h> to get the prototype for write(2). */
+#ifdef HASH_EMIT_KEYS
+#define HASH_EMIT_KEY(hh,head,keyptr,fieldlen)                                   \
+do {                                                                             \
+    unsigned _klen = fieldlen;                                                   \
+    write(HASH_EMIT_KEYS, &_klen, sizeof(_klen));                                \
+    write(HASH_EMIT_KEYS, keyptr, fieldlen);                                     \
+} while (0)
+#else 
+#define HASH_EMIT_KEY(hh,head,keyptr,fieldlen)                    
+#endif
+
+/* default to Jenkin's hash unless overridden e.g. DHASH_FUNCTION=HASH_SAX */
+#ifdef HASH_FUNCTION 
+#define HASH_FCN HASH_FUNCTION
+#else
+#define HASH_FCN HASH_JEN
+#endif
+
+/* The Bernstein hash function, used in Perl prior to v5.6 */
+#define HASH_BER(key,keylen,num_bkts,hashv,bkt)                                  \
+do {                                                                             \
+  unsigned _hb_keylen=keylen;                                                    \
+  char *_hb_key=(char*)(key);                                                    \
+  (hashv) = 0;                                                                   \
+  while (_hb_keylen--)  { (hashv) = ((hashv) * 33) + *_hb_key++; }               \
+  bkt = (hashv) & (num_bkts-1);                                                  \
+} while (0)
+
+
+/* SAX/FNV/OAT/JEN hash functions are macro variants of those listed at 
+ * http://eternallyconfuzzled.com/tuts/algorithms/jsw_tut_hashing.aspx */
+#define HASH_SAX(key,keylen,num_bkts,hashv,bkt)                                  \
+do {                                                                             \
+  unsigned _sx_i;                                                                \
+  char *_hs_key=(char*)(key);                                                    \
+  hashv = 0;                                                                     \
+  for(_sx_i=0; _sx_i < keylen; _sx_i++)                                          \
+      hashv ^= (hashv << 5) + (hashv >> 2) + _hs_key[_sx_i];                     \
+  bkt = hashv & (num_bkts-1);                                                    \
+} while (0)
+
+#define HASH_FNV(key,keylen,num_bkts,hashv,bkt)                                  \
+do {                                                                             \
+  unsigned _fn_i;                                                                \
+  char *_hf_key=(char*)(key);                                                    \
+  hashv = 2166136261UL;                                                          \
+  for(_fn_i=0; _fn_i < keylen; _fn_i++)                                          \
+      hashv = (hashv * 16777619) ^ _hf_key[_fn_i];                               \
+  bkt = hashv & (num_bkts-1);                                                    \
+} while(0);
+#define HASH_OAT(key,keylen,num_bkts,hashv,bkt)                                  \
+do {                                                                             \
+  unsigned _ho_i;                                                                \
+  char *_ho_key=(char*)(key);                                                    \
+  hashv = 0;                                                                     \
+  for(_ho_i=0; _ho_i < keylen; _ho_i++) {                                        \
+      hashv += _ho_key[_ho_i];                                                   \
+      hashv += (hashv << 10);                                                    \
+      hashv ^= (hashv >> 6);                                                     \
+  }                                                                              \
+  hashv += (hashv << 3);                                                         \
+  hashv ^= (hashv >> 11);                                                        \
+  hashv += (hashv << 15);                                                        \
+  bkt = hashv & (num_bkts-1);                                                    \
+} while(0)
+
+#define HASH_JEN_MIX(a,b,c)                                                      \
+do {                                                                             \
+  a -= b; a -= c; a ^= ( c >> 13 );                                              \
+  b -= c; b -= a; b ^= ( a << 8 );                                               \
+  c -= a; c -= b; c ^= ( b >> 13 );                                              \
+  a -= b; a -= c; a ^= ( c >> 12 );                                              \
+  b -= c; b -= a; b ^= ( a << 16 );                                              \
+  c -= a; c -= b; c ^= ( b >> 5 );                                               \
+  a -= b; a -= c; a ^= ( c >> 3 );                                               \
+  b -= c; b -= a; b ^= ( a << 10 );                                              \
+  c -= a; c -= b; c ^= ( b >> 15 );                                              \
+} while (0)
+
+#define HASH_JEN(key,keylen,num_bkts,hashv,bkt)                                  \
+do {                                                                             \
+  unsigned _hj_i,_hj_j,_hj_k;                                                    \
+  char *_hj_key=(char*)(key);                                                    \
+  hashv = 0xfeedbeef;                                                            \
+  _hj_i = _hj_j = 0x9e3779b9;                                                    \
+  _hj_k = keylen;                                                                \
+  while (_hj_k >= 12) {                                                          \
+    _hj_i +=    (_hj_key[0] + ( (unsigned)_hj_key[1] << 8 )                      \
+        + ( (unsigned)_hj_key[2] << 16 )                                         \
+        + ( (unsigned)_hj_key[3] << 24 ) );                                      \
+    _hj_j +=    (_hj_key[4] + ( (unsigned)_hj_key[5] << 8 )                      \
+        + ( (unsigned)_hj_key[6] << 16 )                                         \
+        + ( (unsigned)_hj_key[7] << 24 ) );                                      \
+    hashv += (_hj_key[8] + ( (unsigned)_hj_key[9] << 8 )                         \
+        + ( (unsigned)_hj_key[10] << 16 )                                        \
+        + ( (unsigned)_hj_key[11] << 24 ) );                                     \
+                                                                                 \
+     HASH_JEN_MIX(_hj_i, _hj_j, hashv);                                          \
+                                                                                 \
+     _hj_key += 12;                                                              \
+     _hj_k -= 12;                                                                \
+  }                                                                              \
+  hashv += keylen;                                                               \
+  switch ( _hj_k ) {                                                             \
+     case 11: hashv += ( (unsigned)_hj_key[10] << 24 );                          \
+     case 10: hashv += ( (unsigned)_hj_key[9] << 16 );                           \
+     case 9:  hashv += ( (unsigned)_hj_key[8] << 8 );                            \
+     case 8:  _hj_j += ( (unsigned)_hj_key[7] << 24 );                           \
+     case 7:  _hj_j += ( (unsigned)_hj_key[6] << 16 );                           \
+     case 6:  _hj_j += ( (unsigned)_hj_key[5] << 8 );                            \
+     case 5:  _hj_j += _hj_key[4];                                               \
+     case 4:  _hj_i += ( (unsigned)_hj_key[3] << 24 );                           \
+     case 3:  _hj_i += ( (unsigned)_hj_key[2] << 16 );                           \
+     case 2:  _hj_i += ( (unsigned)_hj_key[1] << 8 );                            \
+     case 1:  _hj_i += _hj_key[0];                                               \
+  }                                                                              \
+  HASH_JEN_MIX(_hj_i, _hj_j, hashv);                                             \
+  bkt = hashv & (num_bkts-1);                                                    \
+} while(0)
+
+/* The Paul Hsieh hash function */
+#undef get16bits
+#if (defined(__GNUC__) && defined(__i386__)) || defined(__WATCOMC__)             \
+  || defined(_MSC_VER) || defined (__BORLANDC__) || defined (__TURBOC__)
+#define get16bits(d) (*((const uint16_t *) (d)))
+#endif
+
+#if !defined (get16bits)
+#define get16bits(d) ((((uint32_t)(((const uint8_t *)(d))[1])) << 8)             \
+                       +(uint32_t)(((const uint8_t *)(d))[0]) )
+#endif
+#define HASH_SFH(key,keylen,num_bkts,hashv,bkt)                                  \
+do {                                                                             \
+  char *_sfh_key=(char*)(key);                                                   \
+  uint32_t _sfh_tmp, _sfh_len = keylen;                                          \
+                                                                                 \
+  int _sfh_rem = _sfh_len & 3;                                                   \
+  _sfh_len >>= 2;                                                                \
+  hashv = 0xcafebabe;                                                            \
+                                                                                 \
+  /* Main loop */                                                                \
+  for (;_sfh_len > 0; _sfh_len--) {                                              \
+    hashv    += get16bits (_sfh_key);                                            \
+    _sfh_tmp       = (get16bits (_sfh_key+2) << 11) ^ hashv;                     \
+    hashv     = (hashv << 16) ^ _sfh_tmp;                                        \
+    _sfh_key += 2*sizeof (uint16_t);                                             \
+    hashv    += hashv >> 11;                                                     \
+  }                                                                              \
+                                                                                 \
+  /* Handle end cases */                                                         \
+  switch (_sfh_rem) {                                                            \
+    case 3: hashv += get16bits (_sfh_key);                                       \
+            hashv ^= hashv << 16;                                                \
+            hashv ^= _sfh_key[sizeof (uint16_t)] << 18;                          \
+            hashv += hashv >> 11;                                                \
+            break;                                                               \
+    case 2: hashv += get16bits (_sfh_key);                                       \
+            hashv ^= hashv << 11;                                                \
+            hashv += hashv >> 17;                                                \
+            break;                                                               \
+    case 1: hashv += *_sfh_key;                                                  \
+            hashv ^= hashv << 10;                                                \
+            hashv += hashv >> 1;                                                 \
+  }                                                                              \
+                                                                                 \
+    /* Force "avalanching" of final 127 bits */                                  \
+    hashv ^= hashv << 3;                                                         \
+    hashv += hashv >> 5;                                                         \
+    hashv ^= hashv << 4;                                                         \
+    hashv += hashv >> 17;                                                        \
+    hashv ^= hashv << 25;                                                        \
+    hashv += hashv >> 6;                                                         \
+    bkt = hashv & (num_bkts-1);                                                  \
+} while(0);
+
+#ifdef HASH_USING_NO_STRICT_ALIASING
+/* The MurmurHash exploits some CPU's (e.g. x86) tolerance for unaligned reads.
+ * For other types of CPU's (e.g. Sparc) an unaligned read causes a bus error.
+ * So MurmurHash comes in two versions, the faster unaligned one and the slower
+ * aligned one. We only use the faster one on CPU's where we know it's safe. 
+ *
+ * Note the preprocessor built-in defines can be emitted using:
+ *
+ *   gcc -m64 -dM -E - < /dev/null                  (on gcc)
+ *   cc -## a.c (where a.c is a simple test file)   (Sun Studio)
+ */
+#if (defined(__i386__) || defined(__x86_64__)) 
+#define HASH_MUR HASH_MUR_UNALIGNED
+#else
+#define HASH_MUR HASH_MUR_ALIGNED
+#endif
+
+/* Appleby's MurmurHash fast version for unaligned-tolerant archs like i386 */
+#define HASH_MUR_UNALIGNED(key,keylen,num_bkts,hashv,bkt)                        \
+do {                                                                             \
+  const unsigned int _mur_m = 0x5bd1e995;                                        \
+  const int _mur_r = 24;                                                         \
+  hashv = 0xcafebabe ^ keylen;                                                   \
+  char *_mur_key = (char *)(key);                                                \
+  uint32_t _mur_tmp, _mur_len = keylen;                                          \
+                                                                                 \
+  for (;_mur_len >= 4; _mur_len-=4) {                                            \
+    _mur_tmp = *(uint32_t *)_mur_key;                                            \
+    _mur_tmp *= _mur_m;                                                          \
+    _mur_tmp ^= _mur_tmp >> _mur_r;                                              \
+    _mur_tmp *= _mur_m;                                                          \
+    hashv *= _mur_m;                                                             \
+    hashv ^= _mur_tmp;                                                           \
+    _mur_key += 4;                                                               \
+  }                                                                              \
+                                                                                 \
+  switch(_mur_len)                                                               \
+  {                                                                              \
+    case 3: hashv ^= _mur_key[2] << 16;                                          \
+    case 2: hashv ^= _mur_key[1] << 8;                                           \
+    case 1: hashv ^= _mur_key[0];                                                \
+            hashv *= _mur_m;                                                     \
+  };                                                                             \
+                                                                                 \
+  hashv ^= hashv >> 13;                                                          \
+  hashv *= _mur_m;                                                               \
+  hashv ^= hashv >> 15;                                                          \
+                                                                                 \
+  bkt = hashv & (num_bkts-1);                                                    \
+} while(0)
+
+/* Appleby's MurmurHash version for alignment-sensitive archs like Sparc */
+#define HASH_MUR_ALIGNED(key,keylen,num_bkts,hashv,bkt)                          \
+do {                                                                             \
+  const unsigned int _mur_m = 0x5bd1e995;                                        \
+  const int _mur_r = 24;                                                         \
+  hashv = 0xcafebabe ^ (keylen);                                                 \
+  char *_mur_key = (char *)(key);                                                \
+  uint32_t _mur_len = keylen;                                                    \
+  int _mur_align = (int)_mur_key & 3;                                            \
+                                                                                 \
+  if (_mur_align && (_mur_len >= 4)) {                                           \
+    unsigned _mur_t = 0, _mur_d = 0;                                             \
+    switch(_mur_align) {                                                         \
+      case 1: _mur_t |= _mur_key[2] << 16;                                       \
+      case 2: _mur_t |= _mur_key[1] << 8;                                        \
+      case 3: _mur_t |= _mur_key[0];                                             \
+    }                                                                            \
+    _mur_t <<= (8 * _mur_align);                                                 \
+    _mur_key += 4-_mur_align;                                                    \
+    _mur_len -= 4-_mur_align;                                                    \
+    int _mur_sl = 8 * (4-_mur_align);                                            \
+    int _mur_sr = 8 * _mur_align;                                                \
+                                                                                 \
+    for (;_mur_len >= 4; _mur_len-=4) {                                          \
+      _mur_d = *(unsigned *)_mur_key;                                            \
+      _mur_t = (_mur_t >> _mur_sr) | (_mur_d << _mur_sl);                        \
+      unsigned _mur_k = _mur_t;                                                  \
+      _mur_k *= _mur_m;                                                          \
+      _mur_k ^= _mur_k >> _mur_r;                                                \
+      _mur_k *= _mur_m;                                                          \
+      hashv *= _mur_m;                                                           \
+      hashv ^= _mur_k;                                                           \
+      _mur_t = _mur_d;                                                           \
+      _mur_key += 4;                                                             \
+    }                                                                            \
+    _mur_d = 0;                                                                  \
+    if(_mur_len >= _mur_align) {                                                 \
+      switch(_mur_align) {                                                       \
+        case 3: _mur_d |= _mur_key[2] << 16;                                     \
+        case 2: _mur_d |= _mur_key[1] << 8;                                      \
+        case 1: _mur_d |= _mur_key[0];                                           \
+      }                                                                          \
+      unsigned _mur_k = (_mur_t >> _mur_sr) | (_mur_d << _mur_sl);               \
+      _mur_k *= _mur_m;                                                          \
+      _mur_k ^= _mur_k >> _mur_r;                                                \
+      _mur_k *= _mur_m;                                                          \
+      hashv *= _mur_m;                                                           \
+      hashv ^= _mur_k;                                                           \
+      _mur_k += _mur_align;                                                      \
+      _mur_len -= _mur_align;                                                    \
+                                                                                 \
+      switch(_mur_len)                                                           \
+      {                                                                          \
+        case 3: hashv ^= _mur_key[2] << 16;                                      \
+        case 2: hashv ^= _mur_key[1] << 8;                                       \
+        case 1: hashv ^= _mur_key[0];                                            \
+                hashv *= _mur_m;                                                 \
+      }                                                                          \
+    } else {                                                                     \
+      switch(_mur_len)                                                           \
+      {                                                                          \
+        case 3: _mur_d ^= _mur_key[2] << 16;                                     \
+        case 2: _mur_d ^= _mur_key[1] << 8;                                      \
+        case 1: _mur_d ^= _mur_key[0];                                           \
+        case 0: hashv ^= (_mur_t >> _mur_sr) | (_mur_d << _mur_sl);              \
+        hashv *= _mur_m;                                                         \
+      }                                                                          \
+    }                                                                            \
+                                                                                 \
+    hashv ^= hashv >> 13;                                                        \
+    hashv *= _mur_m;                                                             \
+    hashv ^= hashv >> 15;                                                        \
+  } else {                                                                       \
+    for (;_mur_len >= 4; _mur_len-=4) {                                          \
+      unsigned _mur_k = *(unsigned*)_mur_key;                                    \
+      _mur_k *= _mur_m;                                                          \
+      _mur_k ^= _mur_k >> _mur_r;                                                \
+      _mur_k *= _mur_m;                                                          \
+      hashv *= _mur_m;                                                           \
+      hashv ^= _mur_k;                                                           \
+      _mur_key += 4;                                                             \
+    }                                                                            \
+    switch(_mur_len)                                                             \
+    {                                                                            \
+      case 3: hashv ^= _mur_key[2] << 16;                                        \
+      case 2: hashv ^= _mur_key[1] << 8;                                         \
+      case 1: hashv ^= _mur_key[0];                                              \
+      hashv *= _mur_m;                                                           \
+    }                                                                            \
+                                                                                 \
+    hashv ^= hashv >> 13;                                                        \
+    hashv *= _mur_m;                                                             \
+    hashv ^= hashv >> 15;                                                        \
+  }                                                                              \
+  bkt = hashv & (num_bkts-1);                                                    \
+} while(0)
+#endif  /* HASH_USING_NO_STRICT_ALIASING */
+
+/* key comparison function; return 0 if keys equal */
+#define HASH_KEYCMP(a,b,len) memcmp(a,b,len) 
+
+/* iterate over items in a known bucket to find desired item */
+#define HASH_FIND_IN_BKT(tbl,hh,head,keyptr,keylen_in,out)                       \
+do {                                                                             \
+ if (head.hh_head) DECLTYPE_ASSIGN(out,ELMT_FROM_HH(tbl,head.hh_head));          \
+ else out=NULL;                                                                  \
+ while (out) {                                                                   \
+    if (out->hh.keylen == keylen_in) {                                           \
+        if ((HASH_KEYCMP(out->hh.key,keyptr,keylen_in)) == 0) break;             \
+    }                                                                            \
+    if (out->hh.hh_next) DECLTYPE_ASSIGN(out,ELMT_FROM_HH(tbl,out->hh.hh_next)); \
+    else out = NULL;                                                             \
+ }                                                                               \
+} while(0)
+
+/* add an item to a bucket  */
+#define HASH_ADD_TO_BKT(head,addhh)                                              \
+do {                                                                             \
+ head.count++;                                                                   \
+ (addhh)->hh_next = head.hh_head;                                                \
+ (addhh)->hh_prev = NULL;                                                        \
+ if (head.hh_head) { (head).hh_head->hh_prev = (addhh); }                        \
+ (head).hh_head=addhh;                                                           \
+ if (head.count >= ((head.expand_mult+1) * HASH_BKT_CAPACITY_THRESH)             \
+     && (addhh)->tbl->noexpand != 1) {                                           \
+       HASH_EXPAND_BUCKETS((addhh)->tbl);                                        \
+ }                                                                               \
+} while(0)
+
+/* remove an item from a given bucket */
+#define HASH_DEL_IN_BKT(hh,head,hh_del)                                          \
+    (head).count--;                                                              \
+    if ((head).hh_head == hh_del) {                                              \
+      (head).hh_head = hh_del->hh_next;                                          \
+    }                                                                            \
+    if (hh_del->hh_prev) {                                                       \
+        hh_del->hh_prev->hh_next = hh_del->hh_next;                              \
+    }                                                                            \
+    if (hh_del->hh_next) {                                                       \
+        hh_del->hh_next->hh_prev = hh_del->hh_prev;                              \
+    }                                                                
+
+/* Bucket expansion has the effect of doubling the number of buckets
+ * and redistributing the items into the new buckets. Ideally the
+ * items will distribute more or less evenly into the new buckets
+ * (the extent to which this is true is a measure of the quality of
+ * the hash function as it applies to the key domain). 
+ * 
+ * With the items distributed into more buckets, the chain length
+ * (item count) in each bucket is reduced. Thus by expanding buckets
+ * the hash keeps a bound on the chain length. This bounded chain 
+ * length is the essence of how a hash provides constant time lookup.
+ * 
+ * The calculation of tbl->ideal_chain_maxlen below deserves some
+ * explanation. First, keep in mind that we're calculating the ideal
+ * maximum chain length based on the *new* (doubled) bucket count.
+ * In fractions this is just n/b (n=number of items,b=new num buckets).
+ * Since the ideal chain length is an integer, we want to calculate 
+ * ceil(n/b). We don't depend on floating point arithmetic in this
+ * hash, so to calculate ceil(n/b) with integers we could write
+ * 
+ *      ceil(n/b) = (n/b) + ((n%b)?1:0)
+ * 
+ * and in fact a previous version of this hash did just that.
+ * But now we have improved things a bit by recognizing that b is
+ * always a power of two. We keep its base 2 log handy (call it lb),
+ * so now we can write this with a bit shift and logical AND:
+ * 
+ *      ceil(n/b) = (n>>lb) + ( (n & (b-1)) ? 1:0)
+ * 
+ */
+#define HASH_EXPAND_BUCKETS(tbl)                                                 \
+do {                                                                             \
+    unsigned _he_bkt;                                                            \
+    unsigned _he_bkt_i;                                                          \
+    struct UT_hash_handle *_he_thh, *_he_hh_nxt;                                 \
+    UT_hash_bucket *_he_new_buckets, *_he_newbkt;                                \
+    _he_new_buckets = (UT_hash_bucket*)uthash_malloc(                            \
+             2 * tbl->num_buckets * sizeof(struct UT_hash_bucket));              \
+    if (!_he_new_buckets) { uthash_fatal( "out of memory"); }                    \
+    memset(_he_new_buckets, 0,                                                   \
+            2 * tbl->num_buckets * sizeof(struct UT_hash_bucket));               \
+    tbl->ideal_chain_maxlen =                                                    \
+       (tbl->num_items >> (tbl->log2_num_buckets+1)) +                           \
+       ((tbl->num_items & ((tbl->num_buckets*2)-1)) ? 1 : 0);                    \
+    tbl->nonideal_items = 0;                                                     \
+    for(_he_bkt_i = 0; _he_bkt_i < tbl->num_buckets; _he_bkt_i++)                \
+    {                                                                            \
+        _he_thh = tbl->buckets[ _he_bkt_i ].hh_head;                             \
+        while (_he_thh) {                                                        \
+           _he_hh_nxt = _he_thh->hh_next;                                        \
+           HASH_TO_BKT( _he_thh->hashv, tbl->num_buckets*2, _he_bkt);            \
+           _he_newbkt = &(_he_new_buckets[ _he_bkt ]);                           \
+           if (++(_he_newbkt->count) > tbl->ideal_chain_maxlen) {                \
+             tbl->nonideal_items++;                                              \
+             _he_newbkt->expand_mult = _he_newbkt->count /                       \
+                                        tbl->ideal_chain_maxlen;                 \
+           }                                                                     \
+           _he_thh->hh_prev = NULL;                                              \
+           _he_thh->hh_next = _he_newbkt->hh_head;                               \
+           if (_he_newbkt->hh_head) _he_newbkt->hh_head->hh_prev =               \
+                _he_thh;                                                         \
+           _he_newbkt->hh_head = _he_thh;                                        \
+           _he_thh = _he_hh_nxt;                                                 \
+        }                                                                        \
+    }                                                                            \
+    uthash_free( tbl->buckets, tbl->num_buckets*sizeof(struct UT_hash_bucket) ); \
+    tbl->num_buckets *= 2;                                                       \
+    tbl->log2_num_buckets++;                                                     \
+    tbl->buckets = _he_new_buckets;                                              \
+    tbl->ineff_expands = (tbl->nonideal_items > (tbl->num_items >> 1)) ?         \
+        (tbl->ineff_expands+1) : 0;                                              \
+    if (tbl->ineff_expands > 1) {                                                \
+        tbl->noexpand=1;                                                         \
+        uthash_noexpand_fyi(tbl);                                                \
+    }                                                                            \
+    uthash_expand_fyi(tbl);                                                      \
+} while(0)
+
+
+/* This is an adaptation of Simon Tatham's O(n log(n)) mergesort */
+/* Note that HASH_SORT assumes the hash handle name to be hh. 
+ * HASH_SRT was added to allow the hash handle name to be passed in. */
+#define HASH_SORT(head,cmpfcn) HASH_SRT(hh,head,cmpfcn)
+#define HASH_SRT(hh,head,cmpfcn)                                                 \
+do {                                                                             \
+  unsigned _hs_i;                                                                \
+  unsigned _hs_looping,_hs_nmerges,_hs_insize,_hs_psize,_hs_qsize;               \
+  struct UT_hash_handle *_hs_p, *_hs_q, *_hs_e, *_hs_list, *_hs_tail;            \
+  if (head) {                                                                    \
+      _hs_insize = 1;                                                            \
+      _hs_looping = 1;                                                           \
+      _hs_list = &((head)->hh);                                                  \
+      while (_hs_looping) {                                                      \
+          _hs_p = _hs_list;                                                      \
+          _hs_list = NULL;                                                       \
+          _hs_tail = NULL;                                                       \
+          _hs_nmerges = 0;                                                       \
+          while (_hs_p) {                                                        \
+              _hs_nmerges++;                                                     \
+              _hs_q = _hs_p;                                                     \
+              _hs_psize = 0;                                                     \
+              for ( _hs_i = 0; _hs_i  < _hs_insize; _hs_i++ ) {                  \
+                  _hs_psize++;                                                   \
+                  _hs_q = (UT_hash_handle*)((_hs_q->next) ?                      \
+                          ((void*)((char*)(_hs_q->next) +                        \
+                          (head)->hh.tbl->hho)) : NULL);                         \
+                  if (! (_hs_q) ) break;                                         \
+              }                                                                  \
+              _hs_qsize = _hs_insize;                                            \
+              while ((_hs_psize > 0) || ((_hs_qsize > 0) && _hs_q )) {           \
+                  if (_hs_psize == 0) {                                          \
+                      _hs_e = _hs_q;                                             \
+                      _hs_q = (UT_hash_handle*)((_hs_q->next) ?                  \
+                              ((void*)((char*)(_hs_q->next) +                    \
+                              (head)->hh.tbl->hho)) : NULL);                     \
+                      _hs_qsize--;                                               \
+                  } else if ( (_hs_qsize == 0) || !(_hs_q) ) {                   \
+                      _hs_e = _hs_p;                                             \
+                      _hs_p = (UT_hash_handle*)((_hs_p->next) ?                  \
+                              ((void*)((char*)(_hs_p->next) +                    \
+                              (head)->hh.tbl->hho)) : NULL);                     \
+                      _hs_psize--;                                               \
+                  } else if ((                                                   \
+                      cmpfcn(DECLTYPE(head)(ELMT_FROM_HH((head)->hh.tbl,_hs_p)), \
+                             DECLTYPE(head)(ELMT_FROM_HH((head)->hh.tbl,_hs_q))) \
+                             ) <= 0) {                                           \
+                      _hs_e = _hs_p;                                             \
+                      _hs_p = (UT_hash_handle*)((_hs_p->next) ?                  \
+                              ((void*)((char*)(_hs_p->next) +                    \
+                              (head)->hh.tbl->hho)) : NULL);                     \
+                      _hs_psize--;                                               \
+                  } else {                                                       \
+                      _hs_e = _hs_q;                                             \
+                      _hs_q = (UT_hash_handle*)((_hs_q->next) ?                  \
+                              ((void*)((char*)(_hs_q->next) +                    \
+                              (head)->hh.tbl->hho)) : NULL);                     \
+                      _hs_qsize--;                                               \
+                  }                                                              \
+                  if ( _hs_tail ) {                                              \
+                      _hs_tail->next = ((_hs_e) ?                                \
+                            ELMT_FROM_HH((head)->hh.tbl,_hs_e) : NULL);          \
+                  } else {                                                       \
+                      _hs_list = _hs_e;                                          \
+                  }                                                              \
+                  _hs_e->prev = ((_hs_tail) ?                                    \
+                     ELMT_FROM_HH((head)->hh.tbl,_hs_tail) : NULL);              \
+                  _hs_tail = _hs_e;                                              \
+              }                                                                  \
+              _hs_p = _hs_q;                                                     \
+          }                                                                      \
+          _hs_tail->next = NULL;                                                 \
+          if ( _hs_nmerges <= 1 ) {                                              \
+              _hs_looping=0;                                                     \
+              (head)->hh.tbl->tail = _hs_tail;                                   \
+              DECLTYPE_ASSIGN(head,ELMT_FROM_HH((head)->hh.tbl, _hs_list));      \
+          }                                                                      \
+          _hs_insize *= 2;                                                       \
+      }                                                                          \
+      HASH_FSCK(hh,head);                                                        \
+ }                                                                               \
+} while (0)
+
+/* This function selects items from one hash into another hash. 
+ * The end result is that the selected items have dual presence 
+ * in both hashes. There is no copy of the items made; rather 
+ * they are added into the new hash through a secondary hash 
+ * hash handle that must be present in the structure. */
+#define HASH_SELECT(hh_dst, dst, hh_src, src, cond)                              \
+do {                                                                             \
+  unsigned _src_bkt, _dst_bkt;                                                   \
+  void *_last_elt=NULL, *_elt;                                                   \
+  UT_hash_handle *_src_hh, *_dst_hh, *_last_elt_hh=NULL;                         \
+  ptrdiff_t _dst_hho = ((char*)(&(dst)->hh_dst) - (char*)(dst));                 \
+  if (src) {                                                                     \
+    for(_src_bkt=0; _src_bkt < (src)->hh_src.tbl->num_buckets; _src_bkt++) {     \
+      for(_src_hh = (src)->hh_src.tbl->buckets[_src_bkt].hh_head;                \
+          _src_hh;                                                               \
+          _src_hh = _src_hh->hh_next) {                                          \
+          _elt = ELMT_FROM_HH((src)->hh_src.tbl, _src_hh);                       \
+          if (cond(_elt)) {                                                      \
+            _dst_hh = (UT_hash_handle*)(((char*)_elt) + _dst_hho);               \
+            _dst_hh->key = _src_hh->key;                                         \
+            _dst_hh->keylen = _src_hh->keylen;                                   \
+            _dst_hh->hashv = _src_hh->hashv;                                     \
+            _dst_hh->prev = _last_elt;                                           \
+            _dst_hh->next = NULL;                                                \
+            if (_last_elt_hh) { _last_elt_hh->next = _elt; }                     \
+            if (!dst) {                                                          \
+              DECLTYPE_ASSIGN(dst,_elt);                                         \
+              HASH_MAKE_TABLE(hh_dst,dst);                                       \
+            } else {                                                             \
+              _dst_hh->tbl = (dst)->hh_dst.tbl;                                  \
+            }                                                                    \
+            HASH_TO_BKT(_dst_hh->hashv, _dst_hh->tbl->num_buckets, _dst_bkt);    \
+            HASH_ADD_TO_BKT(_dst_hh->tbl->buckets[_dst_bkt],_dst_hh);            \
+            (dst)->hh_dst.tbl->num_items++;                                      \
+            _last_elt = _elt;                                                    \
+            _last_elt_hh = _dst_hh;                                              \
+          }                                                                      \
+      }                                                                          \
+    }                                                                            \
+  }                                                                              \
+  HASH_FSCK(hh_dst,dst);                                                         \
+} while (0)
+
+#define HASH_CLEAR(hh,head)                                                      \
+do {                                                                             \
+  if (head) {                                                                    \
+    uthash_free((head)->hh.tbl->buckets,                                         \
+                (head)->hh.tbl->num_buckets*sizeof(struct UT_hash_bucket));      \
+    uthash_free((head)->hh.tbl, sizeof(UT_hash_table));                          \
+    (head)=NULL;                                                                 \
+  }                                                                              \
+} while(0)
+
+#ifdef NO_DECLTYPE
+#define HASH_ITER(hh,head,el,tmp)                                                \
+for((el)=(head), (*(char**)(&(tmp)))=(char*)((head)?(head)->hh.next:NULL);       \
+  el; (el)=(tmp),(*(char**)(&(tmp)))=(char*)((tmp)?(tmp)->hh.next:NULL)) 
+#else
+#define HASH_ITER(hh,head,el,tmp)                                                \
+for((el)=(head),(tmp)=DECLTYPE(el)((head)?(head)->hh.next:NULL);                 \
+  el; (el)=(tmp),(tmp)=DECLTYPE(el)((tmp)?(tmp)->hh.next:NULL))
+#endif
+
+/* obtain a count of items in the hash */
+#define HASH_COUNT(head) HASH_CNT(hh,head) 
+#define HASH_CNT(hh,head) ((head)?((head)->hh.tbl->num_items):0)
+
+typedef struct UT_hash_bucket {
+   struct UT_hash_handle *hh_head;
+   unsigned count;
+
+   /* expand_mult is normally set to 0. In this situation, the max chain length
+    * threshold is enforced at its default value, HASH_BKT_CAPACITY_THRESH. (If
+    * the bucket's chain exceeds this length, bucket expansion is triggered). 
+    * However, setting expand_mult to a non-zero value delays bucket expansion
+    * (that would be triggered by additions to this particular bucket)
+    * until its chain length reaches a *multiple* of HASH_BKT_CAPACITY_THRESH.
+    * (The multiplier is simply expand_mult+1). The whole idea of this
+    * multiplier is to reduce bucket expansions, since they are expensive, in
+    * situations where we know that a particular bucket tends to be overused.
+    * It is better to let its chain length grow to a longer yet-still-bounded
+    * value, than to do an O(n) bucket expansion too often. 
+    */
+   unsigned expand_mult;
+
+} UT_hash_bucket;
+
+/* random signature used only to find hash tables in external analysis */
+#define HASH_SIGNATURE 0xa0111fe1
+#define HASH_BLOOM_SIGNATURE 0xb12220f2
+
+typedef struct UT_hash_table {
+   UT_hash_bucket *buckets;
+   unsigned num_buckets, log2_num_buckets;
+   unsigned num_items;
+   struct UT_hash_handle *tail; /* tail hh in app order, for fast append    */
+   ptrdiff_t hho; /* hash handle offset (byte pos of hash handle in element */
+
+   /* in an ideal situation (all buckets used equally), no bucket would have
+    * more than ceil(#items/#buckets) items. that's the ideal chain length. */
+   unsigned ideal_chain_maxlen;
+
+   /* nonideal_items is the number of items in the hash whose chain position
+    * exceeds the ideal chain maxlen. these items pay the penalty for an uneven
+    * hash distribution; reaching them in a chain traversal takes >ideal steps */
+   unsigned nonideal_items;
+
+   /* ineffective expands occur when a bucket doubling was performed, but 
+    * afterward, more than half the items in the hash had nonideal chain
+    * positions. If this happens on two consecutive expansions we inhibit any
+    * further expansion, as it's not helping; this happens when the hash
+    * function isn't a good fit for the key domain. When expansion is inhibited
+    * the hash will still work, albeit no longer in constant time. */
+   unsigned ineff_expands, noexpand;
+
+   uint32_t signature; /* used only to find hash tables in external analysis */
+#ifdef HASH_BLOOM
+   uint32_t bloom_sig; /* used only to test bloom exists in external analysis */
+   uint8_t *bloom_bv;
+   char bloom_nbits;
+#endif
+
+} UT_hash_table;
+
+typedef struct UT_hash_handle {
+   struct UT_hash_table *tbl;
+   void *prev;                       /* prev element in app order      */
+   void *next;                       /* next element in app order      */
+   struct UT_hash_handle *hh_prev;   /* previous hh in bucket order    */
+   struct UT_hash_handle *hh_next;   /* next hh in bucket order        */
+   void *key;                        /* ptr to enclosing struct's key  */
+   unsigned keylen;                  /* enclosing struct's key len     */
+   unsigned hashv;                   /* result of hash-fcn(key)        */
+} UT_hash_handle;
+
+#endif /* UTHASH_H */
diff --git a/libs/cocos2d/Support/utlist.h b/libs/cocos2d/Support/utlist.h
new file mode 100644 (file)
index 0000000..34c725b
--- /dev/null
@@ -0,0 +1,490 @@
+/*
+Copyright (c) 2007-2010, Troy D. Hanson   http://uthash.sourceforge.net
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
+OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef UTLIST_H
+#define UTLIST_H
+
+#define UTLIST_VERSION 1.9.1
+
+/* 
+ * This file contains macros to manipulate singly and doubly-linked lists.
+ *
+ * 1. LL_ macros:  singly-linked lists.
+ * 2. DL_ macros:  doubly-linked lists.
+ * 3. CDL_ macros: circular doubly-linked lists.
+ *
+ * To use singly-linked lists, your structure must have a "next" pointer.
+ * To use doubly-linked lists, your structure must "prev" and "next" pointers.
+ * Either way, the pointer to the head of the list must be initialized to NULL.
+ * 
+ * ----------------.EXAMPLE -------------------------
+ * struct item {
+ *      int id;
+ *      struct item *prev, *next;
+ * }
+ *
+ * struct item *list = NULL:
+ *
+ * int main() {
+ *      struct item *item;
+ *      ... allocate and populate item ...
+ *      DL_APPEND(list, item);
+ * }
+ * --------------------------------------------------
+ *
+ * For doubly-linked lists, the append and delete macros are O(1)
+ * For singly-linked lists, append and delete are O(n) but prepend is O(1)
+ * The sort macro is O(n log(n)) for all types of single/double/circular lists.
+ */
+
+/* These macros use decltype or the earlier __typeof GNU extension.
+   As decltype is only available in newer compilers (VS2010 or gcc 4.3+
+   when compiling c++ code), this code uses whatever method is needed
+   or, for VS2008 where neither is available, uses casting workarounds. */
+#ifdef _MSC_VER            /* MS compiler */
+#if _MSC_VER >= 1600 && defined(__cplusplus)  /* VS2010 or newer in C++ mode */
+#define LDECLTYPE(x) decltype(x)
+#else                     /* VS2008 or older (or VS2010 in C mode) */
+#define NO_DECLTYPE
+#define LDECLTYPE(x) char*
+#endif
+#else                      /* GNU, Sun and other compilers */
+#define LDECLTYPE(x) __typeof(x)
+#endif
+
+/* for VS2008 we use some workarounds to get around the lack of decltype,
+ * namely, we always reassign our tmp variable to the list head if we need
+ * to dereference its prev/next pointers, and save/restore the real head.*/
+#ifdef NO_DECLTYPE
+#define _SV(elt,list) _tmp = (char*)(list); {char **_alias = (char**)&(list); *_alias = (elt); }
+#define _NEXT(elt,list) ((char*)((list)->next))
+#define _NEXTASGN(elt,list,to) { char **_alias = (char**)&((list)->next); *_alias=(char*)(to); }
+#define _PREV(elt,list) ((char*)((list)->prev))
+#define _PREVASGN(elt,list,to) { char **_alias = (char**)&((list)->prev); *_alias=(char*)(to); }
+#define _RS(list) { char **_alias = (char**)&(list); *_alias=_tmp; }
+#define _CASTASGN(a,b) { char **_alias = (char**)&(a); *_alias=(char*)(b); }
+#else 
+#define _SV(elt,list)
+#define _NEXT(elt,list) ((elt)->next)
+#define _NEXTASGN(elt,list,to) ((elt)->next)=(to)
+#define _PREV(elt,list) ((elt)->prev)
+#define _PREVASGN(elt,list,to) ((elt)->prev)=(to)
+#define _RS(list)
+#define _CASTASGN(a,b) (a)=(b)
+#endif
+
+/******************************************************************************
+ * The sort macro is an adaptation of Simon Tatham's O(n log(n)) mergesort    *
+ * Unwieldy variable names used here to avoid shadowing passed-in variables.  *
+ *****************************************************************************/
+#define LL_SORT(list, cmp)                                                                     \
+do {                                                                                           \
+  LDECLTYPE(list) _ls_p;                                                                       \
+  LDECLTYPE(list) _ls_q;                                                                       \
+  LDECLTYPE(list) _ls_e;                                                                       \
+  LDECLTYPE(list) _ls_tail;                                                                    \
+  LDECLTYPE(list) _ls_oldhead;                                                                 \
+  LDECLTYPE(list) _tmp;                                                                        \
+  int _ls_insize, _ls_nmerges, _ls_psize, _ls_qsize, _ls_i, _ls_looping;                       \
+  if (list) {                                                                                  \
+    _ls_insize = 1;                                                                            \
+    _ls_looping = 1;                                                                           \
+    while (_ls_looping) {                                                                      \
+      _CASTASGN(_ls_p,list);                                                                   \
+      _CASTASGN(_ls_oldhead,list);                                                             \
+      list = NULL;                                                                             \
+      _ls_tail = NULL;                                                                         \
+      _ls_nmerges = 0;                                                                         \
+      while (_ls_p) {                                                                          \
+        _ls_nmerges++;                                                                         \
+        _ls_q = _ls_p;                                                                         \
+        _ls_psize = 0;                                                                         \
+        for (_ls_i = 0; _ls_i < _ls_insize; _ls_i++) {                                         \
+          _ls_psize++;                                                                         \
+          _SV(_ls_q,list); _ls_q = _NEXT(_ls_q,list); _RS(list);                               \
+          if (!_ls_q) break;                                                                   \
+        }                                                                                      \
+        _ls_qsize = _ls_insize;                                                                \
+        while (_ls_psize > 0 || (_ls_qsize > 0 && _ls_q)) {                                    \
+          if (_ls_psize == 0) {                                                                \
+            _ls_e = _ls_q; _SV(_ls_q,list); _ls_q = _NEXT(_ls_q,list); _RS(list); _ls_qsize--; \
+          } else if (_ls_qsize == 0 || !_ls_q) {                                               \
+            _ls_e = _ls_p; _SV(_ls_p,list); _ls_p = _NEXT(_ls_p,list); _RS(list); _ls_psize--; \
+          } else if (cmp(_ls_p,_ls_q) <= 0) {                                                  \
+            _ls_e = _ls_p; _SV(_ls_p,list); _ls_p = _NEXT(_ls_p,list); _RS(list); _ls_psize--; \
+          } else {                                                                             \
+            _ls_e = _ls_q; _SV(_ls_q,list); _ls_q = _NEXT(_ls_q,list); _RS(list); _ls_qsize--; \
+          }                                                                                    \
+          if (_ls_tail) {                                                                      \
+            _SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,_ls_e); _RS(list);                     \
+          } else {                                                                             \
+            _CASTASGN(list,_ls_e);                                                             \
+          }                                                                                    \
+          _ls_tail = _ls_e;                                                                    \
+        }                                                                                      \
+        _ls_p = _ls_q;                                                                         \
+      }                                                                                        \
+      _SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,NULL); _RS(list);                            \
+      if (_ls_nmerges <= 1) {                                                                  \
+        _ls_looping=0;                                                                         \
+      }                                                                                        \
+      _ls_insize *= 2;                                                                         \
+    }                                                                                          \
+  } else _tmp=NULL; /* quiet gcc unused variable warning */                                    \
+} while (0)
+
+#define DL_SORT(list, cmp)                                                                     \
+do {                                                                                           \
+  LDECLTYPE(list) _ls_p;                                                                       \
+  LDECLTYPE(list) _ls_q;                                                                       \
+  LDECLTYPE(list) _ls_e;                                                                       \
+  LDECLTYPE(list) _ls_tail;                                                                    \
+  LDECLTYPE(list) _ls_oldhead;                                                                 \
+  LDECLTYPE(list) _tmp;                                                                        \
+  int _ls_insize, _ls_nmerges, _ls_psize, _ls_qsize, _ls_i, _ls_looping;                       \
+  if (list) {                                                                                  \
+    _ls_insize = 1;                                                                            \
+    _ls_looping = 1;                                                                           \
+    while (_ls_looping) {                                                                      \
+      _CASTASGN(_ls_p,list);                                                                   \
+      _CASTASGN(_ls_oldhead,list);                                                             \
+      list = NULL;                                                                             \
+      _ls_tail = NULL;                                                                         \
+      _ls_nmerges = 0;                                                                         \
+      while (_ls_p) {                                                                          \
+        _ls_nmerges++;                                                                         \
+        _ls_q = _ls_p;                                                                         \
+        _ls_psize = 0;                                                                         \
+        for (_ls_i = 0; _ls_i < _ls_insize; _ls_i++) {                                         \
+          _ls_psize++;                                                                         \
+          _SV(_ls_q,list); _ls_q = _NEXT(_ls_q,list); _RS(list);                               \
+          if (!_ls_q) break;                                                                   \
+        }                                                                                      \
+        _ls_qsize = _ls_insize;                                                                \
+        while (_ls_psize > 0 || (_ls_qsize > 0 && _ls_q)) {                                    \
+          if (_ls_psize == 0) {                                                                \
+            _ls_e = _ls_q; _SV(_ls_q,list); _ls_q = _NEXT(_ls_q,list); _RS(list); _ls_qsize--; \
+          } else if (_ls_qsize == 0 || !_ls_q) {                                               \
+            _ls_e = _ls_p; _SV(_ls_p,list); _ls_p = _NEXT(_ls_p,list); _RS(list); _ls_psize--; \
+          } else if (cmp(_ls_p,_ls_q) <= 0) {                                                  \
+            _ls_e = _ls_p; _SV(_ls_p,list); _ls_p = _NEXT(_ls_p,list); _RS(list); _ls_psize--; \
+          } else {                                                                             \
+            _ls_e = _ls_q; _SV(_ls_q,list); _ls_q = _NEXT(_ls_q,list); _RS(list); _ls_qsize--; \
+          }                                                                                    \
+          if (_ls_tail) {                                                                      \
+            _SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,_ls_e); _RS(list);                     \
+          } else {                                                                             \
+            _CASTASGN(list,_ls_e);                                                             \
+          }                                                                                    \
+          _SV(_ls_e,list); _PREVASGN(_ls_e,list,_ls_tail); _RS(list);                          \
+          _ls_tail = _ls_e;                                                                    \
+        }                                                                                      \
+        _ls_p = _ls_q;                                                                         \
+      }                                                                                        \
+      _CASTASGN(list->prev, _ls_tail);                                                         \
+      _SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,NULL); _RS(list);                            \
+      if (_ls_nmerges <= 1) {                                                                  \
+        _ls_looping=0;                                                                         \
+      }                                                                                        \
+      _ls_insize *= 2;                                                                         \
+    }                                                                                          \
+  } else _tmp=NULL; /* quiet gcc unused variable warning */                                    \
+} while (0)
+
+#define CDL_SORT(list, cmp)                                                                    \
+do {                                                                                           \
+  LDECLTYPE(list) _ls_p;                                                                       \
+  LDECLTYPE(list) _ls_q;                                                                       \
+  LDECLTYPE(list) _ls_e;                                                                       \
+  LDECLTYPE(list) _ls_tail;                                                                    \
+  LDECLTYPE(list) _ls_oldhead;                                                                 \
+  LDECLTYPE(list) _tmp;                                                                        \
+  LDECLTYPE(list) _tmp2;                                                                       \
+  int _ls_insize, _ls_nmerges, _ls_psize, _ls_qsize, _ls_i, _ls_looping;                       \
+  if (list) {                                                                                  \
+    _ls_insize = 1;                                                                            \
+    _ls_looping = 1;                                                                           \
+    while (_ls_looping) {                                                                      \
+      _CASTASGN(_ls_p,list);                                                                   \
+      _CASTASGN(_ls_oldhead,list);                                                             \
+      list = NULL;                                                                             \
+      _ls_tail = NULL;                                                                         \
+      _ls_nmerges = 0;                                                                         \
+      while (_ls_p) {                                                                          \
+        _ls_nmerges++;                                                                         \
+        _ls_q = _ls_p;                                                                         \
+        _ls_psize = 0;                                                                         \
+        for (_ls_i = 0; _ls_i < _ls_insize; _ls_i++) {                                         \
+          _ls_psize++;                                                                         \
+          _SV(_ls_q,list);                                                                     \
+          if (_NEXT(_ls_q,list) == _ls_oldhead) {                                              \
+            _ls_q = NULL;                                                                      \
+          } else {                                                                             \
+            _ls_q = _NEXT(_ls_q,list);                                                         \
+          }                                                                                    \
+          _RS(list);                                                                           \
+          if (!_ls_q) break;                                                                   \
+        }                                                                                      \
+        _ls_qsize = _ls_insize;                                                                \
+        while (_ls_psize > 0 || (_ls_qsize > 0 && _ls_q)) {                                    \
+          if (_ls_psize == 0) {                                                                \
+            _ls_e = _ls_q; _SV(_ls_q,list); _ls_q = _NEXT(_ls_q,list); _RS(list); _ls_qsize--; \
+            if (_ls_q == _ls_oldhead) { _ls_q = NULL; }                                        \
+          } else if (_ls_qsize == 0 || !_ls_q) {                                               \
+            _ls_e = _ls_p; _SV(_ls_p,list); _ls_p = _NEXT(_ls_p,list); _RS(list); _ls_psize--; \
+            if (_ls_p == _ls_oldhead) { _ls_p = NULL; }                                        \
+          } else if (cmp(_ls_p,_ls_q) <= 0) {                                                  \
+            _ls_e = _ls_p; _SV(_ls_p,list); _ls_p = _NEXT(_ls_p,list); _RS(list); _ls_psize--; \
+            if (_ls_p == _ls_oldhead) { _ls_p = NULL; }                                        \
+          } else {                                                                             \
+            _ls_e = _ls_q; _SV(_ls_q,list); _ls_q = _NEXT(_ls_q,list); _RS(list); _ls_qsize--; \
+            if (_ls_q == _ls_oldhead) { _ls_q = NULL; }                                        \
+          }                                                                                    \
+          if (_ls_tail) {                                                                      \
+            _SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,_ls_e); _RS(list);                     \
+          } else {                                                                             \
+            _CASTASGN(list,_ls_e);                                                             \
+          }                                                                                    \
+          _SV(_ls_e,list); _PREVASGN(_ls_e,list,_ls_tail); _RS(list);                          \
+          _ls_tail = _ls_e;                                                                    \
+        }                                                                                      \
+        _ls_p = _ls_q;                                                                         \
+      }                                                                                        \
+      _CASTASGN(list->prev,_ls_tail);                                                          \
+      _CASTASGN(_tmp2,list);                                                                   \
+      _SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,_tmp2); _RS(list);                           \
+      if (_ls_nmerges <= 1) {                                                                  \
+        _ls_looping=0;                                                                         \
+      }                                                                                        \
+      _ls_insize *= 2;                                                                         \
+    }                                                                                          \
+  } else _tmp=NULL; /* quiet gcc unused variable warning */                                    \
+} while (0)
+
+/******************************************************************************
+ * singly linked list macros (non-circular)                                   *
+ *****************************************************************************/
+#define LL_PREPEND(head,add)                                                                   \
+do {                                                                                           \
+  (add)->next = head;                                                                          \
+  head = add;                                                                                  \
+} while (0)
+
+#define LL_APPEND(head,add)                                                                    \
+do {                                                                                           \
+  LDECLTYPE(head) _tmp;                                                                        \
+  (add)->next=NULL;                                                                            \
+  if (head) {                                                                                  \
+    _tmp = head;                                                                               \
+    while (_tmp->next) { _tmp = _tmp->next; }                                                  \
+    _tmp->next=(add);                                                                          \
+  } else {                                                                                     \
+    (head)=(add);                                                                              \
+  }                                                                                            \
+} while (0)
+
+#define LL_DELETE(head,del)                                                                    \
+do {                                                                                           \
+  LDECLTYPE(head) _tmp;                                                                        \
+  if ((head) == (del)) {                                                                       \
+    (head)=(head)->next;                                                                       \
+  } else {                                                                                     \
+    _tmp = head;                                                                               \
+    while (_tmp->next && (_tmp->next != (del))) {                                              \
+      _tmp = _tmp->next;                                                                       \
+    }                                                                                          \
+    if (_tmp->next) {                                                                          \
+      _tmp->next = ((del)->next);                                                              \
+    }                                                                                          \
+  }                                                                                            \
+} while (0)
+
+/* Here are VS2008 replacements for LL_APPEND and LL_DELETE */
+#define LL_APPEND_VS2008(head,add)                                                             \
+do {                                                                                           \
+  if (head) {                                                                                  \
+    (add)->next = head;     /* use add->next as a temp variable */                             \
+    while ((add)->next->next) { (add)->next = (add)->next->next; }                             \
+    (add)->next->next=(add);                                                                   \
+  } else {                                                                                     \
+    (head)=(add);                                                                              \
+  }                                                                                            \
+  (add)->next=NULL;                                                                            \
+} while (0)
+
+#define LL_DELETE_VS2008(head,del)                                                             \
+do {                                                                                           \
+  if ((head) == (del)) {                                                                       \
+    (head)=(head)->next;                                                                       \
+  } else {                                                                                     \
+    char *_tmp = (char*)(head);                                                                \
+    while (head->next && (head->next != (del))) {                                              \
+      head = head->next;                                                                       \
+    }                                                                                          \
+    if (head->next) {                                                                          \
+      head->next = ((del)->next);                                                              \
+    }                                                                                          \
+    {                                                                                          \
+      char **_head_alias = (char**)&(head);                                                    \
+      *_head_alias = _tmp;                                                                     \
+    }                                                                                          \
+  }                                                                                            \
+} while (0)
+#ifdef NO_DECLTYPE
+#undef LL_APPEND
+#define LL_APPEND LL_APPEND_VS2008
+#undef LL_DELETE
+#define LL_DELETE LL_DELETE_VS2008
+#endif
+/* end VS2008 replacements */
+
+#define LL_FOREACH(head,el)                                                                    \
+    for(el=head;el;el=el->next)
+
+#define LL_FOREACH_SAFE(head,el,tmp)                                                           \
+  for((el)=(head);(el) && (tmp = (el)->next, 1); (el) = tmp)
+
+#define LL_SEARCH_SCALAR(head,out,field,val)                                                   \
+do {                                                                                           \
+    LL_FOREACH(head,out) {                                                                     \
+      if ((out)->field == (val)) break;                                                        \
+    }                                                                                          \
+} while(0) 
+
+#define LL_SEARCH(head,out,elt,cmp)                                                            \
+do {                                                                                           \
+    LL_FOREACH(head,out) {                                                                     \
+      if ((cmp(out,elt))==0) break;                                                            \
+    }                                                                                          \
+} while(0) 
+
+/******************************************************************************
+ * doubly linked list macros (non-circular)                                   *
+ *****************************************************************************/
+#define DL_PREPEND(head,add)                                                                   \
+do {                                                                                           \
+ (add)->next = head;                                                                           \
+ if (head) {                                                                                   \
+   (add)->prev = (head)->prev;                                                                 \
+   (head)->prev = (add);                                                                       \
+ } else {                                                                                      \
+   (add)->prev = (add);                                                                        \
+ }                                                                                             \
+ (head) = (add);                                                                               \
+} while (0)
+
+#define DL_APPEND(head,add)                                                                    \
+do {                                                                                           \
+  if (head) {                                                                                  \
+      (add)->prev = (head)->prev;                                                              \
+      (head)->prev->next = (add);                                                              \
+      (head)->prev = (add);                                                                    \
+      (add)->next = NULL;                                                                      \
+  } else {                                                                                     \
+      (head)=(add);                                                                            \
+      (head)->prev = (head);                                                                   \
+      (head)->next = NULL;                                                                     \
+  }                                                                                            \
+} while (0);
+
+#define DL_DELETE(head,del)                                                                    \
+do {                                                                                           \
+  if ((del)->prev == (del)) {                                                                  \
+      (head)=NULL;                                                                             \
+  } else if ((del)==(head)) {                                                                  \
+      (del)->next->prev = (del)->prev;                                                         \
+      (head) = (del)->next;                                                                    \
+  } else {                                                                                     \
+      (del)->prev->next = (del)->next;                                                         \
+      if ((del)->next) {                                                                       \
+          (del)->next->prev = (del)->prev;                                                     \
+      } else {                                                                                 \
+          (head)->prev = (del)->prev;                                                          \
+      }                                                                                        \
+  }                                                                                            \
+} while (0);
+
+
+#define DL_FOREACH(head,el)                                                                    \
+    for(el=head;el;el=el->next)
+
+/* this version is safe for deleting the elements during iteration */
+#define DL_FOREACH_SAFE(head,el,tmp)                                                           \
+  for((el)=(head);(el) && (tmp = (el)->next, 1); (el) = tmp)
+
+/* these are identical to their singly-linked list counterparts */
+#define DL_SEARCH_SCALAR LL_SEARCH_SCALAR
+#define DL_SEARCH LL_SEARCH
+
+/******************************************************************************
+ * circular doubly linked list macros                                         *
+ *****************************************************************************/
+#define CDL_PREPEND(head,add)                                                                  \
+do {                                                                                           \
+ if (head) {                                                                                   \
+   (add)->prev = (head)->prev;                                                                 \
+   (add)->next = (head);                                                                       \
+   (head)->prev = (add);                                                                       \
+   (add)->prev->next = (add);                                                                  \
+ } else {                                                                                      \
+   (add)->prev = (add);                                                                        \
+   (add)->next = (add);                                                                        \
+ }                                                                                             \
+(head)=(add);                                                                                  \
+} while (0)
+
+#define CDL_DELETE(head,del)                                                                   \
+do {                                                                                           \
+  if ( ((head)==(del)) && ((head)->next == (head))) {                                          \
+      (head) = 0L;                                                                             \
+  } else {                                                                                     \
+     (del)->next->prev = (del)->prev;                                                          \
+     (del)->prev->next = (del)->next;                                                          \
+     if ((del) == (head)) (head)=(del)->next;                                                  \
+  }                                                                                            \
+} while (0);
+
+#define CDL_FOREACH(head,el)                                                                   \
+    for(el=head;el;el=(el->next==head ? 0L : el->next)) 
+
+#define CDL_FOREACH_SAFE(head,el,tmp1,tmp2)                                                    \
+  for((el)=(head), ((tmp1)=(head)?((head)->prev):NULL);                                        \
+      (el) && ((tmp2)=(el)->next, 1);                                                          \
+      ((el) = (((el)==(tmp1)) ? 0L : (tmp2))))
+
+#define CDL_SEARCH_SCALAR(head,out,field,val)                                                  \
+do {                                                                                           \
+    CDL_FOREACH(head,out) {                                                                    \
+      if ((out)->field == (val)) break;                                                        \
+    }                                                                                          \
+} while(0) 
+
+#define CDL_SEARCH(head,out,elt,cmp)                                                           \
+do {                                                                                           \
+    CDL_FOREACH(head,out) {                                                                    \
+      if ((cmp(out,elt))==0) break;                                                            \
+    }                                                                                          \
+} while(0) 
+
+#endif /* UTLIST_H */
+
diff --git a/libs/cocos2d/ccConfig.h b/libs/cocos2d/ccConfig.h
new file mode 100644 (file)
index 0000000..145f1d4
--- /dev/null
@@ -0,0 +1,282 @@
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2008-2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+
+#import <Availability.h>
+
+/**
+ @file
+ cocos2d (cc) configuration file
+*/
+
+/** @def CC_FIX_ARTIFACTS_BY_STRECHING_TEXEL
+ If enabled, the texture coordinates will be calculated by using this formula:
+   - texCoord.left = (rect.origin.x*2+1) / (texture.wide*2);
+   - texCoord.right = texCoord.left + (rect.size.width*2-2)/(texture.wide*2);
+ The same for bottom and top.
+ This formula prevents artifacts by using 99% of the texture.
+ The "correct" way to prevent artifacts is by using the spritesheet-artifact-fixer.py or a similar tool.
+ Affected nodes:
+       - CCSprite / CCSpriteBatchNode and subclasses: CCBitmapFontAtlas, CCTMXTiledMap
+       - CCLabelAtlas
+       - CCQuadParticleSystem
+       - CCTileMap
+ To enabled set it to 1. Disabled by default.
+ @since v0.99.5
+ */
+#define CC_FIX_ARTIFACTS_BY_STRECHING_TEXEL 0
+
+/** @def CC_FONT_LABEL_SUPPORT
+ If enabled, FontLabel will be used to render .ttf files.
+ If the .ttf file is not found, then it will use the standard UIFont class
+ If disabled, the standard UIFont class will be used.
+ To disable set it to 0. Enabled by default.
+
+ Only valid for cocos2d-ios. Not supported on cocos2d-mac
+ */
+#define CC_FONT_LABEL_SUPPORT  1
+
+/** @def CC_DIRECTOR_FAST_FPS
+ If enabled, then the FPS will be drawn using CCLabelAtlas (fast rendering).
+ You will need to add the fps_images.png to your project.
+ If disabled, the FPS will be rendered using CCLabel (slow rendering)
+ To enable set it to a value different than 0. Enabled by default.
+ */
+#define CC_DIRECTOR_FAST_FPS   1
+
+/** @def CC_DIRECTOR_FPS_INTERVAL
+ Senconds between FPS updates.
+ 0.5 seconds, means that the FPS number will be updated every 0.5 seconds.
+ Having a bigger number means a more reliable FPS
+ Default value: 0.1f
+ */
+#define CC_DIRECTOR_FPS_INTERVAL (0.1f)
+
+/** @def CC_DIRECTOR_DISPATCH_FAST_EVENTS
+ If enabled, and only when it is used with CCFastDirector, the main loop will wait 0.04 seconds to
+ dispatch all the events, even if there are not events to dispatch.
+ If your game uses lot's of events (eg: touches) it might be a good idea to enable this feature.
+ Otherwise, it is safe to leave it disabled.
+ To enable set it to 1. Disabled by default.
+ @warning This feature is experimental
+ */
+#define CC_DIRECTOR_DISPATCH_FAST_EVENTS 0
+
+/** @def CC_DIRECTOR_MAC_USE_DISPLAY_LINK_THREAD
+ If enabled, cocos2d-mac will run on the Display Link thread. If disabled cocos2d-mac will run in its own thread.
+ If enabled, the images will be drawn at the "correct" time, but the events might not be very responsive.
+ If disabled, some frames might be skipped, but the events will be dispatched as they arrived.
+ To enable set it to a 1, to disable it set to 0. Enabled by default.
+
+ Only valid for cocos2d-mac. Not supported on cocos2d-ios.
+
+ */
+#define CC_DIRECTOR_MAC_USE_DISPLAY_LINK_THREAD 1
+
+/** @def CC_COCOSNODE_RENDER_SUBPIXEL
+ If enabled, the CCNode objects (CCSprite, CCLabel,etc) will be able to render in subpixels.
+ If disabled, integer pixels will be used.
+ To enable set it to 1. Enabled by default.
+ */
+#define CC_COCOSNODE_RENDER_SUBPIXEL 1
+
+/** @def CC_SPRITEBATCHNODE_RENDER_SUBPIXEL
+ If enabled, the CCSprite objects rendered with CCSpriteBatchNode will be able to render in subpixels.
+ If disabled, integer pixels will be used.
+ To enable set it to 1. Enabled by default.
+ */
+#define CC_SPRITEBATCHNODE_RENDER_SUBPIXEL     1
+
+
+#if defined(__ARM_NEON__) || TARGET_IPHONE_SIMULATOR || defined(__MAC_OS_X_VERSION_MAX_ALLOWED)
+/** @def CC_USES_VBO
+ If enabled, batch nodes (texture atlas and particle system) will use VBO instead of vertex list (VBO is recommended by Apple)
+ To enable set it to 1.
+ Enabled by default on iPhone with ARMv7 processors, iPhone Simulator and Mac
+ Disabled by default on iPhone with ARMv6 processors.
+ @since v0.99.5
+ */
+#define CC_USES_VBO 1
+#else
+#define CC_USES_VBO 0
+#endif
+
+/** @def CC_NODE_TRANSFORM_USING_AFFINE_MATRIX
+ If enabled, CCNode will transform the nodes using a cached Affine matrix.
+ If disabled, the node will be transformed using glTranslate,glRotate,glScale.
+ Using the affine matrix only requires 2 GL calls.
+ Using the translate/rotate/scale requires 5 GL calls.
+ But computing the Affine matrix is relative expensive.
+ But according to performance tests, Affine matrix performs better.
+ This parameter doesn't affect SpriteSheet nodes.
+ To enable set it to a value different than 0. Enabled by default.
+
+ */
+#define CC_NODE_TRANSFORM_USING_AFFINE_MATRIX 1
+
+/** @def CC_OPTIMIZE_BLEND_FUNC_FOR_PREMULTIPLIED_ALPHA
+ If most of your imamges have pre-multiplied alpha, set it to 1 (if you are going to use .PNG/.JPG file images).
+ Only set to 0 if ALL your images by-pass Apple UIImage loading system (eg: if you use libpng or PVR images)
+
+ To enable set it to a value different than 0. Enabled by default.
+
+ @since v0.99.5
+ */
+#define CC_OPTIMIZE_BLEND_FUNC_FOR_PREMULTIPLIED_ALPHA 1
+
+/** @def CC_TEXTURE_ATLAS_USE_TRIANGLE_STRIP
+ Use GL_TRIANGLE_STRIP instead of GL_TRIANGLES when rendering the texture atlas.
+ It seems it is the recommend way, but it is much slower, so, enable it at your own risk
+ To enable set it to a value different than 0. Disabled by default.
+
+ */
+#define CC_TEXTURE_ATLAS_USE_TRIANGLE_STRIP 0
+
+
+/** @def CC_TEXTURE_NPOT_SUPPORT
+ If enabled, NPOT textures will be used where available. Only 3rd gen (and newer) devices support NPOT textures.
+ NPOT textures have the following limitations:
+       - They can't have mipmaps
+       - They only accept GL_CLAMP_TO_EDGE in GL_TEXTURE_WRAP_{S,T}
+ To enable set it to a value different than 0. Disabled by default.
+
+ @since v0.99.2
+ */
+#define CC_TEXTURE_NPOT_SUPPORT 0
+
+/** @def CC_RETINA_DISPLAY_SUPPORT
+ If enabled, cocos2d supports retina display. 
+ For performance reasons, it's recommended disable it in games without retina display support, like iPad only games.
+ To enable set it to 1. Use 0 to disable it. Enabled by default.
+ @since v0.99.5
+ */
+#define CC_RETINA_DISPLAY_SUPPORT 1
+
+/** @def CC_RETINA_DISPLAY_FILENAME_SUFFIX
+ It's the suffix that will be appended to the files in order to load "retina display" images.
+
+ On an iPhone4 with Retina Display support enabled, the file @"sprite-hd.png" will be loaded instead of @"sprite.png".
+ If the file doesn't exist it will use the non-retina display image.
+ Platforms: Only used on Retina Display devices like iPhone 4.
+ @since v0.99.5
+ */ 
+#define CC_RETINA_DISPLAY_FILENAME_SUFFIX @"-hd"
+
+/** @def CC_USE_LA88_LABELS_ON_NEON_ARCH
+ If enabled, it will use LA88 (16-bit textures) on Neon devices for CCLabelTTF objects.
+ If it is disabled, or if it is used on another architecture it will use A8 (8-bit textures).
+ On Neon devices, LA88 textures are 6% faster than A8 textures, but then will consume 2x memory.
+ This feature is disabled by default.
+ Platforms: Only used on ARM Neon architectures like iPhone 3GS or newer and iPad.
+
+ @since v0.99.5
+ */
+#define CC_USE_LA88_LABELS_ON_NEON_ARCH 0
+
+/** @def CC_SPRITE_DEBUG_DRAW
+ If enabled, all subclasses of CCSprite will draw a bounding box
+ Useful for debugging purposes only. It is recommened to leave it disabled.
+ To enable set it to a value different than 0. Disabled by default.
+ */
+#define CC_SPRITE_DEBUG_DRAW 0
+
+/** @def CC_SPRITEBATCHNODE_DEBUG_DRAW
+ If enabled, all subclasses of CCSprite that are rendered using an CCSpriteBatchNode draw a bounding box.
+ Useful for debugging purposes only. It is recommened to leave it disabled.
+ To enable set it to a value different than 0. Disabled by default.
+ */
+#define CC_SPRITEBATCHNODE_DEBUG_DRAW 0
+
+/** @def CC_LABELBMFONT_DEBUG_DRAW
+ If enabled, all subclasses of CCLabelBMFont will draw a bounding box
+ Useful for debugging purposes only. It is recommened to leave it disabled.
+ To enable set it to a value different than 0. Disabled by default.
+ */
+#define CC_LABELBMFONT_DEBUG_DRAW 0
+
+/** @def CC_LABELBMFONT_DEBUG_DRAW
+ If enabled, all subclasses of CCLabeltAtlas will draw a bounding box
+ Useful for debugging purposes only. It is recommened to leave it disabled.
+ To enable set it to a value different than 0. Disabled by default.
+ */
+#define CC_LABELATLAS_DEBUG_DRAW 0
+
+/** @def CC_ENABLE_PROFILERS
+ If enabled, will activate various profilers withing cocos2d. This statistical data will be output to the console
+ once per second showing average time (in milliseconds) required to execute the specific routine(s).
+ Useful for debugging purposes only. It is recommened to leave it disabled.
+ To enable set it to a value different than 0. Disabled by default.
+ */
+#define CC_ENABLE_PROFILERS 0
+
+//
+// DON'T edit this macro.
+//
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+
+#if CC_RETINA_DISPLAY_SUPPORT
+#define CC_IS_RETINA_DISPLAY_SUPPORTED 1
+#else
+#define CC_IS_RETINA_DISPLAY_SUPPORTED 0
+#endif
+
+#elif __MAC_OS_X_VERSION_MAX_ALLOWED
+
+#define CC_IS_RETINA_DISPLAY_SUPPORTED 0
+
+#endif
+
+
diff --git a/libs/cocos2d/ccMacros.h b/libs/cocos2d/ccMacros.h
new file mode 100644 (file)
index 0000000..4e08725
--- /dev/null
@@ -0,0 +1,253 @@
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2008-2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+
+#import <math.h>
+#import "ccConfig.h"
+
+#import <Foundation/Foundation.h>
+#import <Availability.h>
+
+/**
+ @file
+ cocos2d helper macros
+ */
+
+/*
+ * if COCOS2D_DEBUG is not defined, or if it is 0 then
+ *     all CCLOGXXX macros will be disabled
+ *
+ * if COCOS2D_DEBUG==1 then:
+ *             CCLOG() will be enabled
+ *             CCLOGERROR() will be enabled
+ *             CCLOGINFO()     will be disabled
+ *
+ * if COCOS2D_DEBUG==2 or higher then:
+ *             CCLOG() will be enabled
+ *             CCLOGERROR() will be enabled
+ *             CCLOGINFO()     will be enabled 
+ */
+#if !defined(COCOS2D_DEBUG) || COCOS2D_DEBUG == 0
+#define CCLOG(...) do {} while (0)
+#define CCLOGINFO(...) do {} while (0)
+#define CCLOGERROR(...) do {} while (0)
+
+#elif COCOS2D_DEBUG == 1
+#define CCLOG(...) NSLog(__VA_ARGS__)
+#define CCLOGERROR(...) NSLog(__VA_ARGS__)
+#define CCLOGINFO(...) do {} while (0)
+
+#elif COCOS2D_DEBUG > 1
+#define CCLOG(...) NSLog(__VA_ARGS__)
+#define CCLOGERROR(...) NSLog(__VA_ARGS__)
+#define CCLOGINFO(...) NSLog(__VA_ARGS__)
+#endif // COCOS2D_DEBUG
+
+/** @def CC_SWAP
+simple macro that swaps 2 variables
+*/
+#define CC_SWAP( x, y )                        \
+({ __typeof__(x) temp  = (x);          \
+               x = y; y = temp;                \
+})
+
+
+/** @def CCRANDOM_MINUS1_1
+ returns a random float between -1 and 1
+ */
+#define CCRANDOM_MINUS1_1() ((random() / (float)0x3fffffff )-1.0f)
+
+/** @def CCRANDOM_0_1
+ returns a random float between 0 and 1
+ */
+#define CCRANDOM_0_1() ((random() / (float)0x7fffffff ))
+
+/** @def CC_DEGREES_TO_RADIANS
+ converts degrees to radians
+ */
+#define CC_DEGREES_TO_RADIANS(__ANGLE__) ((__ANGLE__) * 0.01745329252f) // PI / 180
+
+/** @def CC_RADIANS_TO_DEGREES
+ converts radians to degrees
+ */
+#define CC_RADIANS_TO_DEGREES(__ANGLE__) ((__ANGLE__) * 57.29577951f) // PI * 180
+
+/** @def CC_BLEND_SRC
+default gl blend src function. Compatible with premultiplied alpha images.
+*/
+#if CC_OPTIMIZE_BLEND_FUNC_FOR_PREMULTIPLIED_ALPHA
+#define CC_BLEND_SRC GL_ONE
+#define CC_BLEND_DST GL_ONE_MINUS_SRC_ALPHA
+#else
+#define CC_BLEND_SRC GL_SRC_ALPHA
+#define CC_BLEND_DST GL_ONE_MINUS_SRC_ALPHA
+#endif // ! CC_OPTIMIZE_BLEND_FUNC_FOR_PREMULTIPLIED_ALPHA
+
+/** @def CC_ENABLE_DEFAULT_GL_STATES
+ GL states that are enabled:
+       - GL_TEXTURE_2D
+       - GL_VERTEX_ARRAY
+       - GL_TEXTURE_COORD_ARRAY
+       - GL_COLOR_ARRAY
+ */
+#define CC_ENABLE_DEFAULT_GL_STATES() {                                \
+       glEnableClientState(GL_VERTEX_ARRAY);                   \
+       glEnableClientState(GL_COLOR_ARRAY);                    \
+       glEnableClientState(GL_TEXTURE_COORD_ARRAY);    \
+       glEnable(GL_TEXTURE_2D);                                                \
+}
+
+/** @def CC_DISABLE_DEFAULT_GL_STATES 
+ Disable default GL states:
+       - GL_TEXTURE_2D
+       - GL_VERTEX_ARRAY
+       - GL_TEXTURE_COORD_ARRAY
+       - GL_COLOR_ARRAY
+ */
+#define CC_DISABLE_DEFAULT_GL_STATES() {                       \
+       glDisable(GL_TEXTURE_2D);                                               \
+       glDisableClientState(GL_TEXTURE_COORD_ARRAY);   \
+       glDisableClientState(GL_COLOR_ARRAY);                   \
+       glDisableClientState(GL_VERTEX_ARRAY);                  \
+}
+
+/** @def CC_DIRECTOR_INIT
+       - Initializes an EAGLView with 0-bit depth format, and RGB565 render buffer.
+       - The EAGLView view will have multiple touches disabled.
+       - It will create a UIWindow and it will assign it the 'window' variable. 'window' must be declared before calling this marcro.
+       - It will parent the EAGLView to the created window
+       - If the firmware >= 3.1 it will create a Display Link Director. Else it will create an NSTimer director.
+       - It will try to run at 60 FPS.
+       - The FPS won't be displayed.
+       - The orientation will be portrait.
+       - It will connect the director with the EAGLView.
+
+ IMPORTANT: If you want to use another type of render buffer (eg: RGBA8)
+ or if you want to use a 16-bit or 24-bit depth buffer, you should NOT
+ use this macro. Instead, you should create the EAGLView manually.
+ @since v0.99.4
+ */
+
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+
+#define CC_DIRECTOR_INIT()                                                                                                                                             \
+do     {                                                                                                                                                                                       \
+       window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];                                       \
+       if( ! [CCDirector setDirectorType:kCCDirectorTypeDisplayLink] )                                                         \
+               [CCDirector setDirectorType:kCCDirectorTypeNSTimer];                                                                    \
+       CCDirector *__director = [CCDirector sharedDirector];                                                                           \
+       [__director setDeviceOrientation:kCCDeviceOrientationPortrait];                                                         \
+       [__director setDisplayFPS:NO];                                                                                                                          \
+       [__director setAnimationInterval:1.0/60];                                                                                                       \
+       EAGLView *__glView = [EAGLView viewWithFrame:[window bounds]                                                            \
+                                                                       pixelFormat:kEAGLColorFormatRGB565                                                      \
+                                                                       depthFormat:0 /* GL_DEPTH_COMPONENT24_OES */                            \
+                                                        preserveBackbuffer:NO                                                                                          \
+                                                                        sharegroup:nil                                                                                         \
+                                                                 multiSampling:NO                                                                                              \
+                                                               numberOfSamples:0                                                                                               \
+                                                                                                       ];                                                                                      \
+       [__director setOpenGLView:__glView];                                                                                                            \
+       [window addSubview:__glView];                                                                                                                           \
+       [window makeKeyAndVisible];                                                                                                                                     \
+} while(0)
+
+
+#elif __MAC_OS_X_VERSION_MAX_ALLOWED
+
+#import "Platforms/Mac/MacWindow.h"
+
+#define CC_DIRECTOR_INIT(__WINSIZE__)                                                                                                                  \
+do     {                                                                                                                                                                                       \
+       NSRect frameRect = NSMakeRect(0, 0, (__WINSIZE__).width, (__WINSIZE__).height);                         \
+       self.window = [[MacWindow alloc] initWithFrame:frameRect fullscreen:NO];                                        \
+       self.glView = [[MacGLView alloc] initWithFrame:frameRect shareContext:nil];                                     \
+       [self.window setContentView:self.glView];                                                                                                       \
+       CCDirector *__director = [CCDirector sharedDirector];                                                                           \
+       [__director setDisplayFPS:NO];                                                                                                                          \
+       [__director setOpenGLView:self.glView];                                                                                                         \
+       [(CCDirectorMac*)__director setOriginalWinSize:__WINSIZE__];                                                            \
+       [self.window makeMainWindow];                                                                                                                           \
+       [self.window makeKeyAndOrderFront:self];                                                                                                        \
+} while(0)
+
+#endif
+
+ /** @def CC_DIRECTOR_END
+  Stops and removes the director from memory.
+  Removes the EAGLView from its parent
+  
+  @since v0.99.4
+  */
+#define CC_DIRECTOR_END()                                                                              \
+do {                                                                                                                   \
+       CCDirector *__director = [CCDirector sharedDirector];           \
+       CC_GLVIEW *__view = [__director openGLView];                            \
+       [__view removeFromSuperview];                                                           \
+       [__director end];                                                                                       \
+} while(0)
+
+
+#if CC_IS_RETINA_DISPLAY_SUPPORTED
+
+/****************************/
+/** RETINA DISPLAY ENABLED **/
+/****************************/
+
+/** @def CC_CONTENT_SCALE_FACTOR
+ On Mac it returns 1;
+ On iPhone it returns 2 if RetinaDisplay is On. Otherwise it returns 1
+ */
+#import "Platforms/iOS/CCDirectorIOS.h"
+#define CC_CONTENT_SCALE_FACTOR() __ccContentScaleFactor
+
+
+/** @def CC_RECT_PIXELS_TO_POINTS
+ Converts a rect in pixels to points
+ */
+#define CC_RECT_PIXELS_TO_POINTS(__pixels__)                                                                                                                                           \
+       CGRectMake( (__pixels__).origin.x / CC_CONTENT_SCALE_FACTOR(), (__pixels__).origin.y / CC_CONTENT_SCALE_FACTOR(),       \
+                       (__pixels__).size.width / CC_CONTENT_SCALE_FACTOR(), (__pixels__).size.height / CC_CONTENT_SCALE_FACTOR() )
+
+/** @def CC_RECT_POINTS_TO_PIXELS
+ Converts a rect in points to pixels
+ */
+#define CC_RECT_POINTS_TO_PIXELS(__points__)                                                                                                                                           \
+       CGRectMake( (__points__).origin.x * CC_CONTENT_SCALE_FACTOR(), (__points__).origin.y * CC_CONTENT_SCALE_FACTOR(),       \
+                       (__points__).size.width * CC_CONTENT_SCALE_FACTOR(), (__points__).size.height * CC_CONTENT_SCALE_FACTOR() )
+
+#else // retina disabled
+
+/*****************************/
+/** RETINA DISPLAY DISABLED **/
+/*****************************/
+
+#define CC_CONTENT_SCALE_FACTOR() 1
+#define CC_RECT_PIXELS_TO_POINTS(__pixels__) __pixels__
+#define CC_RECT_POINTS_TO_PIXELS(__points__) __points__
+
+#endif // CC_IS_RETINA_DISPLAY_SUPPORTED
diff --git a/libs/cocos2d/ccTypes.h b/libs/cocos2d/ccTypes.h
new file mode 100644 (file)
index 0000000..46917b3
--- /dev/null
@@ -0,0 +1,287 @@
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2008-2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+
+/**
+ @file
+ cocos2d (cc) types
+*/
+
+#import <Availability.h>
+#import <Foundation/Foundation.h>
+
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+#import <CoreGraphics/CGGeometry.h>    // CGPoint
+#endif
+
+#import "Platforms/CCGL.h"
+
+/** RGB color composed of bytes 3 bytes
+@since v0.8
+ */
+typedef struct _ccColor3B
+{
+       GLubyte r;
+       GLubyte g;
+       GLubyte b;
+} ccColor3B;
+
+//! helper macro that creates an ccColor3B type
+static inline ccColor3B
+ccc3(const GLubyte r, const GLubyte g, const GLubyte b)
+{
+       ccColor3B c = {r, g, b};
+       return c;
+}
+//ccColor3B predefined colors
+//! White color (255,255,255)
+static const ccColor3B ccWHITE = {255,255,255};
+//! Yellow color (255,255,0)
+static const ccColor3B ccYELLOW = {255,255,0};
+//! Blue color (0,0,255)
+static const ccColor3B ccBLUE = {0,0,255};
+//! Green Color (0,255,0)
+static const ccColor3B ccGREEN = {0,255,0};
+//! Red Color (255,0,0,)
+static const ccColor3B ccRED = {255,0,0};
+//! Magenta Color (255,0,255)
+static const ccColor3B ccMAGENTA = {255,0,255};
+//! Black Color (0,0,0)
+static const ccColor3B ccBLACK = {0,0,0};
+//! Orange Color (255,127,0)
+static const ccColor3B ccORANGE = {255,127,0};
+//! Gray Color (166,166,166)
+static const ccColor3B ccGRAY = {166,166,166};
+
+/** RGBA color composed of 4 bytes
+@since v0.8
+*/
+typedef struct _ccColor4B
+{
+       GLubyte r;
+       GLubyte g;
+       GLubyte b;
+       GLubyte a;
+} ccColor4B;
+//! helper macro that creates an ccColor4B type
+static inline ccColor4B
+ccc4(const GLubyte r, const GLubyte g, const GLubyte b, const GLubyte o)
+{
+       ccColor4B c = {r, g, b, o};
+       return c;
+}
+
+
+/** RGBA color composed of 4 floats
+@since v0.8
+*/
+typedef struct _ccColor4F {
+       GLfloat r;
+       GLfloat g;
+       GLfloat b;
+       GLfloat a;
+} ccColor4F;
+
+/** Returns a ccColor4F from a ccColor3B. Alpha will be 1.
+ @since v0.99.1
+ */
+static inline ccColor4F ccc4FFromccc3B(ccColor3B c)
+{
+       return (ccColor4F){c.r/255.f, c.g/255.f, c.b/255.f, 1.f};
+}
+
+/** Returns a ccColor4F from a ccColor4B.
+ @since v0.99.1
+ */
+static inline ccColor4F ccc4FFromccc4B(ccColor4B c)
+{
+       return (ccColor4F){c.r/255.f, c.g/255.f, c.b/255.f, c.a/255.f};
+}
+
+/** returns YES if both ccColor4F are equal. Otherwise it returns NO.
+ @since v0.99.1
+ */
+static inline BOOL ccc4FEqual(ccColor4F a, ccColor4F b)
+{
+       return a.r == b.r && a.g == b.g && a.b == b.b && a.a == b.a;
+}
+
+/** A vertex composed of 2 GLfloats: x, y
+ @since v0.8
+ */
+typedef struct _ccVertex2F
+{
+       GLfloat x;
+       GLfloat y;
+} ccVertex2F;
+
+/** A vertex composed of 2 floats: x, y
+ @since v0.8
+ */
+typedef struct _ccVertex3F
+{
+       GLfloat x;
+       GLfloat y;
+       GLfloat z;
+} ccVertex3F;
+               
+/** A texcoord composed of 2 floats: u, y
+ @since v0.8
+ */
+typedef struct _ccTex2F {
+        GLfloat u;
+        GLfloat v;
+} ccTex2F;
+
+//! Point Sprite component
+typedef struct _ccPointSprite
+{
+       ccVertex2F      pos;            // 8 bytes
+       ccColor4B       color;          // 4 bytes
+       GLfloat         size;           // 4 bytes
+} ccPointSprite;
+
+//!    A 2D Quad. 4 * 2 floats
+typedef struct _ccQuad2 {
+       ccVertex2F              tl;
+       ccVertex2F              tr;
+       ccVertex2F              bl;
+       ccVertex2F              br;
+} ccQuad2;
+
+
+//!    A 3D Quad. 4 * 3 floats
+typedef struct _ccQuad3 {
+       ccVertex3F              bl;
+       ccVertex3F              br;
+       ccVertex3F              tl;
+       ccVertex3F              tr;
+} ccQuad3;
+
+//! A 2D grid size
+typedef struct _ccGridSize
+{
+       NSInteger       x;
+       NSInteger       y;
+} ccGridSize;
+
+//! helper function to create a ccGridSize
+static inline ccGridSize
+ccg(const NSInteger x, const NSInteger y)
+{
+       ccGridSize v = {x, y};
+       return v;
+}
+
+//! a Point with a vertex point, a tex coord point and a color 4B
+typedef struct _ccV2F_C4B_T2F
+{
+       //! vertices (2F)
+       ccVertex2F              vertices;
+       //! colors (4B)
+       ccColor4B               colors;
+       //! tex coords (2F)
+       ccTex2F                 texCoords;
+} ccV2F_C4B_T2F;
+
+//! a Point with a vertex point, a tex coord point and a color 4F
+typedef struct _ccV2F_C4F_T2F
+{
+       //! vertices (2F)
+       ccVertex2F              vertices;
+       //! colors (4F)
+       ccColor4F               colors;
+       //! tex coords (2F)
+       ccTex2F                 texCoords;
+} ccV2F_C4F_T2F;
+
+//! a Point with a vertex point, a tex coord point and a color 4B
+typedef struct _ccV3F_C4B_T2F
+{
+       //! vertices (3F)
+       ccVertex3F              vertices;                       // 12 bytes
+//     char __padding__[4];
+
+       //! colors (4B)
+       ccColor4B               colors;                         // 4 bytes
+//     char __padding2__[4];
+
+       // tex coords (2F)
+       ccTex2F                 texCoords;                      // 8 byts
+} ccV3F_C4B_T2F;
+
+//! 4 ccVertex2FTex2FColor4B Quad
+typedef struct _ccV2F_C4B_T2F_Quad
+{
+       //! bottom left
+       ccV2F_C4B_T2F   bl;
+       //! bottom right
+       ccV2F_C4B_T2F   br;
+       //! top left
+       ccV2F_C4B_T2F   tl;
+       //! top right
+       ccV2F_C4B_T2F   tr;
+} ccV2F_C4B_T2F_Quad;
+
+//! 4 ccVertex3FTex2FColor4B
+typedef struct _ccV3F_C4B_T2F_Quad
+{
+       //! top left
+       ccV3F_C4B_T2F   tl;
+       //! bottom left
+       ccV3F_C4B_T2F   bl;
+       //! top right
+       ccV3F_C4B_T2F   tr;
+       //! bottom right
+       ccV3F_C4B_T2F   br;
+} ccV3F_C4B_T2F_Quad;
+
+//! 4 ccVertex2FTex2FColor4F Quad
+typedef struct _ccV2F_C4F_T2F_Quad
+{
+       //! bottom left
+       ccV2F_C4F_T2F   bl;
+       //! bottom right
+       ccV2F_C4F_T2F   br;
+       //! top left
+       ccV2F_C4F_T2F   tl;
+       //! top right
+       ccV2F_C4F_T2F   tr;
+} ccV2F_C4F_T2F_Quad;
+
+//! Blend Function used for textures
+typedef struct _ccBlendFunc
+{
+       //! source blend function
+       GLenum src;
+       //! destination blend function
+       GLenum dst;
+} ccBlendFunc;
+
+//! delta time type
+//! if you want more resolution redefine it as a double
+typedef float ccTime;
+//typedef double ccTime;
diff --git a/libs/cocos2d/cocos2d.h b/libs/cocos2d/cocos2d.h
new file mode 100644 (file)
index 0000000..fed2a9b
--- /dev/null
@@ -0,0 +1,161 @@
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2008-2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+/** @mainpage cocos2d for iPhone API reference
+ *
+ * @image html Icon.png
+ *
+ * @section intro Introduction
+ * This is cocos2d API reference
+ *
+ * The programming guide is hosted here: http://www.cocos2d-iphone.org/wiki/doku.php/prog_guide:index
+ *
+ * <hr>
+ *
+ * @todo A native english speaker should check the grammar. We need your help!
+ *
+ */
+
+// 0x00 HI ME LO
+// 00   01 00 00
+#define COCOS2D_VERSION 0x00010000
+
+#import <Availability.h>
+
+//
+// all cocos2d include files
+//
+#import "ccConfig.h"   // should be included first
+
+#import "CCActionManager.h"
+#import "CCAction.h"
+#import "CCActionInstant.h"
+#import "CCActionInterval.h"
+#import "CCActionEase.h"
+#import "CCActionCamera.h"
+#import "CCActionTween.h"
+#import "CCActionEase.h"
+#import "CCActionTiledGrid.h"
+#import "CCActionGrid3D.h"
+#import "CCActionGrid.h"
+#import "CCActionProgressTimer.h"
+#import "CCActionPageTurn3D.h"
+
+#import "CCAnimation.h"
+#import "CCAnimationCache.h"
+#import "CCSprite.h"
+#import "CCSpriteFrame.h"
+#import "CCSpriteBatchNode.h"
+#import "CCSpriteFrameCache.h"
+
+#import "CCLabelTTF.h"
+#import "CCLabelBMFont.h"
+#import "CCLabelAtlas.h"
+
+#import "CCParticleSystem.h"
+#import "CCParticleSystemPoint.h"
+#import "CCParticleSystemQuad.h"
+#import "CCParticleExamples.h"
+
+#import "CCTexture2D.h"
+#import "CCTexturePVR.h"
+#import "CCTextureCache.h"
+#import "CCTextureAtlas.h"
+
+#import "CCTransition.h"
+#import "CCTransitionPageTurn.h"
+#import "CCTransitionRadial.h"
+
+#import "CCTMXTiledMap.h"
+#import "CCTMXLayer.h"
+#import "CCTMXObjectGroup.h"
+#import "CCTMXXMLParser.h"
+#import "CCTileMapAtlas.h"
+
+#import "CCLayer.h"
+#import "CCMenu.h"
+#import "CCMenuItem.h"
+#import "CCDrawingPrimitives.h"
+#import "CCScene.h"
+#import "CCScheduler.h"
+#import "CCBlockSupport.h"
+#import "CCCamera.h"
+#import "CCProtocols.h"
+#import "CCNode.h"
+#import "CCDirector.h"
+#import "CCAtlasNode.h"
+#import "CCGrabber.h"
+#import "CCGrid.h"
+#import "CCParallaxNode.h"
+#import "CCRenderTexture.h"
+#import "CCMotionStreak.h"
+#import "CCConfiguration.h"
+
+//
+// cocos2d macros
+//
+#import "ccTypes.h"
+#import "ccMacros.h"
+
+
+// Platform common
+#import "Platforms/CCGL.h"
+#import "Platforms/CCNS.h"
+
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+#import "Platforms/iOS/CCTouchDispatcher.h"
+#import "Platforms/iOS/CCTouchDelegateProtocol.h"
+#import "Platforms/iOS/CCTouchHandler.h"
+#import "Platforms/iOS/EAGLView.h"
+#import "Platforms/iOS/CCDirectorIOS.h"
+
+#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED)
+#import "Platforms/Mac/MacGLView.h"
+#import "Platforms/Mac/CCDirectorMac.h"
+#endif
+
+//
+// cocos2d helper files
+//
+#import "Support/OpenGL_Internal.h"
+#import "Support/CCFileUtils.h"
+#import "Support/CGPointExtension.h"
+#import "Support/ccCArray.h"
+#import "Support/CCArray.h"
+#import "Support/ccUtils.h"
+
+#if CC_ENABLE_PROFILERS
+#import "Support/CCProfiling.h"
+#endif // CC_ENABLE_PROFILERS
+
+
+// free functions
+NSString * cocos2dVersion(void);
+
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+#ifndef __IPHONE_4_0
+#error "If you are targeting iPad, you should set BASE SDK = 4.0 (or 4.1, or 4.2), and set the 'iOS deploy target' = 3.2"
+#endif
+#endif
diff --git a/libs/cocos2d/cocos2d.m b/libs/cocos2d/cocos2d.m
new file mode 100644 (file)
index 0000000..08f82a1
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2008-2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+
+#import <Foundation/Foundation.h>
+
+static NSString *version = @"cocos2d v1.0.0-rc3";
+
+NSString *cocos2dVersion()
+{      
+       return version;
+}
index e08918a..01cc2bf 100644 (file)
@@ -51,6 +51,7 @@ static QQGame* _CurrentGame = NULL;
         
         _world = [[QQWorld alloc] init];
         _world.proxiedNotifier = self;
+        // _root.scaleX = _root.scaleY = _world.scale;
         [_world addObserver:self selector:@selector(onCollide:) name:QQ_EVENT_CONTACT_BEGIN object:nil];
         [_world addObserver:self selector:@selector(onCollide:) name:QQ_EVENT_CONTACT_END object:nil];
         
@@ -137,6 +138,7 @@ static QQGame* _CurrentGame = NULL;
     if (_ticks == 1)
         NSLog(@"%@", self);
     
+    // _root.scaleX = _root.scaleY = _world.scale;
     for (QQActor* actor in self.actors)
         [actor tick];
     [self.world step];