Adding CocosPlayer for iOS

This commit is contained in:
Rohan Kuruvilla 2013-04-17 17:24:59 -07:00
parent b648d392ba
commit 9b86791eef
58 changed files with 10992 additions and 193 deletions

View File

@ -22,8 +22,9 @@ char *_js_log_buf_ccbuilder = NULL;
USING_NS_CC;
using namespace CocosDenshion;
CCScene *mainScene;
AppDelegate::AppDelegate()
AppDelegate::AppDelegate(): isRetina(false), isIPhone(false)
{
}
@ -32,6 +33,23 @@ AppDelegate::~AppDelegate()
CCScriptEngineManager::sharedManager()->purgeSharedManager();
}
bool runMainScene() {
/* Push the new scene with a fancy transition. */
ccColor3B transitionColor;
transitionColor.r = 0;
transitionColor.g = 0;
transitionColor.b = 0;
mainScene = PlayerStatus::loadMainScene("StatusLayer.ccbi");
if(CCDirector::sharedDirector()->getRunningScene() != NULL) {
CCDirector::sharedDirector()->replaceScene(CCTransitionFade::create(0.5f, mainScene, transitionColor));
} else {
CCDirector::sharedDirector()->runWithScene(CCTransitionFade::create(0.5f, mainScene, transitionColor));
}
return true;
}
void handle_ccb_run() {
CCFileUtils::sharedFileUtils()->purgeCachedEntries();
ScriptingCore::getInstance()->runScript("main.js");
@ -45,21 +63,27 @@ void handle_set_orient(bool isPortrait) {
CCLOG("ORIENTATION HALF IMPLEMENTED");
}
void handle_set_message(const char* msg) {
CCBHelper::setInstructionsMessage(msg);
}
void handle_set_status(const char* msg) {
CCBHelper::setStatusMessage(msg);
}
void handle_disconnected() {
CCBHelper::setStatusMessage("Disconnected");
}
void handle_ccb_stop() {
CCLOG("STOP UNIMPLEMENTED");
runMainScene();
}
extern "C" {
bool runMainScene() {
PlayerStatus::loadMainScene("StatusLayer.ccbi");
return true;
}
const char * getCCBDirectoryPath();
bool AppDelegate::applicationDidFinishLaunching()
{
@ -68,46 +92,106 @@ bool AppDelegate::applicationDidFinishLaunching()
pDirector->setOpenGLView(CCEGLView::sharedOpenGLView());
pDirector->setProjection(kCCDirectorProjection2D);
CCSize screenSize = CCEGLView::sharedOpenGLView()->getFrameSize();
CCSize designSize = CCSizeMake(320, 480);
CCSize resourceSize = CCSizeMake(320, 480);
std::vector<std::string> resDirOrders;
std::string res;
TargetPlatform platform = CCApplication::sharedApplication()->getTargetPlatform();
CCFileUtils* pFileUtils = CCFileUtils::sharedFileUtils();
std::vector<std::string> searchResOrder;
string res = "xlarge";
// if (screenSize.height > 1024)
// {
// resourceSize = CCSizeMake(1280, 1920);
// searchResOrder.push_back("resources-xlarge");
// res = "xlarge";
// }
// else
if (screenSize.height > 960)
{
resourceSize = CCSizeMake(640, 960);
searchResOrder.push_back("resources-large");
res = "large";
}
else if (screenSize.height > 480)
{
resourceSize = CCSizeMake(480, 720);
searchResOrder.push_back("resources-medium");
res = "medium";
}
else
{
resourceSize = CCSizeMake(320, 568);
searchResOrder.push_back("resources-small");
res = "small";
}
pFileUtils->setSearchResolutionsOrder(searchResOrder);
pDirector->setContentScaleFactor(resourceSize.height/designSize.height);
if (platform == kTargetIphone || platform == kTargetIpad)
{
std::vector<std::string> searchPaths = CCFileUtils::sharedFileUtils()->getSearchPaths();
searchPaths.insert(searchPaths.begin(), "Published files iOS");
searchPaths.insert(searchPaths.begin(), getCCBDirectoryPath());
CCFileUtils::sharedFileUtils()->setSearchPaths(searchPaths);
if (screenSize.height > 1024)
{
res = "iPad";
designSize = CCSizeMake(360, 480);
resourceSize = CCSizeMake(1536, 2048);
resDirOrders.push_back("resources-ipadhd");
resDirOrders.push_back("resources-ipad");
resDirOrders.push_back("resources-iphonehd");
isIPhone = false;
isRetina = true;
}
else if (screenSize.height > 960)
{
res = "iPad";
designSize = CCSizeMake(360, 480);
resourceSize = CCSizeMake(768, 1024);
resDirOrders.push_back("resources-ipad");
resDirOrders.push_back("resources-iphonehd");
isIPhone = false;
isRetina = false;
}
else if (screenSize.height > 480)
{
res = "iPhone";
resourceSize = CCSizeMake(640, 960);
resDirOrders.push_back("resources-iphonehd");
resDirOrders.push_back("resources-iphone");
isIPhone = true;
isRetina = true;
}
else
{
res = "iPhone";
resourceSize = CCSizeMake(320, 480);
resDirOrders.push_back("resources-iphone");
isIPhone = true;
isRetina = false;
}
}
else if (platform == kTargetAndroid || platform == kTargetWindows)
{
if (screenSize.height > 960)
{
res = "xlarge";
resourceSize = CCSizeMake(1280, 1920);
resDirOrders.push_back("resources-xlarge");
resDirOrders.push_back("resources-large");
resDirOrders.push_back("resources-medium");
resDirOrders.push_back("resources-small");
}
else if (screenSize.height > 720)
{
res = "large";
resourceSize = CCSizeMake(640, 960);
resDirOrders.push_back("resources-large");
resDirOrders.push_back("resources-medium");
resDirOrders.push_back("resources-small");
}
else if (screenSize.height > 480)
{
res = "medium";
resourceSize = CCSizeMake(480, 720);
resDirOrders.push_back("resources-medium");
resDirOrders.push_back("resources-small");
}
else
{
res = "small";
resourceSize = CCSizeMake(320, 480);
resDirOrders.push_back("resources-small");
}
}
CCFileUtils *pFileUtils = CCFileUtils::sharedFileUtils();
pFileUtils->setSearchResolutionsOrder(resDirOrders);
pDirector->setContentScaleFactor(resourceSize.width/designSize.width);
CCEGLView::sharedOpenGLView()->setDesignResolutionSize(designSize.width, designSize.height, kResolutionNoBorder);
std::vector<std::string> searchPaths = pFileUtils->getSearchPaths();
searchPaths.insert(searchPaths.begin(), pFileUtils->getWritablePath());
pFileUtils->setSearchPaths(searchPaths);

View File

@ -20,7 +20,7 @@
class AppDelegate : private cocos2d::CCApplication
{
public:
// bool isRetina = false, isIPhone = false;
bool isRetina, isIPhone;
AppDelegate();
virtual ~AppDelegate();
@ -46,11 +46,14 @@ public:
};
bool runMainScene();
void handle_ccb_run();
void handle_ccb_stop();
void handle_connected();
void handle_disconnected();
void handle_set_orient(bool isPortrait);
void handle_set_message(const char *msg);
void handle_set_status(const char *msg);
#endif // _APP_DELEGATE_H_

View File

@ -0,0 +1,21 @@
//
// CCBDirectoryComparer.h
// CocosBuilder
//
// Created by Viktor Lidholt on 2/5/13.
//
//
#import <Foundation/Foundation.h>
@interface CCBDirectoryComparer : NSObject
{
}
@property (nonatomic,readonly) NSMutableDictionary* files;
- (void) loadDirectory: (NSString*) dir;
- (NSArray*) diffWithFiles:(NSDictionary*) diffFiles;
@end

View File

@ -0,0 +1,109 @@
//
// CCBDirectoryComparer.m
// CocosBuilder
//
// Created by Viktor Lidholt on 2/5/13.
//
//
#import "CCBDirectoryComparer.h"
@implementation CCBDirectoryComparer
- (id) init
{
self = [super init];
if (!self) return NULL;
_files = [[NSMutableDictionary dictionary] retain];
return self;
}
- (NSDate*) modificationDateForFile:(NSString*)file
{
NSDictionary* attr = [[NSFileManager defaultManager] attributesOfItemAtPath:file error:NULL];
return [attr objectForKey:NSFileModificationDate];
}
- (void) loadDirectory: (NSString*) dir subDir:(NSString*) subDir
{
NSFileManager* fm = [NSFileManager defaultManager];
NSString* absDir = dir;
if (subDir) absDir = [dir stringByAppendingPathComponent:subDir];
NSArray* contents = [fm contentsOfDirectoryAtPath:absDir error:NULL];
for (NSString* file in contents)
{
NSString* absFile = [absDir stringByAppendingPathComponent:file];
NSString* relFile = file;
if (subDir) relFile = [subDir stringByAppendingPathComponent:file];
BOOL isDir = NO;
if ([fm fileExistsAtPath:absFile isDirectory:&isDir])
{
if (isDir)
{
// Go down in sub directory
NSString* subSubDir = file;
if (subDir) subSubDir = [subDir stringByAppendingPathComponent:file];
[self loadDirectory:dir subDir:subSubDir];
}
else
{
// Add file
NSDate* date = [self modificationDateForFile:absFile];
[_files setObject:date forKey:relFile];
}
}
}
}
- (void) loadDirectory: (NSString*) dir
{
[_files removeAllObjects];
[self loadDirectory:dir subDir: NULL];
}
// More approximate comparison of dates
- (BOOL) isEqualDate:(NSDate*) d1 to:(NSDate*) d2
{
NSTimeInterval delta = fabs([d1 timeIntervalSinceDate:d2]);
return (delta < 2.0);
}
- (NSArray*) diffWithFiles:(NSDictionary*) diffFiles
{
NSMutableArray* diff = [NSMutableArray array];
for (NSString* file in _files)
{
NSDate* fileDate = [_files objectForKey:file];
NSDate* diffFileDate = [diffFiles objectForKey:file];
if (diffFileDate && [self isEqualDate:fileDate to:diffFileDate])
{
// Files are equal, there is no diff
continue;
}
// Add to list of modified files
[diff addObject:file];
NSLog(@"MOD: %@ src: %@ dst: %@", file, fileDate, diffFileDate);
}
return diff;
}
- (void) dealloc
{
[super dealloc];
[_files release];
}
@end

View File

@ -16,13 +16,19 @@ using namespace cocos2d;
using namespace std;
extern "C" {
void setPairingCodeJNI(int code) {
JniMethodInfo t;
if (JniHelper::getStaticMethodInfo(t, SOCKET_CLASS_NAME, "setPairingCode", "(I)V")) {
void updatePairing(const char *code) {
int pairingCode = 0;
if(strcmp(code, "Auto") == 0) {
pairingCode = -1;
} else {
pairingCode = atoi(code);
}
JniMethodInfo t;
if (JniHelper::getStaticMethodInfo(t, SOCKET_CLASS_NAME, "setPairingCode", "(I)V")) {
t.env->CallStaticVoidMethod(t.classID, t.methodID, code);
t.env->DeleteLocalRef(t.classID);
}
}
}
}
void cleanCacheDirJNI() {
JniMethodInfo t;
@ -41,4 +47,7 @@ extern "C" {
t.env->DeleteLocalRef(t.classID);
}
}
const char *getCCBDirectoryPath() {
return "";
}
}

View File

@ -27,7 +27,7 @@ THE SOFTWARE.
#include <string>
extern "C" {
extern void setPairingCodeJNI(int code);
extern void updatePairing(const char *code);
extern void cleanCacheDirJNI();
extern void setDeviceResolutionJNI(const char* res);
}

View File

@ -4,84 +4,41 @@ CCMenuItemImage * CCBHelper::mBtnRun = NULL;
CCMenuItemImage * CCBHelper::mBtnPair = NULL;
CCMenuItemImage * CCBHelper::mBtnReset = NULL;
CCLabelTTF * CCBHelper::mLblStatus = NULL;
CCLabelTTF * CCBHelper::mLblPair = NULL;
CCLabelTTF * CCBHelper::mLblInstructions = NULL;
std::string CCBHelper::mLblStatus_background = "";
std::string CCBHelper::mLblPair_background = "";
bool CCBHelper::mLblInstructions_visible = true;
void CCBHelper::setStatusObject(CCLabelTTF *status) {
if(mLblStatus == NULL) {
mLblStatus = CCLabelTTF::create();
}
status->retain();
mLblStatus = status;
if(mLblStatus_background != "") {
status->setString(mLblStatus_background.c_str());
}
}
std::string CCBHelper::mLblStatus_prev = "";
std::string CCBHelper::mLblStatus_str = "";
std::string CCBHelper::mLblInstructions_prev= "";
std::string CCBHelper::mLblInstructions_str= "";
std::string CCBHelper::mLblPair_prev = "";
std::string CCBHelper::mLblPair_str = "";
bool CCBHelper::gameAvailable = false;
void CCBHelper::setStatusMessage(std::string str) {
if(mLblStatus == NULL) {
mLblStatus_background = str;
} else {
mLblStatus->setString(str.c_str());
}
if(mLblStatus_prev.compare(str) != 0) {
mLblStatus_prev = str;
mLblStatus_str = str;
}
}
void CCBHelper::setInstructionsObject(CCLabelTTF *status) {
if(mLblInstructions == NULL) {
mLblInstructions = CCLabelTTF::create();
}
status->retain();
mLblInstructions = status;
if(!mLblInstructions_visible) {
mLblInstructions->setVisible(false);
}
void CCBHelper::setInstructionsMessage(std::string str) {
if(mLblInstructions_prev.compare(str) != 0) {
mLblInstructions_prev = str;
mLblInstructions_str = str;
}
}
void CCBHelper::setInstructionsMessage(bool isVisible) {
if(mLblInstructions == NULL) {
mLblInstructions_visible = isVisible;
} else {
mLblInstructions_visible = isVisible;
mLblInstructions->setVisible(isVisible);
}
}
void CCBHelper::setPairObject(CCLabelTTF *pair) {
if(mLblPair == NULL) {
mLblPair = CCLabelTTF::create();
}
pair->retain();
mLblPair = pair;
if(mLblPair_background != "") {
mLblPair->setString(mLblPair_background.c_str());
}
}
void CCBHelper::setPairMessage(std::string str) {
if(mLblPair == NULL) {
mLblPair_background = str;
} else {
mLblPair->setString(str.c_str());
}
if(mLblPair_prev.compare(str) != 0) {
mLblPair_prev = str;
mLblPair_str = str;
}
}
bool CCBHelper::checkIsMainJSPresent() {
gameAvailable = CCFileUtils::sharedFileUtils()->isFileExist(CCFileUtils::sharedFileUtils()->fullPathForFilename("main.js"));
}
bool CCBHelper::isMainJSPresent() {
std::string path = CCFileUtils::sharedFileUtils()->fullPathForFilename("main.js");
CCLOG("PATH RETURNED: %s", path.c_str());
if(path == "") {
return false;
}
return true;
return gameAvailable;
}

View File

@ -8,22 +8,16 @@ class CCBHelper {
static CCMenuItemImage * mBtnRun;
static CCMenuItemImage * mBtnReset;
static CCMenuItemImage * mBtnPair;
static bool gameAvailable;
static std::string mLblStatus_prev, mLblStatus_str;
static std::string mLblPair_prev, mLblPair_str;
static std::string mLblInstructions_str, mLblInstructions_prev;
static CCLabelTTF *mLblStatus;
static std::string mLblStatus_background;
static CCLabelTTF *mLblPair;
static std::string mLblPair_background;
static CCLabelTTF *mLblInstructions;
static bool mLblInstructions_visible;
static void setPairObject(CCLabelTTF *pair);
static void setPairMessage(std::string str);
static void setInstructionsObject(CCLabelTTF *instr);
static void setInstructionsMessage(bool isVisible);
static void setStatusObject(CCLabelTTF *status);
static void setStatusMessage(std::string str);
static void setPairMessage(std::string str);
static void setInstructionsMessage(std::string msg);
static void setStatusMessage(std::string str);
static bool isMainJSPresent();
static bool checkIsMainJSPresent();
};

View File

@ -3,17 +3,18 @@
#include "PlayerStatusLoader.h"
#include "MainSceneHelper.h"
#include "AppDelegate.h"
#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)
#include "GUI/CCEditBox/CCEditBox.h"
#include "jni/Java_org_cocos2dx_lib_Cocos2dxHelper.h"
#endif
USING_NS_CC;
USING_NS_CC_EXT;
PlayerStatus::PlayerStatus(): mBtnRun(NULL), mBtnReset(NULL), mBtnPair(NULL),
mLblStatus(NULL), mLblInstructions(NULL), mLblPair(NULL)
mLblStatus(NULL), mLblInstructions(NULL), mLblPair(NULL), editBox(NULL)
{
this->scheduleUpdate();
}
PlayerStatus::~PlayerStatus()
@ -46,32 +47,57 @@ SEL_MenuHandler PlayerStatus::onResolveCCBCCMenuItemSelector(CCObject * pTarget,
return NULL;
}
void PlayerStatus::setDeviceResolution(std::string res) {
setDeviceResolutionJNI(res.c_str());
}
void updatePairing(const char *pairing);
void editBoxCallbackFunc(const char* pText, void* ctx)
{
PlayerStatus *thiz = (PlayerStatus *)ctx;
std::string text(pText);
if(text == "" || text == " ") {
thiz->mLblPair->setString("Auto");
setPairingCodeJNI(-1);
updatePairing("Auto");
} else {
thiz->mLblPair->setString(pText);
setPairingCodeJNI(atoi(pText));
updatePairing(pText);
}
}
void PlayerStatus::pressedPair(CCObject * pSender, cocos2d::extension::CCControlEvent pCCControlEvent) {
showEditTextDialogJNI("Enter pairing code", "", kEditBoxInputModeNumeric, kEditBoxInputFlagInitialCapsWord,
kKeyboardReturnTypeDone, 4, editBoxCallbackFunc, (void*)this);
TargetPlatform platform = CCApplication::sharedApplication()->getTargetPlatform();
#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)
showEditTextDialogJNI("Enter pairing code", "", kEditBoxInputModeNumeric, kEditBoxInputFlagInitialCapsWord,
kKeyboardReturnTypeDone, 4, editBoxCallbackFunc, (void*)this);
#else
if(!this->editBox) {
this->editBox = CCEditBox::create(CCSize(150, 50), CCScale9Sprite::create("green_edit.png"));
PairingDelegate *p = new PairingDelegate();
p->setTarget(this);
CCPoint visibleOrigin = CCEGLView::sharedOpenGLView()->getVisibleOrigin();
CCSize visibleSize = CCEGLView::sharedOpenGLView()->getVisibleSize();
editBox->setPosition(ccp(visibleOrigin.x+visibleSize.width/2, visibleOrigin.y+visibleSize.height*1/2));
editBox->setDelegate(p);
editBox->setPlaceHolder("Enter Pairing Code");
editBox->setPlaceholderFontColor(ccRED);
editBox->setFont("arial", 15);
editBox->setMaxLength(4);
editBox->setReturnType(kKeyboardReturnTypeDone);
this->addChild(editBox);
} else {
this->editBox->setVisible(true);
}
#endif
}
void PlayerStatus::pressedReset(CCObject * pSender, cocos2d::extension::CCControlEvent pCCControlEvent) {
cleanCacheDirJNI();
this->mBtnRun->setEnabled(false);
this->mBtnReset->setEnabled(false);
CCBHelper::checkIsMainJSPresent();
}
@ -82,54 +108,33 @@ void PlayerStatus::pressedRun(CCObject * pSender, cocos2d::extension::CCControlE
bool PlayerStatus::onAssignCCBMemberVariable(CCObject * pTarget, const char * pMemberVariableName, CCNode * pNode) {
CCB_MEMBERVARIABLEASSIGNER_GLUE(this, "btnRun", CCMenuItemImage *, this->mBtnRun);
if(strcmp(pMemberVariableName, "btnRun")) {
if(this->mBtnRun == NULL) {
this->mBtnRun = CCMenuItemImage::create();
}
if(CCBHelper::isMainJSPresent()) {
this->mBtnRun->setEnabled(true);
} else {
this->mBtnRun->setEnabled(false);
}
}
CCB_MEMBERVARIABLEASSIGNER_GLUE(this, "btnReset", CCMenuItemImage *, this->mBtnReset);
if(strcmp(pMemberVariableName, "btnReset")) {
if(this->mBtnReset == NULL) {
this->mBtnReset = CCMenuItemImage::create();
}
if(CCBHelper::isMainJSPresent()) {
this->mBtnReset->setEnabled(true);
} else {
this->mBtnReset->setEnabled(false);
}
}
CCB_MEMBERVARIABLEASSIGNER_GLUE(this, "btnPair", CCMenuItemImage *, this->mBtnPair);
CCB_MEMBERVARIABLEASSIGNER_GLUE(this, "lblStatus", CCLabelTTF *, this->mLblStatus);
if(strcmp(pMemberVariableName, "lblStatus") && this->mLblStatus != NULL) {
CCBHelper::setStatusObject(this->mLblStatus);
}
CCB_MEMBERVARIABLEASSIGNER_GLUE(this, "lblPair", CCLabelTTF *, this->mLblPair);
if(strcmp(pMemberVariableName, "lblPair") && this->mLblPair != NULL) {
CCBHelper::setPairObject(this->mLblPair);
}
CCB_MEMBERVARIABLEASSIGNER_GLUE(this, "lblInstructions", CCLabelTTF *, this->mLblInstructions);
if(strcmp(pMemberVariableName, "lblInstructions") && this->mLblInstructions != NULL) {
CCBHelper::setInstructionsObject(this->mLblInstructions);
}
return false;
}
void PlayerStatus::update(float dt) {
if(this->mLblInstructions != NULL && CCBHelper::mLblInstructions_str != "") {
this->mLblInstructions->setString(CCBHelper::mLblInstructions_str.c_str());
}
if(this->mLblStatus != NULL && CCBHelper::mLblStatus_str != "") {
this->mLblStatus->setString(CCBHelper::mLblStatus_str.c_str());
}
if(CCBHelper::isMainJSPresent() && this->mBtnRun != NULL && this->mBtnReset != NULL) {
this->mBtnRun->setEnabled(true);
this->mBtnReset->setEnabled(true);
} else if(this->mBtnRun != NULL && this->mBtnReset != NULL) {
this->mBtnRun->setEnabled(false);
this->mBtnReset->setEnabled(false);
}
}
void PlayerStatus::loadMainScene(const char *pCCBFileName) {
cocos2d::CCScene* PlayerStatus::loadMainScene(const char *pCCBFileName) {
CCNodeLoaderLibrary * ccNodeLoaderLibrary = CCNodeLoaderLibrary::newDefaultCCNodeLoaderLibrary();
ccNodeLoaderLibrary->registerCCNodeLoader("PlayerStatusLayer", PlayerStatusLoader::loader());
@ -149,12 +154,7 @@ void PlayerStatus::loadMainScene(const char *pCCBFileName) {
if(node != NULL) {
scene->addChild(node);
}
/* Push the new scene with a fancy transition. */
ccColor3B transitionColor;
transitionColor.r = 0;
transitionColor.g = 0;
transitionColor.b = 0;
CCDirector::sharedDirector()->runWithScene(CCTransitionFade::create(0.5f, scene, transitionColor));
}
CCBHelper::checkIsMainJSPresent();
return scene;
}

View File

@ -16,7 +16,22 @@
* This thread describes the problem:
* http://www.cocoabuilder.com/archive/xcode/265549-crash-in-virtual-method-call.html
*/
void editBoxCallbackFunc(const char* pText, void* ctx);
class PairingDelegate: public cocos2d::extension::CCEditBoxDelegate {
private:
void *m_Target;
public:
void setTarget(void *target) {
m_Target = target;
}
virtual void editBoxReturn(cocos2d::extension::CCEditBox* editBox) {
editBoxCallbackFunc(editBox->getText(), m_Target);
editBox->setVisible(false);
}
};
class PlayerStatus
: public cocos2d::CCLayer
@ -30,8 +45,6 @@ public:
PlayerStatus();
virtual ~PlayerStatus();
void openTest(const char * pCCBFileName, const char * pCCNodeName = NULL, cocos2d::extension::CCNodeLoader * pCCNodeLoader = NULL);
virtual cocos2d::SEL_MenuHandler onResolveCCBCCMenuItemSelector(cocos2d::CCObject * pTarget, const char * pSelectorName);
virtual cocos2d::extension::SEL_CCControlHandler onResolveCCBCCControlSelector(cocos2d::CCObject * pTarget, const char * pSelectorName);
virtual bool onAssignCCBMemberVariable(cocos2d::CCObject * pTarget, const char * pMemberVariableName, cocos2d::CCNode * pNode);
@ -40,7 +53,7 @@ public:
void pressedPair(CCObject * pSender, cocos2d::extension::CCControlEvent pCCControlEvent);
void pressedReset(CCObject * pSender, cocos2d::extension::CCControlEvent pCCControlEvent);
void pressedRun(CCObject * pSender, cocos2d::extension::CCControlEvent pCCControlEvent);
static void loadMainScene(const char *fileName);
static cocos2d::CCScene* loadMainScene(const char *fileName);
static void setDeviceResolution(std::string res);
cocos2d::CCMenuItemImage* mBtnRun;
@ -50,6 +63,9 @@ public:
cocos2d::CCLabelTTF* mLblStatus;
cocos2d::CCLabelTTF* mLblInstructions;
cocos2d::CCLabelTTF* mLblPair;
virtual void update(float dt);
private:
cocos2d::extension::CCEditBox *editBox;
};

View File

@ -0,0 +1,99 @@
/*
* CocosBuilder: http://www.cocosbuilder.com
*
* Copyright (c) 2012 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 "ThoMoServerStub.h"
#import "cocos2d.h"
// Network status states
typedef enum {
kCCBNetworkUninitialized = -1,
kCCBNetworkStatusWaiting,
kCCBNetworkStatusTooMany,
kCCBNetworkStatusConnected,
kCCBNetworkStatusShutDown,
} CCBNetworkStatus;
// Player status states
typedef enum {
kCCBPlayerStatuskUninitialized = -1,
kCCBPlayerStatusIdle,
kCCBPlayerStatusPlay,
kCCBPlayerStatusUnzip,
kCCBPlayerStatusStop,
kCCBPlayerStatusNotConnected,
kCCBPlayerStatusExecuteScript,
} CCBPlayerStatus;
// Predefined messages
extern NSString *kCCBNetworkStatusStringWaiting;
extern NSString *kCCBNetworkStatusStringTooMany;
extern NSString *kCCBNetworkStatusStringConnected;
extern NSString *kCCBNetworkStatusStringShutDown;
extern NSString *kCCBPlayerStatusStringNotConnected;
extern NSString *kCCBPlayerStatusStringIdle;
extern NSString *kCCBPlayerStatusStringUnzip;
extern NSString *kCCBPlayerStatusStringStop;
extern NSString *kCCBPlayerStatusStringPlay;
extern NSString *kCCBPlayerStatusStringScript;
@interface ServerController : NSObject <ThoMoServerDelegateProtocol>
{
ThoMoServerStub* server;
NSMutableSet* connectedClients;
NSFileHandle* pipeReadHandle;
CCBNetworkStatus networkStatus;
CCBPlayerStatus playerStatus;
BOOL playerWindowDisplayed;
}
/** Network status: who is the network connection */
@property (nonatomic, readwrite) CCBNetworkStatus networkStatus;
/** State of the player: playing, unzipping, stopping, etc. */
@property (nonatomic, readwrite) CCBPlayerStatus playerStatus;
@property (nonatomic, readwrite) BOOL playerWindowDisplayed;
@property (nonatomic, readwrite) BOOL isRetina;
@property (nonatomic, readwrite) BOOL isIPhone;
@property (nonatomic,copy) NSString* serverStatus;
- (void) start;
- (void) stop;
- (void) setPairingCode: (NSString *)code;
- (void) startIfNotStarted;
- (void) updatePairing;
- (void) sendDeviceName;
- (void) sendResultString:(NSString*) str;
- (void) sendLog:(NSString*)log;
- (void) sendFileList;
@end

View File

@ -0,0 +1,607 @@
/*
* CocosBuilder: http://www.cocosbuilder.com
*
* Copyright (c) 2012 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 "ServerController.h"
#import "js_bindings_core.h"
#import "CCBReader.h"
#include "PlayerStatus.h"
#include "AppDelegate.h"
#import "CCBDirectoryComparer.h"
#import "SSZipArchive.h"
// Predefined messages
NSString *kCCBNetworkStatusStringWaiting = @"Waiting for connections";
NSString *kCCBNetworkStatusStringTooMany = @"Too many connections";
NSString *kCCBNetworkStatusStringConnected = @"Connected";
NSString *kCCBNetworkStatusStringShutDown = @"Server shut down";
NSString *kCCBPlayerStatusStringNotConnected = @"Connect by running CocosBuilder on the same local wireless network as CocosPlayer.\nIf multiple instances of CocosBuilder is run on the same network, use a unique pairing code.";
NSString *kCCBPlayerStatusStringIdle = @"Idle";
NSString *kCCBPlayerStatusStringUnzip = @"Action: Unzip game";
NSString *kCCBPlayerStatusStringStop = @"Action: Stop";
NSString *kCCBPlayerStatusStringPlay = @"Action: Run";
NSString *kCCBPlayerStatusStringScript = @"Action: Executing script";
@implementation ServerController
@synthesize networkStatus, playerStatus, playerWindowDisplayed, isRetina, isIPhone;
#pragma mark Initializers and setup
- (NSString*) protocolIdentifier
{
NSString* pairing = [[NSUserDefaults standardUserDefaults] objectForKey:@"pairing"];
if (pairing)
{
return [NSString stringWithFormat:@"CocosP-%@",pairing];
}
else
{
return @"CocosPlayer";
}
}
- (id) init
{
self = [super init];
if (!self) return NULL;
connectedClients = [[NSMutableSet alloc] init];
server = [[ThoMoServerStub alloc] initWithProtocolIdentifier:[self protocolIdentifier]];
[server setDelegate:self];
networkStatus = kCCBNetworkUninitialized;
playerStatus = kCCBPlayerStatuskUninitialized;
playerWindowDisplayed = YES;
return self;
}
#pragma mark Redirection of std out
- (void) redirectStdErr
{
NSPipe* pipe = [NSPipe pipe];
pipeReadHandle = [pipe fileHandleForReading];
[pipeReadHandle readInBackgroundAndNotify];
int err = dup2([[pipe fileHandleForWriting] fileDescriptor], STDERR_FILENO);
if (!err) NSLog(@"ConsoleWindow: Failed to redirect stderr");
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(readData:) name:NSFileHandleReadCompletionNotification object:pipeReadHandle];
[pipeReadHandle retain];
}
- (void) readData:(NSNotification*)notification
{
[pipeReadHandle readInBackgroundAndNotify] ;
NSString *str = [[NSString alloc] initWithData: [[notification userInfo] objectForKey: NSFileHandleNotificationDataItem] encoding: NSASCIIStringEncoding] ;
[self sendLog:str];
}
#pragma mark Control methods
- (void) start
{
if (server)
{
[server start];
// Redirect std out
[self redirectStdErr];
}
}
- (void) startIfNotStarted
{
if (!server)
{
server = [[ThoMoServerStub alloc] initWithProtocolIdentifier:[self protocolIdentifier]];
[server setDelegate:self];
[server start];
self.networkStatus = kCCBNetworkStatusWaiting;
}
}
- (void) stop
{
if (server)
{
self.networkStatus = kCCBNetworkStatusShutDown;
//[[[PlayerStatusLayer sharedInstance] lblInstructions] setString:kCCBPlayerStatusStringStop];
handle_ccb_stop();
[server stop];
[server release];
server = NULL;
[connectedClients removeAllObjects];
}
}
- (void) updatePairing
{
// Stop old server
[server stop];
[server release];
server = NULL;
[connectedClients removeAllObjects];
// Start new server
server = [[ThoMoServerStub alloc] initWithProtocolIdentifier:[self protocolIdentifier]];
[server setDelegate:self];
[server start];
self.networkStatus = kCCBNetworkStatusWaiting;
}
#pragma mark Helper methods
- (void) executeJavaScript:(NSString*)script
{
self.playerStatus = kCCBPlayerStatusExecuteScript;
// NSThread *cocos2dThread = [[CCDirector sharedDirector] runningThread];
//
// [cocos2dThread performBlock:^(void) {
// NSString * string = @"None\n";
// jsval out;
// BOOL success = [[JSBCore sharedInstance] evalString:script outVal:&out];
//
// if(success)
// {
// /*
// if(JSVAL_IS_BOOLEAN(out))
// {
// string = [NSString stringWithFormat:@"Result(bool): %@.\n", (JSVAL_TO_BOOLEAN(out)) ? @"true" : @"false"];
// }
// else if(JSVAL_IS_INT(out))
// {
// string = [NSString stringWithFormat:@"Result(int): %i.\n", JSVAL_TO_INT(out)];
// }
// else if(JSVAL_IS_DOUBLE(out))
// {
// string = [NSString stringWithFormat:@"Result(double): %d.\n", JSVAL_TO_DOUBLE(out)];
// }
// else if(JSVAL_IS_STRING(out)) {
// NSString *tmp;
// jsval_to_nsstring( [[ScriptingCore sharedInstance] globalContext], out, &tmp );
// string = [NSString stringWithFormat:@"Result(string): %d.\n", tmp];
// }
// else if (JSVAL_IS_VOID(out) )
// string = @"Result(void):\n";
// else if (JSVAL_IS_OBJECT(out) )
// string = @"Result(object):\n";
// */
// string = @"Success\n";
// }
// else
// {
// string = [NSString stringWithFormat:@"Error evaluating script:\n#############################\n%@\n#############################\n", script];
// }
//
// //[self sendResultString:string];
// }
// waitUntilDone:NO];
}
- (void) listDirectory:(NSString*)dir prefix:(NSString*)prefix
{
NSArray* files = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:dir error:NULL];
for (NSString* file in files)
{
NSLog(@"%@%@", prefix, file);
BOOL isDir = NO;
if ([[NSFileManager defaultManager] fileExistsAtPath:[dir stringByAppendingPathComponent:file] isDirectory:&isDir] && isDir)
{
[self listDirectory:[dir stringByAppendingPathComponent:file] prefix:[prefix stringByAppendingString:@" "]];
}
}
}
- (void) extractZipData:(NSData*)data
{
self.playerStatus = kCCBPlayerStatusUnzip;
id runBlock = ^(void) {
std::string path = cocos2d::CCFileUtils::sharedFileUtils()->getWritablePath();
NSString *writeablePath = [NSString stringWithCString:path.c_str() encoding:NSASCIIStringEncoding];
// NSArray *searchPaths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
NSString* dirPath = [writeablePath stringByAppendingPathComponent:@"ccb"];
NSString* zipPath = [dirPath stringByAppendingPathComponent:@"ccb.zip"];
BOOL isDirectory = NO;
if (![[NSFileManager defaultManager] fileExistsAtPath:dirPath isDirectory:&isDirectory])
{
[[NSFileManager defaultManager] createDirectoryAtPath:dirPath withIntermediateDirectories:YES attributes:NULL error:NULL];
}
if(![data writeToFile:zipPath atomically:YES])
{
NSLog(@"CocosPlayer: Failed to write zip file");
return;
}
if(![SSZipArchive unzipFileAtPath:zipPath toDestination:dirPath overwrite:YES password:NULL error:NULL]) {
NSLog(@"CocosPlayer: Failed to unzip resources");
}
[self listDirectory:dirPath prefix:@""];
// Send updated list of files on device
[self sendFileList];
};
double delayInSeconds = 0.01;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
dispatch_after(popTime, dispatch_get_main_queue(), runBlock);
}
-(void) stopJSApp
{
if( ! playerWindowDisplayed ) {
//[[AppController appController] stopJSApp];
playerWindowDisplayed = YES;
}
}
-(void) runJSApp
{
handle_ccb_run();
//[[AppController appController] runJSApp];
playerWindowDisplayed = NO;
}
- (void) stopMain
{
if( ! playerWindowDisplayed ) {
self.playerStatus = kCCBPlayerStatusStop;
// NSThread *cocos2dThread = [[CCDirector sharedDirector] runningThread];
//
// [cocos2dThread performBlock:^(void) {
// [self stopJSApp];
// } waitUntilDone:YES];
//
// Force update network status
handle_ccb_stop();
CCBNetworkStatus tmp = networkStatus;
networkStatus = kCCBNetworkUninitialized;
self.networkStatus = tmp;
}
}
- (void) runMain
{
double delayInSeconds = 0.05;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
self.playerStatus = kCCBPlayerStatusPlay;
[self runJSApp];
});
}
#pragma mark Server callbacks
- (void) server:(ThoMoServerStub *)theServer acceptedConnectionFromClient:(NSString *)aClientIdString
{
[connectedClients addObject:aClientIdString];
if (connectedClients.count == 1)
{
self.networkStatus = kCCBNetworkStatusConnected;
[self sendDeviceName];
[self sendFileList];
}
else
{
self.networkStatus = kCCBNetworkStatusTooMany;
}
}
- (void)server:(ThoMoServerStub *)theServer lostConnectionToClient:(NSString *)aClientIdString errorMessage:(NSString *)errorMessage
{
[connectedClients removeObject:aClientIdString];
if (connectedClients.count == 0)
{
self.networkStatus = kCCBNetworkStatusWaiting;
}
else if (connectedClients.count == 1)
{
self.networkStatus = kCCBNetworkStatusConnected;
}
else
{
self.networkStatus = kCCBNetworkStatusTooMany;
}
}
- (void)serverDidShutDown:(ThoMoServerStub *)theServer
{
self.networkStatus = kCCBNetworkStatusWaiting;
if( server == theServer || !server) {
[server release];
server = NULL;
[connectedClients removeAllObjects];
[self startIfNotStarted];
}
}
- (void)netServiceProblemEncountered:(NSString *)errorMessage onServer:(ThoMoServerStub *)theServer
{
if( server == theServer ) {
[server stop];
[server release];
server = NULL;
[connectedClients removeAllObjects];
}
}
- (void) server:(ThoMoServerStub *)theServer didReceiveData:(id)theData fromClient:(NSString *)aClientIdString
{
if (connectedClients.count != 1) return;
NSDictionary* msg = theData;
NSString* cmd = [msg objectForKey:@"cmd"];
if ([cmd isEqualToString:@"script"])
{
NSString* script = [msg objectForKey:@"script"];
[self executeJavaScript:script];
}
else if ([cmd isEqualToString:@"run"])
{
[self runMain];
}
else if ([cmd isEqualToString:@"stop"])
{
[self stopMain];
}
else if ([cmd isEqualToString:@"zip"])
{
NSData* zipData = [msg objectForKey:@"data"];
// Extract zip file received from CocosBuilder
[self extractZipData:zipData];
}
else if ([cmd isEqualToString:@"settings"])
{
NSArray* arr = [msg objectForKey:@"orientations"];
NSUInteger orientations = 0;
if ([[arr objectAtIndex:0] boolValue]) orientations |= UIInterfaceOrientationMaskPortrait;
if ([[arr objectAtIndex:1] boolValue]) orientations |= UIInterfaceOrientationMaskPortraitUpsideDown;
if ([[arr objectAtIndex:2] boolValue]) orientations |= UIInterfaceOrientationMaskLandscapeLeft;
if ([[arr objectAtIndex:3] boolValue]) orientations |= UIInterfaceOrientationMaskLandscapeRight;
//[AppController appController].deviceOrientations = orientations;
}
}
#pragma mark Server/Player status
-(void) setNetworkStatus:(CCBNetworkStatus)aNetworkStatus
{
if( networkStatus != aNetworkStatus ) {
networkStatus = aNetworkStatus;
//PlayerStatusLayer *statusLayer = [PlayerStatusLayer sharedInstance];
switch (networkStatus) {
case kCCBNetworkStatusConnected:
if( playerWindowDisplayed)
handle_set_status((const char *)[kCCBNetworkStatusStringConnected cStringUsingEncoding:NSASCIIStringEncoding]);
self.playerStatus = kCCBPlayerStatusIdle;
break;
case kCCBNetworkStatusShutDown:
if( playerWindowDisplayed)
handle_set_status((const char *)[kCCBNetworkStatusStringShutDown cStringUsingEncoding:NSASCIIStringEncoding]);
self.playerStatus = kCCBPlayerStatusNotConnected;
break;
case kCCBNetworkStatusTooMany:
if( playerWindowDisplayed)
handle_set_status((const char *)[kCCBNetworkStatusStringWaiting cStringUsingEncoding:NSASCIIStringEncoding]);
[self stopJSApp];
self.playerStatus = kCCBPlayerStatusNotConnected;
break;
case kCCBNetworkStatusWaiting:
if( playerWindowDisplayed)
handle_set_status((const char *)[kCCBNetworkStatusStringWaiting cStringUsingEncoding:NSASCIIStringEncoding]);
self.playerStatus = kCCBPlayerStatusNotConnected;
[self stopJSApp];
break;
default:
break;
}
}
}
-(void) setPlayerStatus:(CCBPlayerStatus)aPlayerStatus
{
if( playerStatus != aPlayerStatus) {
playerStatus = aPlayerStatus;
//PlayerStatusLayer *statusLayer = [PlayerStatusLayer sharedInstance];
switch (playerStatus) {
case kCCBPlayerStatusExecuteScript:
if( playerWindowDisplayed) {
handle_set_message((const char *)[kCCBPlayerStatusStringScript cStringUsingEncoding:NSASCIIStringEncoding]);
}
break;
case kCCBPlayerStatusPlay:
if( playerWindowDisplayed)
handle_set_message((const char *)[kCCBPlayerStatusStringPlay cStringUsingEncoding:NSASCIIStringEncoding]);
break;
case kCCBPlayerStatusStop:
if( playerWindowDisplayed)
handle_set_message((const char *)[kCCBPlayerStatusStringStop cStringUsingEncoding:NSASCIIStringEncoding]);
break;
case kCCBPlayerStatusUnzip:
if( playerWindowDisplayed)
handle_set_message((const char *)[kCCBPlayerStatusStringUnzip cStringUsingEncoding:NSASCIIStringEncoding]);
break;
case kCCBPlayerStatusIdle:
if( playerWindowDisplayed)
handle_set_message((const char *)[kCCBPlayerStatusStringIdle cStringUsingEncoding:NSASCIIStringEncoding]);
break;
case kCCBPlayerStatusNotConnected:
if( playerWindowDisplayed)
handle_set_message((const char *)[kCCBPlayerStatusStringNotConnected cStringUsingEncoding:NSASCIIStringEncoding]);
break;
default:
break;
}
}
}
#pragma mark Sending messages
- (void) sendMessage:(NSDictionary*) msg
{
if (connectedClients.count == 1)
{
[server sendToAllClients:msg];
}
}
- (NSString*) getUUID
{
// Check user defaults
NSString* uuid = [[NSUserDefaults standardUserDefaults] objectForKey:@"uuid"];
if (uuid) return uuid;
// Generate UUID
CFUUIDRef uuidObject = CFUUIDCreate(kCFAllocatorDefault);
uuid = (NSString *)CFUUIDCreateString(kCFAllocatorDefault, uuidObject);
CFRelease(uuidObject);
// Store generated id
[[NSUserDefaults standardUserDefaults] setObject:uuid forKey:@"uuid"];
return uuid;
}
- (void) sendDeviceName
{
NSMutableDictionary* msg = [NSMutableDictionary dictionary];
[msg setObject:@"deviceinfo" forKey:@"cmd"];
[msg setObject:[[UIDevice currentDevice] name] forKey:@"devicename"];
//[msg setObject:[AppController appController].deviceType forKey:@"devicetype"];
if(self.isIPhone)
{
[msg setObject:@"iPhone" forKey:@"devicetype"];
}
else
{
[msg setObject:@"iPad" forKey:@"devicetype"];
}
[msg setObject:[NSNumber numberWithBool:self.isRetina] forKey:@"retinadisplay"];
[msg setObject:[self getUUID] forKey:@"uuid"];
[self sendMessage:msg];
}
- (void) sendResultString:(NSString*) str
{
NSMutableDictionary* msg = [NSMutableDictionary dictionary];
[msg setObject:@"result" forKey:@"cmd"];
[msg setObject:str forKey:@"result"];
[self sendMessage:msg];
}
- (void) sendLog:(NSString*)log
{
NSMutableDictionary* msg = [NSMutableDictionary dictionary];
[msg setObject:@"log" forKey:@"cmd"];
[msg setObject:log forKey:@"string"];
[self sendMessage:msg];
}
- (void) sendFileList
{
// Get the contents of the CCB working directory, with files
CCBDirectoryComparer* dc = [[[CCBDirectoryComparer alloc] init] autorelease];
std::string path = cocos2d::CCFileUtils::sharedFileUtils()->getWritablePath();
NSString *writeablePath = [NSString stringWithCString:path.c_str() encoding:NSASCIIStringEncoding];
// NSArray *searchPaths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
NSString* dirPath = [writeablePath stringByAppendingPathComponent:@"ccb"];
// NSArray *searchPaths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
// NSString* dirPath = [[searchPaths objectAtIndex:0] stringByAppendingPathComponent:@"ccb"];
[dc loadDirectory:dirPath];
NSDictionary* dirFiles = dc.files;
// Send message
NSMutableDictionary* msg = [NSMutableDictionary dictionary];
[msg setObject:@"filelist" forKey:@"cmd"];
[msg setObject:dirFiles forKey:@"filelist"];
[self sendMessage:msg];
for (id key in msg) {
NSLog(@"key: %@, value: %@", key, [msg objectForKey:key]);
}
}
#pragma mark Common
- (void) dealloc
{
[server release];
[connectedClients release];
[super dealloc];
}
@end

View File

@ -0,0 +1,21 @@
//
// CCBDirectoryComparer.h
// CocosBuilder
//
// Created by Viktor Lidholt on 2/5/13.
//
//
#import <Foundation/Foundation.h>
@interface CCBDirectoryComparer : NSObject
{
}
@property (nonatomic,readonly) NSMutableDictionary* files;
- (void) loadDirectory: (NSString*) dir;
- (NSArray*) diffWithFiles:(NSDictionary*) diffFiles;
@end

View File

@ -0,0 +1,109 @@
//
// CCBDirectoryComparer.m
// CocosBuilder
//
// Created by Viktor Lidholt on 2/5/13.
//
//
#import "CCBDirectoryComparer.h"
@implementation CCBDirectoryComparer
- (id) init
{
self = [super init];
if (!self) return NULL;
_files = [[NSMutableDictionary dictionary] retain];
return self;
}
- (NSDate*) modificationDateForFile:(NSString*)file
{
NSDictionary* attr = [[NSFileManager defaultManager] attributesOfItemAtPath:file error:NULL];
return [attr objectForKey:NSFileModificationDate];
}
- (void) loadDirectory: (NSString*) dir subDir:(NSString*) subDir
{
NSFileManager* fm = [NSFileManager defaultManager];
NSString* absDir = dir;
if (subDir) absDir = [dir stringByAppendingPathComponent:subDir];
NSArray* contents = [fm contentsOfDirectoryAtPath:absDir error:NULL];
for (NSString* file in contents)
{
NSString* absFile = [absDir stringByAppendingPathComponent:file];
NSString* relFile = file;
if (subDir) relFile = [subDir stringByAppendingPathComponent:file];
BOOL isDir = NO;
if ([fm fileExistsAtPath:absFile isDirectory:&isDir])
{
if (isDir)
{
// Go down in sub directory
NSString* subSubDir = file;
if (subDir) subSubDir = [subDir stringByAppendingPathComponent:file];
[self loadDirectory:dir subDir:subSubDir];
}
else
{
// Add file
NSDate* date = [self modificationDateForFile:absFile];
[_files setObject:date forKey:relFile];
}
}
}
}
- (void) loadDirectory: (NSString*) dir
{
[_files removeAllObjects];
[self loadDirectory:dir subDir: NULL];
}
// More approximate comparison of dates
- (BOOL) isEqualDate:(NSDate*) d1 to:(NSDate*) d2
{
NSTimeInterval delta = fabs([d1 timeIntervalSinceDate:d2]);
return (delta < 2.0);
}
- (NSArray*) diffWithFiles:(NSDictionary*) diffFiles
{
NSMutableArray* diff = [NSMutableArray array];
for (NSString* file in _files)
{
NSDate* fileDate = [_files objectForKey:file];
NSDate* diffFileDate = [diffFiles objectForKey:file];
if (diffFileDate && [self isEqualDate:fileDate to:diffFileDate])
{
// Files are equal, there is no diff
continue;
}
// Add to list of modified files
[diff addObject:file];
NSLog(@"MOD: %@ src: %@ dst: %@", file, fileDate, diffFileDate);
}
return diff;
}
- (void) dealloc
{
[super dealloc];
[_files release];
}
@end

View File

@ -0,0 +1,99 @@
/*
* CocosBuilder: http://www.cocosbuilder.com
*
* Copyright (c) 2012 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 "ThoMoServerStub.h"
#import "cocos2d.h"
// Network status states
typedef enum {
kCCBNetworkUninitialized = -1,
kCCBNetworkStatusWaiting,
kCCBNetworkStatusTooMany,
kCCBNetworkStatusConnected,
kCCBNetworkStatusShutDown,
} CCBNetworkStatus;
// Player status states
typedef enum {
kCCBPlayerStatuskUninitialized = -1,
kCCBPlayerStatusIdle,
kCCBPlayerStatusPlay,
kCCBPlayerStatusUnzip,
kCCBPlayerStatusStop,
kCCBPlayerStatusNotConnected,
kCCBPlayerStatusExecuteScript,
} CCBPlayerStatus;
// Predefined messages
extern NSString *kCCBNetworkStatusStringWaiting;
extern NSString *kCCBNetworkStatusStringTooMany;
extern NSString *kCCBNetworkStatusStringConnected;
extern NSString *kCCBNetworkStatusStringShutDown;
extern NSString *kCCBPlayerStatusStringNotConnected;
extern NSString *kCCBPlayerStatusStringIdle;
extern NSString *kCCBPlayerStatusStringUnzip;
extern NSString *kCCBPlayerStatusStringStop;
extern NSString *kCCBPlayerStatusStringPlay;
extern NSString *kCCBPlayerStatusStringScript;
@interface ServerController : NSObject <ThoMoServerDelegateProtocol>
{
ThoMoServerStub* server;
NSMutableSet* connectedClients;
NSFileHandle* pipeReadHandle;
CCBNetworkStatus networkStatus;
CCBPlayerStatus playerStatus;
BOOL playerWindowDisplayed;
}
/** Network status: who is the network connection */
@property (nonatomic, readwrite) CCBNetworkStatus networkStatus;
/** State of the player: playing, unzipping, stopping, etc. */
@property (nonatomic, readwrite) CCBPlayerStatus playerStatus;
@property (nonatomic, readwrite) BOOL playerWindowDisplayed;
@property (nonatomic, readwrite) BOOL isRetina;
@property (nonatomic, readwrite) BOOL isIPhone;
@property (nonatomic,copy) NSString* serverStatus;
- (void) start;
- (void) stop;
- (void) setPairingCode: (NSString *)code;
- (void) startIfNotStarted;
- (void) updatePairing;
- (void) sendDeviceName;
- (void) sendResultString:(NSString*) str;
- (void) sendLog:(NSString*)log;
- (void) sendFileList;
@end

View File

@ -0,0 +1,607 @@
/*
* CocosBuilder: http://www.cocosbuilder.com
*
* Copyright (c) 2012 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 "ServerController.h"
#import "js_bindings_core.h"
#import "CCBReader.h"
#include "PlayerStatus.h"
#include "AppDelegate.h"
#import "CCBDirectoryComparer.h"
#import "SSZipArchive.h"
// Predefined messages
NSString *kCCBNetworkStatusStringWaiting = @"Waiting for connections";
NSString *kCCBNetworkStatusStringTooMany = @"Too many connections";
NSString *kCCBNetworkStatusStringConnected = @"Connected";
NSString *kCCBNetworkStatusStringShutDown = @"Server shut down";
NSString *kCCBPlayerStatusStringNotConnected = @"Connect by running CocosBuilder on the same local wireless network as CocosPlayer.\nIf multiple instances of CocosBuilder is run on the same network, use a unique pairing code.";
NSString *kCCBPlayerStatusStringIdle = @"Idle";
NSString *kCCBPlayerStatusStringUnzip = @"Action: Unzip game";
NSString *kCCBPlayerStatusStringStop = @"Action: Stop";
NSString *kCCBPlayerStatusStringPlay = @"Action: Run";
NSString *kCCBPlayerStatusStringScript = @"Action: Executing script";
@implementation ServerController
@synthesize networkStatus, playerStatus, playerWindowDisplayed, isRetina, isIPhone;
#pragma mark Initializers and setup
- (NSString*) protocolIdentifier
{
NSString* pairing = [[NSUserDefaults standardUserDefaults] objectForKey:@"pairing"];
if (pairing)
{
return [NSString stringWithFormat:@"CocosP-%@",pairing];
}
else
{
return @"CocosPlayer";
}
}
- (id) init
{
self = [super init];
if (!self) return NULL;
connectedClients = [[NSMutableSet alloc] init];
server = [[ThoMoServerStub alloc] initWithProtocolIdentifier:[self protocolIdentifier]];
[server setDelegate:self];
networkStatus = kCCBNetworkUninitialized;
playerStatus = kCCBPlayerStatuskUninitialized;
playerWindowDisplayed = YES;
return self;
}
#pragma mark Redirection of std out
- (void) redirectStdErr
{
NSPipe* pipe = [NSPipe pipe];
pipeReadHandle = [pipe fileHandleForReading];
[pipeReadHandle readInBackgroundAndNotify];
int err = dup2([[pipe fileHandleForWriting] fileDescriptor], STDERR_FILENO);
if (!err) NSLog(@"ConsoleWindow: Failed to redirect stderr");
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(readData:) name:NSFileHandleReadCompletionNotification object:pipeReadHandle];
[pipeReadHandle retain];
}
- (void) readData:(NSNotification*)notification
{
[pipeReadHandle readInBackgroundAndNotify] ;
NSString *str = [[NSString alloc] initWithData: [[notification userInfo] objectForKey: NSFileHandleNotificationDataItem] encoding: NSASCIIStringEncoding] ;
[self sendLog:str];
}
#pragma mark Control methods
- (void) start
{
if (server)
{
[server start];
// Redirect std out
[self redirectStdErr];
}
}
- (void) startIfNotStarted
{
if (!server)
{
server = [[ThoMoServerStub alloc] initWithProtocolIdentifier:[self protocolIdentifier]];
[server setDelegate:self];
[server start];
self.networkStatus = kCCBNetworkStatusWaiting;
}
}
- (void) stop
{
if (server)
{
self.networkStatus = kCCBNetworkStatusShutDown;
//[[[PlayerStatusLayer sharedInstance] lblInstructions] setString:kCCBPlayerStatusStringStop];
handle_ccb_stop();
[server stop];
[server release];
server = NULL;
[connectedClients removeAllObjects];
}
}
- (void) updatePairing
{
// Stop old server
[server stop];
[server release];
server = NULL;
[connectedClients removeAllObjects];
// Start new server
server = [[ThoMoServerStub alloc] initWithProtocolIdentifier:[self protocolIdentifier]];
[server setDelegate:self];
[server start];
self.networkStatus = kCCBNetworkStatusWaiting;
}
#pragma mark Helper methods
- (void) executeJavaScript:(NSString*)script
{
self.playerStatus = kCCBPlayerStatusExecuteScript;
// NSThread *cocos2dThread = [[CCDirector sharedDirector] runningThread];
//
// [cocos2dThread performBlock:^(void) {
// NSString * string = @"None\n";
// jsval out;
// BOOL success = [[JSBCore sharedInstance] evalString:script outVal:&out];
//
// if(success)
// {
// /*
// if(JSVAL_IS_BOOLEAN(out))
// {
// string = [NSString stringWithFormat:@"Result(bool): %@.\n", (JSVAL_TO_BOOLEAN(out)) ? @"true" : @"false"];
// }
// else if(JSVAL_IS_INT(out))
// {
// string = [NSString stringWithFormat:@"Result(int): %i.\n", JSVAL_TO_INT(out)];
// }
// else if(JSVAL_IS_DOUBLE(out))
// {
// string = [NSString stringWithFormat:@"Result(double): %d.\n", JSVAL_TO_DOUBLE(out)];
// }
// else if(JSVAL_IS_STRING(out)) {
// NSString *tmp;
// jsval_to_nsstring( [[ScriptingCore sharedInstance] globalContext], out, &tmp );
// string = [NSString stringWithFormat:@"Result(string): %d.\n", tmp];
// }
// else if (JSVAL_IS_VOID(out) )
// string = @"Result(void):\n";
// else if (JSVAL_IS_OBJECT(out) )
// string = @"Result(object):\n";
// */
// string = @"Success\n";
// }
// else
// {
// string = [NSString stringWithFormat:@"Error evaluating script:\n#############################\n%@\n#############################\n", script];
// }
//
// //[self sendResultString:string];
// }
// waitUntilDone:NO];
}
- (void) listDirectory:(NSString*)dir prefix:(NSString*)prefix
{
NSArray* files = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:dir error:NULL];
for (NSString* file in files)
{
NSLog(@"%@%@", prefix, file);
BOOL isDir = NO;
if ([[NSFileManager defaultManager] fileExistsAtPath:[dir stringByAppendingPathComponent:file] isDirectory:&isDir] && isDir)
{
[self listDirectory:[dir stringByAppendingPathComponent:file] prefix:[prefix stringByAppendingString:@" "]];
}
}
}
- (void) extractZipData:(NSData*)data
{
self.playerStatus = kCCBPlayerStatusUnzip;
id runBlock = ^(void) {
std::string path = cocos2d::CCFileUtils::sharedFileUtils()->getWritablePath();
NSString *writeablePath = [NSString stringWithCString:path.c_str() encoding:NSASCIIStringEncoding];
// NSArray *searchPaths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
NSString* dirPath = [writeablePath stringByAppendingPathComponent:@"ccb"];
NSString* zipPath = [dirPath stringByAppendingPathComponent:@"ccb.zip"];
BOOL isDirectory = NO;
if (![[NSFileManager defaultManager] fileExistsAtPath:dirPath isDirectory:&isDirectory])
{
[[NSFileManager defaultManager] createDirectoryAtPath:dirPath withIntermediateDirectories:YES attributes:NULL error:NULL];
}
if(![data writeToFile:zipPath atomically:YES])
{
NSLog(@"CocosPlayer: Failed to write zip file");
return;
}
if(![SSZipArchive unzipFileAtPath:zipPath toDestination:dirPath overwrite:YES password:NULL error:NULL]) {
NSLog(@"CocosPlayer: Failed to unzip resources");
}
[self listDirectory:dirPath prefix:@""];
// Send updated list of files on device
[self sendFileList];
};
double delayInSeconds = 0.01;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
dispatch_after(popTime, dispatch_get_main_queue(), runBlock);
}
-(void) stopJSApp
{
if( ! playerWindowDisplayed ) {
//[[AppController appController] stopJSApp];
playerWindowDisplayed = YES;
}
}
-(void) runJSApp
{
handle_ccb_run();
//[[AppController appController] runJSApp];
playerWindowDisplayed = NO;
}
- (void) stopMain
{
if( ! playerWindowDisplayed ) {
self.playerStatus = kCCBPlayerStatusStop;
// NSThread *cocos2dThread = [[CCDirector sharedDirector] runningThread];
//
// [cocos2dThread performBlock:^(void) {
// [self stopJSApp];
// } waitUntilDone:YES];
//
// Force update network status
handle_ccb_stop();
CCBNetworkStatus tmp = networkStatus;
networkStatus = kCCBNetworkUninitialized;
self.networkStatus = tmp;
}
}
- (void) runMain
{
double delayInSeconds = 0.05;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
self.playerStatus = kCCBPlayerStatusPlay;
[self runJSApp];
});
}
#pragma mark Server callbacks
- (void) server:(ThoMoServerStub *)theServer acceptedConnectionFromClient:(NSString *)aClientIdString
{
[connectedClients addObject:aClientIdString];
if (connectedClients.count == 1)
{
self.networkStatus = kCCBNetworkStatusConnected;
[self sendDeviceName];
[self sendFileList];
}
else
{
self.networkStatus = kCCBNetworkStatusTooMany;
}
}
- (void)server:(ThoMoServerStub *)theServer lostConnectionToClient:(NSString *)aClientIdString errorMessage:(NSString *)errorMessage
{
[connectedClients removeObject:aClientIdString];
if (connectedClients.count == 0)
{
self.networkStatus = kCCBNetworkStatusWaiting;
}
else if (connectedClients.count == 1)
{
self.networkStatus = kCCBNetworkStatusConnected;
}
else
{
self.networkStatus = kCCBNetworkStatusTooMany;
}
}
- (void)serverDidShutDown:(ThoMoServerStub *)theServer
{
self.networkStatus = kCCBNetworkStatusWaiting;
if( server == theServer || !server) {
[server release];
server = NULL;
[connectedClients removeAllObjects];
[self startIfNotStarted];
}
}
- (void)netServiceProblemEncountered:(NSString *)errorMessage onServer:(ThoMoServerStub *)theServer
{
if( server == theServer ) {
[server stop];
[server release];
server = NULL;
[connectedClients removeAllObjects];
}
}
- (void) server:(ThoMoServerStub *)theServer didReceiveData:(id)theData fromClient:(NSString *)aClientIdString
{
if (connectedClients.count != 1) return;
NSDictionary* msg = theData;
NSString* cmd = [msg objectForKey:@"cmd"];
if ([cmd isEqualToString:@"script"])
{
NSString* script = [msg objectForKey:@"script"];
[self executeJavaScript:script];
}
else if ([cmd isEqualToString:@"run"])
{
[self runMain];
}
else if ([cmd isEqualToString:@"stop"])
{
[self stopMain];
}
else if ([cmd isEqualToString:@"zip"])
{
NSData* zipData = [msg objectForKey:@"data"];
// Extract zip file received from CocosBuilder
[self extractZipData:zipData];
}
else if ([cmd isEqualToString:@"settings"])
{
NSArray* arr = [msg objectForKey:@"orientations"];
NSUInteger orientations = 0;
if ([[arr objectAtIndex:0] boolValue]) orientations |= UIInterfaceOrientationMaskPortrait;
if ([[arr objectAtIndex:1] boolValue]) orientations |= UIInterfaceOrientationMaskPortraitUpsideDown;
if ([[arr objectAtIndex:2] boolValue]) orientations |= UIInterfaceOrientationMaskLandscapeLeft;
if ([[arr objectAtIndex:3] boolValue]) orientations |= UIInterfaceOrientationMaskLandscapeRight;
//[AppController appController].deviceOrientations = orientations;
}
}
#pragma mark Server/Player status
-(void) setNetworkStatus:(CCBNetworkStatus)aNetworkStatus
{
if( networkStatus != aNetworkStatus ) {
networkStatus = aNetworkStatus;
//PlayerStatusLayer *statusLayer = [PlayerStatusLayer sharedInstance];
switch (networkStatus) {
case kCCBNetworkStatusConnected:
if( playerWindowDisplayed)
handle_set_status((const char *)[kCCBNetworkStatusStringConnected cStringUsingEncoding:NSASCIIStringEncoding]);
self.playerStatus = kCCBPlayerStatusIdle;
break;
case kCCBNetworkStatusShutDown:
if( playerWindowDisplayed)
handle_set_status((const char *)[kCCBNetworkStatusStringShutDown cStringUsingEncoding:NSASCIIStringEncoding]);
self.playerStatus = kCCBPlayerStatusNotConnected;
break;
case kCCBNetworkStatusTooMany:
if( playerWindowDisplayed)
handle_set_status((const char *)[kCCBNetworkStatusStringWaiting cStringUsingEncoding:NSASCIIStringEncoding]);
[self stopJSApp];
self.playerStatus = kCCBPlayerStatusNotConnected;
break;
case kCCBNetworkStatusWaiting:
if( playerWindowDisplayed)
handle_set_status((const char *)[kCCBNetworkStatusStringWaiting cStringUsingEncoding:NSASCIIStringEncoding]);
self.playerStatus = kCCBPlayerStatusNotConnected;
[self stopJSApp];
break;
default:
break;
}
}
}
-(void) setPlayerStatus:(CCBPlayerStatus)aPlayerStatus
{
if( playerStatus != aPlayerStatus) {
playerStatus = aPlayerStatus;
//PlayerStatusLayer *statusLayer = [PlayerStatusLayer sharedInstance];
switch (playerStatus) {
case kCCBPlayerStatusExecuteScript:
if( playerWindowDisplayed) {
handle_set_message((const char *)[kCCBPlayerStatusStringScript cStringUsingEncoding:NSASCIIStringEncoding]);
}
break;
case kCCBPlayerStatusPlay:
if( playerWindowDisplayed)
handle_set_message((const char *)[kCCBPlayerStatusStringPlay cStringUsingEncoding:NSASCIIStringEncoding]);
break;
case kCCBPlayerStatusStop:
if( playerWindowDisplayed)
handle_set_message((const char *)[kCCBPlayerStatusStringStop cStringUsingEncoding:NSASCIIStringEncoding]);
break;
case kCCBPlayerStatusUnzip:
if( playerWindowDisplayed)
handle_set_message((const char *)[kCCBPlayerStatusStringUnzip cStringUsingEncoding:NSASCIIStringEncoding]);
break;
case kCCBPlayerStatusIdle:
if( playerWindowDisplayed)
handle_set_message((const char *)[kCCBPlayerStatusStringIdle cStringUsingEncoding:NSASCIIStringEncoding]);
break;
case kCCBPlayerStatusNotConnected:
if( playerWindowDisplayed)
handle_set_message((const char *)[kCCBPlayerStatusStringNotConnected cStringUsingEncoding:NSASCIIStringEncoding]);
break;
default:
break;
}
}
}
#pragma mark Sending messages
- (void) sendMessage:(NSDictionary*) msg
{
if (connectedClients.count == 1)
{
[server sendToAllClients:msg];
}
}
- (NSString*) getUUID
{
// Check user defaults
NSString* uuid = [[NSUserDefaults standardUserDefaults] objectForKey:@"uuid"];
if (uuid) return uuid;
// Generate UUID
CFUUIDRef uuidObject = CFUUIDCreate(kCFAllocatorDefault);
uuid = (NSString *)CFUUIDCreateString(kCFAllocatorDefault, uuidObject);
CFRelease(uuidObject);
// Store generated id
[[NSUserDefaults standardUserDefaults] setObject:uuid forKey:@"uuid"];
return uuid;
}
- (void) sendDeviceName
{
NSMutableDictionary* msg = [NSMutableDictionary dictionary];
[msg setObject:@"deviceinfo" forKey:@"cmd"];
[msg setObject:[[UIDevice currentDevice] name] forKey:@"devicename"];
//[msg setObject:[AppController appController].deviceType forKey:@"devicetype"];
if(self.isIPhone)
{
[msg setObject:@"iPhone" forKey:@"devicetype"];
}
else
{
[msg setObject:@"iPad" forKey:@"devicetype"];
}
[msg setObject:[NSNumber numberWithBool:self.isRetina] forKey:@"retinadisplay"];
[msg setObject:[self getUUID] forKey:@"uuid"];
[self sendMessage:msg];
}
- (void) sendResultString:(NSString*) str
{
NSMutableDictionary* msg = [NSMutableDictionary dictionary];
[msg setObject:@"result" forKey:@"cmd"];
[msg setObject:str forKey:@"result"];
[self sendMessage:msg];
}
- (void) sendLog:(NSString*)log
{
NSMutableDictionary* msg = [NSMutableDictionary dictionary];
[msg setObject:@"log" forKey:@"cmd"];
[msg setObject:log forKey:@"string"];
[self sendMessage:msg];
}
- (void) sendFileList
{
// Get the contents of the CCB working directory, with files
CCBDirectoryComparer* dc = [[[CCBDirectoryComparer alloc] init] autorelease];
std::string path = cocos2d::CCFileUtils::sharedFileUtils()->getWritablePath();
NSString *writeablePath = [NSString stringWithCString:path.c_str() encoding:NSASCIIStringEncoding];
// NSArray *searchPaths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
NSString* dirPath = [writeablePath stringByAppendingPathComponent:@"ccb"];
// NSArray *searchPaths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
// NSString* dirPath = [[searchPaths objectAtIndex:0] stringByAppendingPathComponent:@"ccb"];
[dc loadDirectory:dirPath];
NSDictionary* dirFiles = dc.files;
// Send message
NSMutableDictionary* msg = [NSMutableDictionary dictionary];
[msg setObject:@"filelist" forKey:@"cmd"];
[msg setObject:dirFiles forKey:@"filelist"];
[self sendMessage:msg];
for (id key in msg) {
NSLog(@"key: %@, value: %@", key, [msg objectForKey:key]);
}
}
#pragma mark Common
- (void) dealloc
{
[server release];
[connectedClients release];
[super dealloc];
}
@end

View File

@ -0,0 +1,68 @@
/*
* ThoMoClientDelegateProtocol.h
* ThoMoNetworkingFramework
*
* Created by Thorsten Karrer on 6/8/09.
* Copyright 2010 media computing group - RWTH Aachen University.
*
* 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.
*
*/
@class ThoMoClientStub;
@protocol ThoMoClientDelegateProtocol <NSObject>
@optional
/// Connection notification (optional)
- (void)client:(ThoMoClientStub *)theClient didConnectToServer:(NSString *)aServerIdString;
/// Disconnection notification (optional)
- (void)client:(ThoMoClientStub *)theClient didDisconnectFromServer:(NSString *)aServerIdString errorMessage:(NSString *)errorMessage;
/// Bonjour problem notification (optional)
- (void)netServiceProblemEncountered:(NSString *)errorMessage onClient:(ThoMoClientStub *)theClient;
/// Client shutdown notification (optional)
- (void)clientDidShutDown:(ThoMoClientStub *)theClient;
@required
/// Data received notification (required)
/**
Sent to its delegate by the client stub on the main thread whenever it received data from a server.
\param[in] theClient The client stub that received the data.
\param[in] theData The object that was bein received.
\param[in] aServerIdString A string designating the server that sent the data.
*/
-(void)client:(ThoMoClientStub *)theClient didReceiveData:(id)theData fromServer:(NSString *)aServerIdString;
@end

View File

@ -0,0 +1,407 @@
/*
* ThoMoClientStub.m
* ThoMoNetworkingFramework
*
* Created by Thorsten Karrer on 29.6.09.
* Copyright 2010 media computing group - RWTH Aachen University.
*
* 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 "ThoMoClientStub_private.h"
#import "ThoMoTCPConnection.h"
// =====================================================================================================================
#pragma mark -
#pragma mark defines
// ---------------------------------------------------------------------------------------------------------------------
#define kThoMoNetworkInfoKeyServer kThoMoNetworkInfoKeyRemoteConnectionIdString
#define kThoMoNetworkInfoKeyClient kThoMoNetworkInfoKeyLocalNetworkStub
// =====================================================================================================================
// =====================================================================================================================
#pragma mark -
#pragma mark Class Extensions
// ---------------------------------------------------------------------------------------------------------------------
@interface ThoMoClientStub()
-(void)netServiceProblemRelayMethod:(NSDictionary *)infoDict;
-(void)didReceiveDataRelayMethod:(NSDictionary *)infoDict;
-(void)resolveNetService:(NSNetService *)theNetService;
@end
// =====================================================================================================================
// =====================================================================================================================
#pragma mark -
#pragma mark ThoMoClientStub implementation
// ---------------------------------------------------------------------------------------------------------------------
@implementation ThoMoClientStub
#pragma mark -
#pragma mark Properties
@synthesize delegate;
@synthesize offeredNetServices;
@synthesize connectedNetServices;
#pragma mark -
#pragma mark Housekeeping
-(id)initWithProtocolIdentifier:(NSString *)theProtocolIdentifier;
{
self = [super initWithProtocolIdentifier:theProtocolIdentifier];
if (self != nil) {
// add inits here
self.offeredNetServices = [[NSMutableArray alloc] init];
self.connectedNetServices = [[NSMutableDictionary alloc] init];
}
return self;
}
- (void) dealloc
{
[self stop];
self.offeredNetServices = nil;
self.connectedNetServices = nil;
[super dealloc];
}
#pragma mark -
#pragma mark Control
-(NSArray *)connectedServers;
{
return [super activeConnections];
}
-(void)send:(id<NSCoding>)anObject toServer:(NSString *)theServerIdString;
{
[super send:anObject toConnection:theServerIdString];
}
-(void)sendToAllServers:(id<NSCoding>)anObject;
{
for (NSString *aServer in [self connectedServers])
{
[self send:anObject toServer:aServer];
}
}
// DEPRECATED
-(void)sendData:(id<NSCoding>)theData toServer:(NSString *)theServerIdString;
{
[self send:theData toServer:theServerIdString];
}
// PRIVATE API
-(void)sendBytes:(NSData *)theBytes toServer:(NSString *)theServerIdString;
{
[super sendByteData:theBytes toConnection:theServerIdString];
}
#pragma mark -
#pragma mark Private Methods
// override
-(BOOL)setup;
{
if (![super setup])
return NO;
//start NSNetServiceBrowser
browser = [[NSNetServiceBrowser alloc] init];
[browser setDelegate:self];
[browser searchForServicesOfType:[NSString stringWithFormat:@"_%@._tcp.", protocolIdentifier] inDomain:@"local"];
return YES;
}
// override
-(void)teardown;
{
[browser stop];
[browser release];
browser = nil;
[super teardown];
}
// get a key string ("ip_address:port") for a resolved service
-(NSString *)keyStringFromResolvedService:(NSNetService *)theService;
{
NSData *addr = [[theService addresses] objectAtIndex:0];
NSMutableString *peerKey = [[[self keyStringFromAddress:addr] mutableCopy] autorelease];
return peerKey;
}
#pragma mark -
#pragma mark Delegate Methods
#pragma mark NSNetServiceBrowserDelegate
// notification when service was found
- (void) netServiceBrowser:(NSNetServiceBrowser*)browser didFindService:(NSNetService*)service moreComing:(BOOL)moreComing
{
NSLog(@"NetService %@ offered by %@", [service type], [service name]);
//need to hold a reference to the service
[self.offeredNetServices addObject:service];
//resolve net service
[service retain]; // retain - will be released after the resolve comes back!
[service setDelegate:self];
[service resolveWithTimeout:0.0];
}
//notification when service was removed
- (void) netServiceBrowser:(NSNetServiceBrowser*)browser didRemoveService:(NSNetService*)service moreComing:(BOOL)moreComing
{
NSLog(@"NetService %@ no longer offered by %@", [service type], [service name]);
// remove the service from the list
[self.offeredNetServices removeObject:service];
}
#pragma mark NSNetServiceDelegate
//notification when netservice was resolved
- (void)netServiceDidResolveAddress:(NSNetService *)sender
{
NSLog(@"Resolved Server Address %@", [sender hostName]);
// get a key string ("ip_address:port") for the service
NSString *key = [self keyStringFromResolvedService:sender];
NSAssert(key != nil, @"Could not create key string for resolved service");
// Tho 04.01.10: changed. Connections are only established if the service is not present in the connectedNetServices dict.
// This dict should now always contain exactly those services for which we have an open connection
if (![self.connectedNetServices objectForKey:key])
{
//connect to server and cache the NetService if the NetService can build us the I/O-Streams
NSInputStream *istream;
NSOutputStream *ostream;
// -[NSNetService getInputStream:outputStream:] currently returns the stream
// with a reference that we have to release (something that's counter to the
// standard Cocoa memory management rules <rdar://problem/6868813>).
BOOL gotStreams = [sender getInputStream:&istream outputStream:&ostream];
if (gotStreams)
{
[self openNewConnection:key inputStream:istream outputStream:ostream];
[istream release];
[ostream release];
[self.connectedNetServices setObject:sender forKey:key];
}
}
// we're done here, stop resolving
[sender stop];
[sender release];
}
- (void)netService:(NSNetService *)sender didNotResolve:(NSDictionary *)errorDict
{
NSString *errorString;
switch ([(NSNumber *)[errorDict objectForKey:NSNetServicesErrorCode] intValue])
{
case NSNetServicesBadArgumentError:
errorString = @"NSNetServicesBadArgumentError";
break;
case NSNetServicesCancelledError:
errorString = @"NSNetServicesCancelledError";
break;
case NSNetServicesCollisionError:
errorString = @"NSNetServicesCollisionError";
break;
case NSNetServicesInvalidError:
errorString = @"NSNetServicesInvalidError";
break;
case NSNetServicesNotFoundError:
errorString = @"NSNetServicesNotFoundError";
break;
case NSNetServicesTimeoutError:
errorString = @"NSNetServicesTimeoutError";
break;
case NSNetServicesUnknownError:
errorString = @"NSNetServicesUnknownError";
break;
default:
errorString = @"Unknown. Possibly an error on the Mach layer.";
break;
}
NSLog(@"Could not resolve service %@ because of error %@", sender, errorString);
// stop the resolve
[sender stop];
[sender release];
}
#pragma mark Connection Delegate Methods
// override
-(void)streamsDidOpenOnConnection:(ThoMoTCPConnection *)theConnection;
{
// let the super class issue the user notifications on the main thread
[super streamsDidOpenOnConnection:theConnection];
}
// override
-(void)streamEndEncountered:(NSStream *)theStream onConnection:(ThoMoTCPConnection *)theConnection;
{
NSString *connectionKey = [self keyForConnection:theConnection];
// this closes the connection, removes it from the connections array, and schedules the connectionLostRelayMethod selector on the main run loop
[super streamEndEncountered:theStream onConnection:theConnection];
// remove the connection's service from the connectedNetServices dict (it may still be contained in the offered array)
NSNetService *connectionNetService = [[self.connectedNetServices objectForKey:connectionKey] retain]; // retain - will be released after the resolve comes back!
[self.connectedNetServices removeObjectForKey:connectionKey];
// try to re-resolve the netService if it is still offered by Bonjour
if ([offeredNetServices containsObject:connectionNetService])
{
[self performSelector:@selector(resolveNetService:) withObject:connectionNetService afterDelay:1];
}
}
// override
-(void)streamErrorEncountered:(NSStream *)theStream onConnection:(ThoMoTCPConnection *)theConnection;
{
NSString *connectionKey = [self keyForConnection:theConnection];
// this closes the connection, removes it from the connections array, and schedules the connectionLostRelayMethod selector on the main run loop
[super streamErrorEncountered:theStream onConnection:theConnection];
// remove the connection's service from the connectedNetServices dict (it may still be contained in the offered array)
NSNetService *connectionNetService = [[self.connectedNetServices objectForKey:connectionKey] retain]; // retain - will be released after the resolve comes back!
[self.connectedNetServices removeObjectForKey:connectionKey];
// try to re-resolve the netService if it is still offered by Bonjour
if ([offeredNetServices containsObject:connectionNetService])
{
[self performSelector:@selector(resolveNetService:) withObject:connectionNetService afterDelay:1];
}
}
#pragma mark -
#pragma mark Hacking Relays
-(void)resolveNetService:(NSNetService *)theNetService;
{
[theNetService resolveWithTimeout:5.0];
}
#pragma mark -
#pragma mark Main Thread Relay Methods
// override
-(void)netWorkStubDidShutDownRelayMethod
{
if ([delegate respondsToSelector:@selector(clientDidShutDown:)])
[delegate clientDidShutDown:self];
}
// override
-(void)netServiceProblemRelayMethod:(NSDictionary *)infoDict
{
if ([delegate respondsToSelector:@selector(netServiceProblemEncountered:onClient:)])
[delegate netServiceProblemEncountered:[infoDict objectForKey:@"kThoMoTCPInfoKeyUserMessage"] onClient:self];
}
// required
// override
-(void)didReceiveDataRelayMethod:(NSDictionary *)infoDict;
{
[delegate client:[infoDict objectForKey:kThoMoNetworkInfoKeyClient]
didReceiveData:[infoDict objectForKey:kThoMoNetworkInfoKeyData]
fromServer:[infoDict objectForKey:kThoMoNetworkInfoKeyServer]];
}
// override
-(void)connectionEstablishedRelayMethod:(NSDictionary *)infoDict;
{
if ([delegate respondsToSelector:@selector(client:didConnectToServer:)])
[delegate client:[infoDict objectForKey:kThoMoNetworkInfoKeyClient]
didConnectToServer:[infoDict objectForKey:kThoMoNetworkInfoKeyServer]];
}
// override
-(void)connectionLostRelayMethod:(NSDictionary *)infoDict;
{
if ([delegate respondsToSelector:@selector(client:didDisconnectFromServer:errorMessage:)])
[delegate client:[infoDict objectForKey:kThoMoNetworkInfoKeyClient]
didDisconnectFromServer:[infoDict objectForKey:kThoMoNetworkInfoKeyServer]
errorMessage:[infoDict objectForKey:kThoMoNetworkInfoKeyUserMessage]];
}
// override
-(void)connectionClosedRelayMethod:(NSDictionary *)infoDict;
{
if ([delegate respondsToSelector:@selector(client:didDisconnectFromServer:errorMessage:)])
[delegate client:[infoDict objectForKey:kThoMoNetworkInfoKeyClient]
didDisconnectFromServer:[infoDict objectForKey:kThoMoNetworkInfoKeyServer]
errorMessage:[infoDict objectForKey:kThoMoNetworkInfoKeyUserMessage]];
}
@end

View File

@ -0,0 +1,57 @@
/*
* ThoMoClientStub.h
* ThoMoNetworkingFramework
*
* Created by Thorsten Karrer on 29.6.09.
* Copyright 2010 media computing group - RWTH Aachen University.
*
* 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 "ThoMoClientDelegateProtocol.h"
#import "ThoMoNetworkStub.h"
@interface ThoMoClientStub : ThoMoNetworkStub <NSNetServiceBrowserDelegate>
{
NSNetServiceBrowser *browser;
NSMutableArray *offeredNetServices; // offered
NSMutableDictionary *connectedNetServices; // connected
id<ThoMoClientDelegateProtocol> delegate;
}
@property (assign) id<ThoMoClientDelegateProtocol> delegate;
@property (retain) NSMutableArray *offeredNetServices;
@property (retain) NSMutableDictionary *connectedNetServices;
-(NSArray *)connectedServers;
-(void)sendData:(id<NSCoding>)theData toServer:(NSString *)theServerIdString DEPRECATED_ATTRIBUTE;
-(void)sendBytes:(NSData *)theBytes toServer:(NSString *)theServerIdString;
-(void)send:(id<NSCoding>)anObject toServer:(NSString *)theServerIdString;
-(void)sendToAllServers:(id<NSCoding>)anObject;
@end

View File

@ -0,0 +1,111 @@
/*
* ThoMoClientStub.h
* ThoMoNetworkingFramework
*
* Created by Thorsten Karrer on 28.9.09.
* Copyright 2010 media computing group - RWTH Aachen University.
*
* 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 "ThoMoClientDelegateProtocol.h"
@interface ThoMoClientStub : NSObject
{
}
/// Delegate accessor
/**
Accessor for the client stubs delegate. Must be compliant to ThoMoClientDelegateProtocol.
*/
@property (assign) id<ThoMoClientDelegateProtocol> delegate;
/// Designated initializer
/**
Initializes the Stub with a protocol identifier string. When started, this client stub will automatically connect to all server stubs (ThoMoServerStub) on the local network that use the same protocol identifier.
\param[in] theProtocolIdentifier A user-definable string of maximum 14 characters length. Determines which server stubs this client stub connects to.
\throws ThoMoInvalidArgumentException if theProtocolIdentifier exceeds 14 characters in length.
*/
-(id)initWithProtocolIdentifier:(NSString *)theProtocolIdentifier;
/// Starts the client stub
/**
Starts the client stub on a background thread. The stub will auto-connect to each server with the same protocol identifier. May be called only once during the lifetime of the client stub.
\throws ThoMoStubAlreadyRunningException if the client stub had already been started before.
*/
-(void) start;
/// Stops the client stub
/**
Stops the client stub and cancels the associated network thread. This will close all open connections. May be called only once during the lifetime of the client stub.
*/
-(void) stop;
/// Returns an array of the connected servers
/**
Returns an array of id-strings for all servers that the client stub is currently connected to. You can use these id-strings to send objects to any of the servers by calling -send:toServer:.
\returns an array of id-strings for all connected servers.
*/
-(NSArray *)connectedServers;
/// Sends an object to a connected server
/**
Sends anObject to the server designated by theServerIdString.
\note Note that actually a copy of the object is being created on the other side of the connection. Altering the internal state of the object on either side of the connection is not reflected on the other side!
\param[in] anObject An NSCoding compliant object that will be copied and re-created at the server stub.
\param[in] theServerIdString A string the server to send the object to. Must be taken from the array returned by connectedServers.
\throws ThoMoInvalidArgumentException if theServerIdString does not designate a server the client stub is connected to.
*/
-(void)send:(id<NSCoding>)anObject toServer:(NSString *)theServerIdString;
/// Sends an object to all connected servers
/**
Sends anObject to all servers in the array returned by connectedServers.
\note Note that actually a copy of the object is being created on the other sides of the connections. Altering the internal state of the object on either side of the connections is not reflected on the other sides!
\param[in] anObject An NSCoding compliant object that will be copied and re-created at the server stubs.
*/
-(void)sendToAllServers:(id<NSCoding>)anObject;
-(void)sendData:(id<NSCoding>)theData toServer:(NSString *)theServerIdString DEPRECATED_ATTRIBUTE;
@end

View File

@ -0,0 +1,77 @@
/*
* ThoMoNetworkStub.h
* ThoMoNetworkingFramework
*
* Created by Thorsten Karrer on 2.7.09.
* Copyright 2010 media computing group - RWTH Aachen University.
*
* 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 "ThoMoTCPConnectionDelegateProtocol.h"
/// Mixin class that encapsulates the network worker thread and the handling of ThoMoTCPConnections.
/**
You should never instantiate a ThoMoNetworkStub directly. Instead, you should use its subclasses ThoMoServerStub and ThoMoClientStub.
Note: the implementation of this class is Bonjour-agnostic and should remain so.
*/
@interface ThoMoNetworkStub : NSObject <ThoMoTCPConnectionDelegateProtocol, NSNetServiceDelegate>
{
NSMutableDictionary *connections;
NSString *protocolIdentifier;
NSThread *networkThread;
}
-(id)initWithProtocolIdentifier:(NSString *)theProtocolIdentifier;
-(void) start;
-(void) stop;
-(NSArray *)activeConnections;
-(void)send:(id<NSCoding>)theData toConnection:(NSString *)theConnectionIdString;
-(void)sendByteData:(NSData *)theData toConnection:(NSString *)theConnectionIdString;
// protected
-(BOOL)setup;
-(void)teardown;
-(NSString *)keyStringFromAddress:(NSData *)addr;
/// Returns the key for theConnection from the connections dictionary.
-(NSString *)keyForConnection:(ThoMoTCPConnection *)theConnection;
-(void) openNewConnection:(NSString *)theConnectionKey inputStream:(NSInputStream *)istr outputStream:(NSOutputStream *)ostr;
@end
NSString *const kThoMoNetworkInfoKeyUserMessage;
NSString *const kThoMoNetworkInfoKeyData;
NSString *const kThoMoNetworkInfoKeyRemoteConnectionIdString;
NSString *const kThoMoNetworkInfoKeyLocalNetworkStub;

View File

@ -0,0 +1,443 @@
/*
* ThoMoNetworkStub.m
* ThoMoNetworkingFramework
*
* Created by Thorsten Karrer on 2.7.09.
* Copyright 2010 media computing group - RWTH Aachen University.
*
* 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 "ThoMoNetworkStub.h"
#import <arpa/inet.h>
#import "ThoMoTCPConnection.h"
#import <pthread.h>
NSString *const kThoMoNetworkInfoKeyUserMessage = @"kThoMoNetworkInfoKeyUserMessage";
NSString *const kThoMoNetworkInfoKeyData = @"kThoMoNetworkInfoKeyData";
NSString *const kThoMoNetworkInfoKeyRemoteConnectionIdString = @"kThoMoNetworkInfoKeyRemoteConnectionIdString";
NSString *const kThoMoNetworkInfoKeyLocalNetworkStub = @"kThoMoNetworkInfoKeyLocalNetworkStub";
NSString *const kThoMoNetworkPrefScopeSpecifierKey = @"kThoMoNetworkPrefScopeSpecifierKey";
// interface category for main thread relay methods
@interface ThoMoNetworkStub (RelayMethods)
-(void)netServiceProblemRelayMethod:(NSDictionary *)infoDict;
-(void)didReceiveDataRelayMethod:(NSDictionary *)infoDict;
-(void)connectionEstablishedRelayMethod:(NSDictionary *)infoDict;
-(void)connectionLostRelayMethod:(NSDictionary *)infoDict;
-(void)connectionClosedRelayMethod:(NSDictionary *)infoDict;
@end
@interface ThoMoNetworkStub ()
-(void)sendDataWithInfoDict:(NSDictionary *)theInfoDict;
@end
// =====================================================================================================================
#pragma mark -
#pragma mark Public Methods
// ---------------------------------------------------------------------------------------------------------------------
@implementation ThoMoNetworkStub
#pragma mark Housekeeping
-(id)initWithProtocolIdentifier:(NSString *)theProtocolIdentifier;
{
self = [super init];
if (self != nil)
{
// check if there is a scope specifier present in the user defaults and add it to the protocolId
NSString *scopeSpecifier = [[NSUserDefaults standardUserDefaults] stringForKey:kThoMoNetworkPrefScopeSpecifierKey];
if (scopeSpecifier)
{
protocolIdentifier = [[scopeSpecifier stringByAppendingFormat:@"-%@", theProtocolIdentifier] retain];
NSLog(@"Warning: ThoMo Networking Protocol Prefix in effect! If your app cannot connect to its counterpart that may be the reason.");
}
else
{
protocolIdentifier = [theProtocolIdentifier copy];
}
if ([protocolIdentifier length] > 14)
{
// clean up internally
[NSException raise:@"ThoMoInvalidArgumentException"
format:@"The protocol identifier plus the optional scoping prefix (\"%@\") exceed"
" Bonjour's maximum allowed length of fourteen characters!", protocolIdentifier];
}
connections = [[NSMutableDictionary alloc] init];
}
return self;
}
-(id)init;
{
return [self initWithProtocolIdentifier:@"thoMoNetworkStubDefaultIdentifier"];
}
// since the networkThread retains our object while it executes, this method will be called after the thread is done
- (void) dealloc
{
[connections release];
[protocolIdentifier release];
[networkThread release];
[super dealloc];
}
// these methods are called on the main thread
#pragma mark Control
-(void) start;
{
if ([networkThread isExecuting])
{
[NSException raise:@"ThoMoStubAlreadyRunningException"
format:@"The client stub had already been started before and cannot be started twice."];
}
// TODO: check if we cannot run a start-stop-start cycle
NSAssert(networkThread == nil, @"Network thread not released properly");
networkThread = [[NSThread alloc] initWithTarget:self selector:@selector(networkThreadEntry) object:nil];
[networkThread start];
}
-(void) stop;
{
[networkThread cancel];
// TODO: check if we can release the networkthread here
}
-(NSArray *)activeConnections;
{
NSArray *result;
@synchronized(self)
{
result = [[connections allKeys] copy];
}
return [result autorelease];
}
-(void)send:(id<NSCoding>)theData toConnection:(NSString *)theConnectionIdString;
{
// NSData *sendData = [NSKeyedArchiver archivedDataWithRootObject:theData];
NSString *errorStr;
NSData *sendData = [NSPropertyListSerialization dataWithPropertyList:theData format:NSPropertyListBinaryFormat_v1_0 options:NULL error:&errorStr];
[self sendByteData:sendData toConnection:theConnectionIdString];
}
-(void)sendByteData:(NSData *)sendData toConnection:(NSString *)theConnectionIdString;
{
NSDictionary *infoDict = [NSDictionary dictionaryWithObjectsAndKeys:
sendData, @"DATA",
theConnectionIdString, @"ID",
nil];
[self performSelector:@selector(sendDataWithInfoDict:) onThread:networkThread withObject:infoDict waitUntilDone:NO];
}
#pragma mark Send Data Relay
// only call on network thread
-(void)sendDataWithInfoDict:(NSDictionary *)theInfoDict;
{
NSData *sendData = [theInfoDict objectForKey:@"DATA"];
NSString *theConnectionIdString = [theInfoDict objectForKey:@"ID"];
ThoMoTCPConnection *connection = nil;
@synchronized(self)
{
connection = [[connections valueForKey:theConnectionIdString] retain];
}
if (!connection)
{
[NSException raise:@"ThoMoInvalidArgumentException"
format:@"No connection found for id %@", theConnectionIdString];
}
else
{
[connection enqueueNextSendObject:sendData];
}
[connection release];
}
#pragma mark Threading Methods
-(void)networkThreadEntry
{
#ifdef DEBUG
#ifdef THOMO_PTHREAD_NAMING_AVAILABLE
pthread_setname_np("ThoMoNetworking Dispatch Thread");
#endif
#endif
NSAutoreleasePool *networkThreadPool = [[NSAutoreleasePool alloc] init];
if([self setup])
{
while (![networkThread isCancelled])
{
NSDate *inOneSecond = [[NSDate alloc] initWithTimeIntervalSinceNow:1];
// catch exceptions and propagate to main thread
@try {
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:inOneSecond];
}
@catch (NSException * e) {
[e performSelectorOnMainThread:@selector(raise) withObject:nil waitUntilDone:YES];
}
[inOneSecond release];
}
[self teardown];
}
[self performSelectorOnMainThread:@selector(networkStubDidShutDownRelayMethod) withObject:nil waitUntilDone:NO];
[networkThreadPool release];
}
#pragma mark -
#pragma mark Connection Delegate Methods
/// Delegate method that gets called from ThoMoTCPConnections whenever they did receive data.
/**
Takes the received data and relays it to a method on the main thread.
This method is typically overridden in the subclasses of ThoMoNetworkStub and then directly called from there.
\param[in] theData reference to the received data
\param[in] theConnection reference to the connection that received the data
*/
-(void)didReceiveData:(NSData *)theData onConnection:(ThoMoTCPConnection *)theConnection;
{
// look up the connection
NSString *connectionKey = [self keyForConnection:theConnection];
// unarchive the data
//id receivedData = [NSKeyedUnarchiver unarchiveObjectWithData:theData];
NSString *errorStr;
NSDictionary *receivedData = [NSPropertyListSerialization propertyListWithData:theData options:NULL format:NULL error:&errorStr];
NSLog(@"DICT: %@", [receivedData description]);
// package the parameters into an info dict and relay them to the main thread
NSDictionary *infoDict = [NSDictionary dictionaryWithObjectsAndKeys:
connectionKey, kThoMoNetworkInfoKeyRemoteConnectionIdString,
self, kThoMoNetworkInfoKeyLocalNetworkStub,
receivedData, kThoMoNetworkInfoKeyData,
nil];
[self performSelectorOnMainThread:@selector(didReceiveDataRelayMethod:) withObject:infoDict waitUntilDone:NO];
}
-(void)streamsDidOpenOnConnection:(ThoMoTCPConnection *)theConnection;
{
// look up the connection
NSString *connectionKey = [self keyForConnection:theConnection];
// package the parameters into an info dict and relay them to the main thread
NSDictionary *infoDict = [NSDictionary dictionaryWithObjectsAndKeys:
self, kThoMoNetworkInfoKeyLocalNetworkStub,
connectionKey, kThoMoNetworkInfoKeyRemoteConnectionIdString,
nil];
[self performSelectorOnMainThread:@selector(connectionEstablishedRelayMethod:) withObject:infoDict waitUntilDone:NO];
}
-(void)streamEndEncountered:(NSStream *)theStream onConnection:(ThoMoTCPConnection *)theConnection;
{
NSString *connectionKey = [self keyForConnection:theConnection];
NSString *userMessage = [NSString stringWithFormat:@"Stream end event encountered on stream to %@! Closing connection.", connectionKey];
[theConnection close];
@synchronized(self)
{
[connections removeObjectForKey:connectionKey];
}
NSDictionary *infoDict = [NSDictionary dictionaryWithObjectsAndKeys:
self, kThoMoNetworkInfoKeyLocalNetworkStub,
connectionKey, kThoMoNetworkInfoKeyRemoteConnectionIdString,
userMessage, kThoMoNetworkInfoKeyUserMessage,
nil];
[self performSelectorOnMainThread:@selector(connectionClosedRelayMethod:) withObject:infoDict waitUntilDone:NO];
}
-(void)streamErrorEncountered:(NSStream *)theStream onConnection:(ThoMoTCPConnection *)theConnection;
{
NSString *connectionKey = [self keyForConnection:theConnection];
NSError *theError = [theStream streamError];
NSString *userMessage = [NSString stringWithFormat:@"Error %i: \"%@\" on stream to %@! Terminating connection.", (int)[theError code], [theError localizedDescription], connectionKey];
[theConnection close];
@synchronized(self)
{
[connections removeObjectForKey:connectionKey];
}
NSDictionary *infoDict = [NSDictionary dictionaryWithObjectsAndKeys:
self, kThoMoNetworkInfoKeyLocalNetworkStub,
connectionKey, kThoMoNetworkInfoKeyRemoteConnectionIdString,
userMessage, kThoMoNetworkInfoKeyUserMessage,
nil];
[self performSelectorOnMainThread:@selector(connectionLostRelayMethod:) withObject:infoDict waitUntilDone:NO];
}
// =====================================================================================================================
#pragma mark -
#pragma mark Protected Methods
// ---------------------------------------------------------------------------------------------------------------------
-(BOOL)setup
{
return YES;
}
-(void)teardown
{
// close all open connections
@synchronized(self)
{
for (ThoMoTCPConnection *connection in [connections allValues]) {
[connection close];
}
[connections removeAllObjects];
}
}
-(NSString *)keyStringFromAddress:(NSData *)addr;
{
// get the peer socket address from the NSData object
// NOTE: there actually is a struct sockaddr in there, NOT a struct sockaddr_in!
// I heard from beej (<http://www.retran.com/beej/sockaddr_inman.html>) that they share the same 15 first bytes so casting should not be a problem.
// You've been warned, though...
struct sockaddr_in *peerSocketAddress = (struct sockaddr_in *)[addr bytes];
// convert in_addr to ascii (note: returns a pointer to a statically allocated buffer inside inet_ntoa! calling again will overwrite)
char *humanReadableAddress = inet_ntoa(peerSocketAddress->sin_addr);
NSString *peerAddress = [NSString stringWithCString:humanReadableAddress encoding:NSUTF8StringEncoding];
NSString *peerPort = [NSString stringWithFormat:@"%d", ntohs(peerSocketAddress->sin_port)];
NSString *peerKey = [NSString stringWithFormat:@"%@:%@", peerAddress, peerPort];
return peerKey;
}
-(NSString *)keyForConnection:(ThoMoTCPConnection *)theConnection;
{
NSString *connectionKey;
NSArray *keys;
@synchronized(self)
{
keys = [connections allKeysForObject:theConnection];
NSAssert([keys count] == 1, @"More than one connection record in dict for a single connection.");
connectionKey = [[keys objectAtIndex:0] copy];
}
return [connectionKey autorelease];
}
-(void) openNewConnection:(NSString *)theConnectionKey inputStream:(NSInputStream *)istr outputStream:(NSOutputStream *)ostr;
{
// create a new ThoMoTCPConnection object and set ourselves as the delegate to forward the incoming data to our own delegate
ThoMoTCPConnection *newConnection = [[ThoMoTCPConnection alloc] initWithDelegate:self inputStream:istr outputStream:ostr];
// store in our dictionary, open, and release our copy
@synchronized(self)
{
// it should never happen that we overwrite a connection
NSAssert(![connections valueForKey:theConnectionKey], @"ERROR: Tried to create a connection with an IP and port that we already have a connection for.");
[connections setValue:newConnection forKey:theConnectionKey];
}
[newConnection open];
[newConnection release];
}
@end
#pragma mark -
#pragma mark Main Thread Relay Methods
@implementation ThoMoNetworkStub (RelayMethods)
-(void)networkStubDidShutDownRelayMethod
{
NSLog(@"%@ :: %@", [self description], NSStringFromSelector(_cmd));
}
-(void)netServiceProblemRelayMethod:(NSDictionary *)infoDict
{
NSLog(@"%@ :: %@", [self description], NSStringFromSelector(_cmd));
}
-(void)didReceiveDataRelayMethod:(NSDictionary *)infoDict;
{
NSLog(@"%@ :: %@", [self description], NSStringFromSelector(_cmd));
}
-(void)connectionEstablishedRelayMethod:(NSDictionary *)infoDict;
{
NSLog(@"%@ :: %@", [self description], NSStringFromSelector(_cmd));
}
-(void)connectionLostRelayMethod:(NSDictionary *)infoDict;
{
NSLog(@"%@ :: %@", [self description], NSStringFromSelector(_cmd));
}
-(void)connectionClosedRelayMethod:(NSDictionary *)infoDict;
{
NSLog(@"%@ :: %@", [self description], NSStringFromSelector(_cmd));
}
#pragma mark -
#pragma mark Debugging
@end

View File

@ -0,0 +1,46 @@
/*
* ThoMoNetworking.h
* ThoMoNetworkingFramework
*
* Created by Thorsten Karrer on 16.3.10.
* Copyright 2010 media computing group - RWTH Aachen University.
*
* 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 <ThoMoNetworking/ThoMoClientStub.h>
#import <ThoMoNetworking/ThoMoServerStub.h>
#import <ThoMoNetworking/ThoMoClientDelegateProtocol.h>
#import <ThoMoNetworking/ThoMoServerDelegateProtocol.h>
/// Debugging pList key.
/**
If a string is set for this key in the current defaults domain the string will be prepended to the protocol id string.
This can be handy when a fully connected bipartite graph is not desirable, e.g., for debugging purposes.
\note Note that the prepended string plus a delimiter ('-') both count towards the length limit for protocol identifier strings of 14 characters!
*/
extern NSString *const kThoMoNetworkPrefScopeSpecifierKey;

View File

@ -0,0 +1,68 @@
/*
* ThoMoServerDelegateProtocol.h
* ThoMoNetworkingFramework
*
* Created by Thorsten Karrer on 6/8/09.
* Copyright 2010 media computing group - RWTH Aachen University.
*
* 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.
*
*/
@class ThoMoServerStub;
@protocol ThoMoServerDelegateProtocol <NSObject>
@optional
/// Connection notification (optional)
- (void)server:(ThoMoServerStub *)theServer acceptedConnectionFromClient:(NSString *)aClientIdString;
/// Disconnection notification (optional)
- (void)server:(ThoMoServerStub *)theServer lostConnectionToClient:(NSString *)aClientIdString errorMessage:(NSString *)errorMessage;
/// Bonjour problem notification (optional)
- (void)netServiceProblemEncountered:(NSString *)errorMessage onServer:(ThoMoServerStub *)theServer;
/// Client shutdown notification (optional)
- (void)serverDidShutDown:(ThoMoServerStub *)theServer;
@required
/// Data received notification (required)
/**
Sent to its delegate by the server stub on the main thread whenever it received data from a client.
\param[in] theServer The server stub that received the data.
\param[in] theData The object that was bein received.
\param[in] aClientIdString A string designating the client that sent the data.
*/
-(void)server:(ThoMoServerStub *)theServer didReceiveData:(id)theData fromClient:(NSString *)aClientIdString;
@end

View File

@ -0,0 +1,450 @@
/*
* ThoMoServerStub.m
* ThoMoNetworkingFramework
*
* Created by Thorsten Karrer on 29.6.09.
* Copyright 2010 media computing group - RWTH Aachen University.
*
* 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 "ThoMoServerStub_private.h"
#import <sys/socket.h>
#import <netinet/in.h>
#import <arpa/inet.h>
#import "ThoMoTCPConnection.h"
#ifdef THOMO_NEEDS_NETWORKING_INCLUDES
#import <unistd.h>
#import <CFNetwork/CFNetwork.h>
#endif
#define kThoMoNetworkInfoKeyServer kThoMoNetworkInfoKeyLocalNetworkStub
#define kThoMoNetworkInfoKeyClient kThoMoNetworkInfoKeyRemoteConnectionIdString
#define NO_INFO_RETAIN_CALLBACK NULL
#define NO_INFO_RELEASE_CALLBACK NULL
#define NO_INFO_COPY_DESCRIPTION_CALLBACK NULL
#define APPLE_DEFINED_ZERO 0
#define SOME_FREE_PORT 0
// =====================================================================================================================
#pragma mark -
#pragma mark Private Interfaces
#pragma mark -
// interface extension for 'private' properties
@interface ThoMoServerStub()
@property (nonatomic, retain) NSNetService *netService;
@property (assign) uint16_t listenPort;
@end
// interface category for private methods
@interface ThoMoServerStub(Private)
-(void) handleNewConnectionFromAddress:(NSData *)addr inputStream:(NSInputStream *)istr outputStream:(NSOutputStream *)ostr;
@end
// interface category for main thread relay methods
@interface ThoMoServerStub(RelayMethods)
-(void)netServiceProblemRelayMethod:(NSDictionary *)infoDict;
-(void)didReceiveDataRelayMethod:(NSDictionary *)infoDict;
-(void)clientDidConnectToServerRelayMethod:(NSDictionary *)infoDict;
-(void)clientDidDisconnectFromServerRelayMethod:(NSDictionary *)infoDict;
@end
// =====================================================================================================================
// =====================================================================================================================
#pragma mark -
#pragma mark ServerStub
#pragma mark -
@implementation ThoMoServerStub
#pragma mark Properties
@synthesize delegate;
#pragma mark Housekeeping
-(id)initWithProtocolIdentifier:(NSString *)theProtocolIdentifier andPort:(uint16_t)thePort;
{
self = [super initWithProtocolIdentifier:theProtocolIdentifier];
if (self != nil) {
// add inits here
self.listenPort = thePort;
}
return self;
}
-(id)initWithProtocolIdentifier:(NSString *)theProtocolIdentifier;
{
self = [self initWithProtocolIdentifier:theProtocolIdentifier andPort:SOME_FREE_PORT];
return self;
}
#pragma mark Control
-(NSArray *)connectedClients;
{
return [super activeConnections];
}
-(void)send:(id<NSCoding>)anObject toClient:(NSString *)theClientIdString;
{
[super send:anObject toConnection:theClientIdString];
}
-(void)sendToAllClients:(id<NSCoding>)theData;
{
for (NSString *aClientId in [self connectedClients])
{
// TODO: this might raise an exception - we should take care to catch and probably re-raise to have the data be sent to at least the rest of the connections
[self send:theData toClient:aClientId];
}
}
-(void)sendData:(id<NSCoding>)theData toClient:(NSString *)theClientIdString;
{
[self send:theData toClient:theClientIdString];
}
-(void)sendBytes:(NSData *)theBytes toClient:(NSString *)theClientIdString;
{
[super sendByteData:theBytes toConnection:theClientIdString];
}
// ---------------------------------------------------------------------------------------------------------------------
#pragma mark -
#pragma mark Private
// ---------------------------------------------------------------------------------------------------------------------
#pragma mark Properties
@synthesize netService;
@synthesize listenPort;
#pragma mark Callbacks
// This function is called by CFSocket when a new connection comes in at our listening socket.
static void ServerStubAcceptCallback(CFSocketRef listenSocket, CFSocketCallBackType callbackType, CFDataRef address, const void *pChildSocketNativeHandle, void *info)
{
// check if this is the right callback
if (callbackType == kCFSocketAcceptCallBack) {
// we have packaged up the server object in the info pointer
ThoMoServerStub *server = (ThoMoServerStub *)info;
// get the BSD child socket for the new connection
CFSocketNativeHandle childSocketNativeHandle = *(CFSocketNativeHandle *)pChildSocketNativeHandle;
// get the socket address of the peer that connected on our listening socket using the peer's name
uint8_t peerName[SOCK_MAXADDRLEN];
socklen_t namelength = sizeof(peerName);
NSData *peerSocketAddress = nil;
if (0 == getpeername(childSocketNativeHandle, (struct sockaddr *)peerName, &namelength))
peerSocketAddress = [NSData dataWithBytes:peerName length:namelength];
// create a pair of input and output streams on the child socket
CFReadStreamRef readStream = NULL;
CFWriteStreamRef writeStream = NULL;
CFStreamCreatePairWithSocket(kCFAllocatorDefault, childSocketNativeHandle, &readStream, &writeStream);
// set the stream properties to close the socket when we're done with the streams
// announce the streams and peer address to the server object (remember, this is just a C callback)
if (readStream && writeStream)
{
CFReadStreamSetProperty(readStream, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue);
CFWriteStreamSetProperty(writeStream, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue);
[server handleNewConnectionFromAddress:peerSocketAddress inputStream:(NSInputStream *)readStream outputStream:(NSOutputStream *)writeStream];
}
else
{
// on any failure, need to destroy the CFSocketNativeHandle
// since we are not going to use it any more
close(childSocketNativeHandle);
}
// clean up
if (readStream) CFRelease(readStream);
if (writeStream) CFRelease(writeStream);
}
}
#pragma mark Methods
-(void) handleNewConnectionFromAddress:(NSData *)addr inputStream:(NSInputStream *)istr outputStream:(NSOutputStream *)ostr;
{
// first convert addr to a key string of format "IP-Address:Port"
// the whole retain thing is because of all the threads bouncing around
NSString *connectionKey = [[[self keyStringFromAddress:addr] retain] autorelease];
// now let the superclass create, open, and register a new ThoMoTCPConnection object
[super openNewConnection:connectionKey inputStream:istr outputStream:ostr];
}
-(BOOL)setup;
{
if (![super setup])
return NO;
// ----------- SOCKET STUFF -----------
// create socket context. we pass the server object as the info pointer to access it later from the callbacks
CFSocketContext socketContext = {APPLE_DEFINED_ZERO, self, NO_INFO_RETAIN_CALLBACK, NO_INFO_RELEASE_CALLBACK, NO_INFO_COPY_DESCRIPTION_CALLBACK};
// create the socket we will use to listen for incoming connections. Here, we can directly install an auto-accepting callback on the socket - handy!
listenSocket = CFSocketCreate(kCFAllocatorDefault, PF_INET, SOCK_STREAM, IPPROTO_TCP, kCFSocketAcceptCallBack, (CFSocketCallBack)&ServerStubAcceptCallback, &socketContext);
if (NULL == listenSocket)
//TODO: raise exception etc...
return NO;
// set socket options to reuse socket if the connection breaks
int yes = 1;
setsockopt(CFSocketGetNative(listenSocket), SOL_SOCKET, SO_REUSEADDR, (void *)&yes, sizeof(yes));
// fill in the address structure we will use to bind the socket (let the kernel choose the port automatically)
struct sockaddr_in listenSocketAddress;
memset(&listenSocketAddress, 0, sizeof(listenSocketAddress));
listenSocketAddress.sin_len = sizeof(listenSocketAddress);
listenSocketAddress.sin_family = AF_INET;
listenSocketAddress.sin_port = htons(self.listenPort); // 0 means some free port
listenSocketAddress.sin_addr.s_addr = htonl(INADDR_ANY); // our own address
NSData *listenSocketAddressData = [NSData dataWithBytes:&listenSocketAddress length:sizeof(listenSocketAddress)];
// bind & listen (in cocoa this is done via the CFSocketSetAddress call)
if (kCFSocketSuccess != CFSocketSetAddress(listenSocket, (CFDataRef)listenSocketAddressData))
{
if (listenSocket) CFRelease(listenSocket);
listenSocket = NULL;
//TODO: raise exception etc...
return NO;
}
// get the port number the kernel chose for us (we need it for bonjour)
listenSocketAddressData = [(NSData *)CFSocketCopyAddress(listenSocket) autorelease];
memcpy(&listenSocketAddress, [listenSocketAddressData bytes], [listenSocketAddressData length]);
self.listenPort = ntohs(listenSocketAddress.sin_port);
// create a RunLoopSource from the socket
CFRunLoopRef runLoop = CFRunLoopGetCurrent();
CFRunLoopSourceRef listenSocketSource = CFSocketCreateRunLoopSource(kCFAllocatorDefault, listenSocket, 0);
// add it to the runloop
CFRunLoopAddSource(runLoop, listenSocketSource, kCFRunLoopCommonModes);
CFRelease(listenSocketSource);
// ----------- BONJOUR STUFF -----------
// use default name and local domain for our bonjour service
NSString *domain = @"local.";
NSString *name = @"";
// The Bonjour application protocol, which must:
// 1) be no longer than 14 characters
// 2) contain only lower-case letters, digits, and hyphens
// 3) begin and end with lower-case letter or digit
// It should also be descriptive and human-readable
// See the following for more information:
// http://developer.apple.com/networking/bonjour/faq.html
NSString *protocol = [NSString stringWithFormat:@"_%@._tcp.", protocolIdentifier];
// create our service object which we want to publish
self.netService = [[[NSNetService alloc] initWithDomain:domain type:protocol name:name port:self.listenPort] autorelease];
if(nil == self.netService) {
if (listenSocket) CFRelease(listenSocket);
listenSocket = NULL;
//TODO: raise exception etc...
return NO;
}
// register ourselves as delegate so we know if the publishing did work
[self.netService setDelegate:self];
// publish the service
[self.netService scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
[self.netService publish];
return YES;
}
-(void)teardown
{
// disable our bonjour service
if (self.netService) {
[self.netService stop];
[self.netService removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
}
self.netService = nil;
// invalidate the socket (also removes it from the run loop and releases the socket context structure)
if (listenSocket) {
CFSocketInvalidate(listenSocket);
}
// release the socket
if (listenSocket) {
CFRelease(listenSocket);
listenSocket = NULL;
}
[super teardown];
}
// ---------------------------------------------------------------------------------------------------------------------
#pragma mark -
#pragma mark Delegate Methods
// ---------------------------------------------------------------------------------------------------------------------
#pragma mark Bonjour
- (void)netServiceWillPublish:(NSNetService *)sender;
{
NSLog(@"Netservice is about to be published.");
}
- (void)netService:(NSNetService *)sender didNotPublish:(NSDictionary *)errorDict;
{
if([sender isEqual:netService])
{
NSString *userMessage = [NSString stringWithFormat:@"Our Netservice did not publish. Error %@", [errorDict objectForKey:NSNetServicesErrorCode]];
NSDictionary *infoDict = [NSDictionary dictionaryWithObjectsAndKeys:
self, kThoMoNetworkInfoKeyServer,
userMessage, kThoMoNetworkInfoKeyUserMessage,
nil];
[self performSelectorOnMainThread:@selector(netServiceProblemRelayMethod:) withObject:infoDict waitUntilDone:NO];
}
}
- (void)netServiceDidPublish:(NSNetService *)sender;
{
NSLog(@"Netservice has been published.");
}
- (void)netServiceDidStop:(NSNetService *)sender;
{
if([sender isEqual:netService])
{
NSString *userMessage = [NSString stringWithFormat:@"Our Netservice did stop!"];
NSDictionary *infoDict = [NSDictionary dictionaryWithObjectsAndKeys:
self, kThoMoNetworkInfoKeyServer,
userMessage, kThoMoNetworkInfoKeyUserMessage,
nil];
[self performSelectorOnMainThread:@selector(netServiceProblemRelayMethod:) withObject:infoDict waitUntilDone:NO];
}
}
// ---------------------------------------------------------------------------------------------------------------------
#pragma mark -
#pragma mark Main Thread Relay Methods
// ---------------------------------------------------------------------------------------------------------------------
-(void)networkStubDidShutDownRelayMethod
{
if ([delegate respondsToSelector:@selector(serverDidShutDown:)])
[delegate serverDidShutDown:self];
}
-(void)netServiceProblemRelayMethod:(NSDictionary *)infoDict
{
if ([delegate respondsToSelector:@selector(netServiceProblemEncountered:onServer:)])
[delegate netServiceProblemEncountered:[infoDict objectForKey:kThoMoNetworkInfoKeyUserMessage] onServer:self];
}
// required
-(void)didReceiveDataRelayMethod:(NSDictionary *)infoDict;
{
[delegate server:[infoDict objectForKey:kThoMoNetworkInfoKeyServer]
didReceiveData:[infoDict objectForKey:kThoMoNetworkInfoKeyData]
fromClient:[infoDict objectForKey:kThoMoNetworkInfoKeyClient]];
}
-(void)connectionEstablishedRelayMethod:(NSDictionary *)infoDict;
{
if ([delegate respondsToSelector:@selector(server:acceptedConnectionFromClient:)])
[delegate server:[infoDict objectForKey:kThoMoNetworkInfoKeyServer] acceptedConnectionFromClient:[infoDict objectForKey:kThoMoNetworkInfoKeyClient]];
}
-(void)connectionLostRelayMethod:(NSDictionary *)infoDict;
{
if ([delegate respondsToSelector:@selector(server:lostConnectionToClient:errorMessage:)])
[delegate server:[infoDict objectForKey:kThoMoNetworkInfoKeyServer]
lostConnectionToClient:[infoDict objectForKey:kThoMoNetworkInfoKeyClient]
errorMessage:[infoDict objectForKey:kThoMoNetworkInfoKeyUserMessage]];
}
-(void)connectionClosedRelayMethod:(NSDictionary *)infoDict;
{
if ([delegate respondsToSelector:@selector(server:lostConnectionToClient:errorMessage:)])
[delegate server:[infoDict objectForKey:kThoMoNetworkInfoKeyServer]
lostConnectionToClient:[infoDict objectForKey:kThoMoNetworkInfoKeyClient]
errorMessage:[infoDict objectForKey:kThoMoNetworkInfoKeyUserMessage]];
}
// ---------------------------------------------------------------------------------------------------------------------
#pragma mark -
#pragma mark Debugging
// ---------------------------------------------------------------------------------------------------------------------
// empty
@end

View File

@ -0,0 +1,54 @@
/*
* ThoMoServerStub.h
* ThoMoNetworkingFramework
*
* Created by Thorsten Karrer on 29.6.09.
* Copyright 2010 media computing group - RWTH Aachen University.
*
* 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 "ThoMoServerDelegateProtocol.h"
#import "ThoMoNetworkStub.h"
@interface ThoMoServerStub : ThoMoNetworkStub {
id<ThoMoServerDelegateProtocol> delegate;
uint16_t listenPort;
CFSocketRef listenSocket;
NSNetService *netService;
}
@property (assign) id<ThoMoServerDelegateProtocol> delegate;
-(NSArray *)connectedClients;
-(void)sendData:(id<NSCoding>)theData toClient:(NSString *)theClientIdString DEPRECATED_ATTRIBUTE;
-(void)sendBytes:(NSData *)theBytes toClient:(NSString *)theClientIdString;
-(void)send:(id<NSCoding>)anObject toClient:(NSString *)theClientIdString;
-(void)sendToAllClients:(id<NSCoding>)theData;
@end

View File

@ -0,0 +1,111 @@
/*
* ThoMoServerStub.h
* ThoMoNetworkingFramework
*
* Created by Thorsten Karrer on 28.9.09.
* Copyright 2010 media computing group - RWTH Aachen University.
*
* 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 "ThoMoServerDelegateProtocol.h"
@interface ThoMoServerStub : NSObject
{
}
/// Delegate accessor
/**
Accessor for the server stubs delegate. Must be compliant to ThoMoServerDelegateProtocol.
*/
@property (assign) id<ThoMoServerDelegateProtocol> delegate;
/// Designated initializer
/**
Initializes the Stub with a protocol identifier string. When started, this server stub will automatically connect to all client stubs (ThoMoClientStub) on the local network that use the same protocol identifier. The server stub will listen on a random free port for client connections.
\param[in] theProtocolIdentifier A user-definable string of maximum 14 characters length. Determines which client stubs this server stub connects to.
\throws ThoMoInvalidArgumentException if theProtocolIdentifier exceeds 14 characters in length.
*/
-(id)initWithProtocolIdentifier:(NSString *)theProtocolIdentifier;
/// Starts the server stub
/**
Starts the server stub on a background thread. The stub will auto-connect to each client with the same protocol identifier. May be called only once during the lifetime of the stub.
\throws ThoMoStubAlreadyRunningException if the server stub had already been started before.
*/
-(void) start;
/// Stops the server stub
/**
Stops the server stub and cancels the associated network thread. This will close all open connections. May be called only once during the lifetime of the stub.
*/
-(void) stop;
/// Returns an array of the connected clients
/**
Returns an array of id-strings for all clients that are currently connected to the server stub. You can use these id-strings to send objects to any of the clients by calling -send:toClient:.
\returns an array of id-strings for all connected clients.
*/
-(NSArray *)connectedClients;
/// Sends an object to a connected client
/**
Sends anObject to the client designated by theClientIdString.
\note Note that actually a copy of the object is being created on the other side of the connection. Altering the internal state of the object on either side of the connection is not reflected on the other side!
\param[in] anObject An NSCoding compliant object that will be copied and re-created at the client stub.
\param[in] theServerIdString A string the client to send the object to. Must be taken from the array returned by connectedClients.
\throws ThoMoInvalidArgumentException if theServerIdString does not designate a client the stub is connected to.
*/
-(void)send:(id<NSCoding>)anObject toClient:(NSString *)theClientIdString;
/// Sends an object to all connected clients
/**
Sends anObject to all clients in the array returned by connectedClients.
\note Note that actually a copy of the object is being created on the other sides of the connections. Altering the internal state of the object on either side of the connections is not reflected on the other sides!
\param[in] anObject An NSCoding compliant object that will be copied and re-created at the client stubs.
*/
-(void)sendToAllClients:(id<NSCoding>)theData;
-(void)sendData:(id<NSCoding>)theData toClient:(NSString *)theClientIdString DEPRECATED_ATTRIBUTE;
@end

View File

@ -0,0 +1,88 @@
/*
* ThoMoTCPConnection.h
* ThoMoNetworkingFramework
*
* Created by Thorsten Karrer on 30.6.09.
* Copyright 2010 media computing group - RWTH Aachen University.
*
* 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 "ThoMoTCPConnectionDelegateProtocol.h"
typedef enum ServerStubSubPackets {
kServerStubSubPacketHeader,
kServerStubSubPacketData,
kServerStubSubPacketCount
} ServerStubSubPacket;
@class ThoMoServerStub;
@interface ThoMoTCPConnection : NSObject <NSStreamDelegate>
{
id <ThoMoTCPConnectionDelegateProtocol> delegate;
NSInputStream *inStream;
NSOutputStream *outStream;
BOOL inReady;
BOOL outReady;
BOOL streamsCanOpen;
BOOL openCallbackSent;
// receiving
ServerStubSubPacket nextExpectedSubpacket;
uint32_t bytesMissingForNextSubpacket;
uint8_t *dataBuffer;
uint8_t *dataBufferCursor;
//sending
NSMutableArray *sendObjectsQueue;
NSMutableData *currentSendObject;
uint32_t bytesRemainingToSend;
uint8_t *sendBuffer;
uint8_t *sendBufferCursor;
BOOL outStreamHasSpaceAvailableEventIgnored;
//keepalive
NSTimer *keepaliveSendTimer;
// reentrancy tracking
BOOL threadIsPresentInMethod;
}
@property (assign) id <ThoMoTCPConnectionDelegateProtocol> delegate;
-(id)initWithDelegate:(id <ThoMoTCPConnectionDelegateProtocol>)theDelegate inputStream:(NSInputStream *)theInStream outputStream:(NSOutputStream *)theOutStream;
-(void)open;
-(void)close;
-(void)setupKeepalive;
-(void)teardownKeepalive;
-(void)enqueueNextSendObject:(NSData *)theData;
@end

View File

@ -0,0 +1,386 @@
/*
* ThoMoTCPConnection.m
* ThoMoNetworkingFramework
*
* Created by Thorsten Karrer on 30.6.09.
* Copyright 2010 media computing group - RWTH Aachen University.
*
* 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 "ThoMoTCPConnection.h"
#define HEADERSIZE 4
#define CHUNKSIZE 1024
@implementation ThoMoTCPConnection
@synthesize delegate;
- (id) init
{
return [self initWithDelegate:nil inputStream:nil outputStream:nil];
}
-(id)initWithDelegate:(id <ThoMoTCPConnectionDelegateProtocol>)theDelegate inputStream:(NSInputStream *)theInStream outputStream:(NSOutputStream *)theOutStream;
{
self = [super init];
if (self != nil) {
if (theDelegate && theInStream && theOutStream)
{
inReady = NO;
outReady = NO;
streamsCanOpen = YES;
openCallbackSent = NO;
dataBuffer = (uint8_t *)malloc(HEADERSIZE);
dataBufferCursor = dataBuffer;
bytesMissingForNextSubpacket = HEADERSIZE;
nextExpectedSubpacket = kServerStubSubPacketHeader;
bytesRemainingToSend = 0;
sendObjectsQueue = [[NSMutableArray alloc] initWithCapacity:5];
currentSendObject = nil;
inStream = [theInStream retain];
outStream = [theOutStream retain];
delegate = theDelegate; // do not retain the delegate
threadIsPresentInMethod = NO;
}
else
{
[self release];
self = nil;
}
}
return self;
}
- (void) dealloc
{
[self close];
[inStream release];
[outStream release];
[sendObjectsQueue release];
free(dataBuffer);
if (sendBuffer)
free(sendBuffer);
[super dealloc];
}
-(void)open;
{
NSAssert(streamsCanOpen, @"Tried to re-open a stream that has already been open at some point - not possible.");
[inStream setDelegate:self];
[inStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[inStream open];
[outStream setDelegate:self];
[outStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[outStream open];
[self setupKeepalive];
streamsCanOpen = NO;
}
-(void)close;
{
[self teardownKeepalive];
// close should also remove it from the runloop - somehow it does not release the runLoop's reference though
[inStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[inStream close];
inReady = NO;
// close should also remove it from the runloop - somehow it does not release the runLoop's reference though
[outStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[outStream close];
outReady = NO;
}
-(void)setupKeepalive;
{
keepaliveSendTimer = [[NSTimer scheduledTimerWithTimeInterval:2 target:self selector:@selector(sendKeepalive:) userInfo:nil repeats:YES] retain];
}
-(void)sendKeepalive:(NSTimer *)theTimer;
{
NSData *nullData = [[NSData dataWithBytes:NULL length:0] retain];
[self enqueueNextSendObject:nullData];
[nullData release];
}
-(void)teardownKeepalive;
{
if (keepaliveSendTimer) {
[keepaliveSendTimer invalidate];
[keepaliveSendTimer release];
keepaliveSendTimer = nil;
}
}
-(void)processBufferAndPrepNextRead
{
switch (nextExpectedSubpacket)
{
case kServerStubSubPacketHeader:
// parse header and update status
NSAssert(dataBufferCursor == &(dataBuffer[HEADERSIZE]), @"Header expected but wrong number of bytes read into buffer!");
// ok, because HEADERSIZE == sizeof(bytesMissingForNextSubpacket)
memcpy(&bytesMissingForNextSubpacket, dataBuffer, HEADERSIZE);
uint32_t tmp = ntohl(bytesMissingForNextSubpacket);
bytesMissingForNextSubpacket = tmp;
//if this is just a keepalive packet it only contains of a header and we can expect another header
if (bytesMissingForNextSubpacket == 0) {
bytesMissingForNextSubpacket = HEADERSIZE;
nextExpectedSubpacket = kServerStubSubPacketHeader;
}
else {
nextExpectedSubpacket = kServerStubSubPacketData;
}
break;
case kServerStubSubPacketData:
{
// parse data and update status
NSData *packetData = [[NSData alloc] initWithBytes:dataBuffer length:(dataBufferCursor - dataBuffer)];
[delegate didReceiveData:packetData onConnection:self];
[packetData release];
bytesMissingForNextSubpacket = HEADERSIZE;
nextExpectedSubpacket = kServerStubSubPacketHeader;
break;
}
default:
NSAssert(NO, @"Packet to process is neither header nor payload");
break;
}
// create new receive buffer
free(dataBuffer);
dataBuffer = (uint8_t *)malloc(bytesMissingForNextSubpacket);
// reset cursor
dataBufferCursor = dataBuffer;
}
-(void)dequeueNextSendObject
{
NSAssert(bytesRemainingToSend == 0, @"Enqueue next send object called although there are still bytes to be sent off");
if (sendBuffer) {
free(sendBuffer);
sendBuffer = NULL;
}
if ([sendObjectsQueue count] > 0)
{
currentSendObject = [[sendObjectsQueue lastObject] retain];
[sendObjectsQueue removeLastObject];
// create new send buffer
uint32_t objectSize = [currentSendObject length];
uint32_t packetSize = objectSize + HEADERSIZE;
sendBuffer = (uint8_t *)malloc(packetSize);
// fill it
uint32_t objectSize_n = htonl(objectSize);
memcpy(sendBuffer, &objectSize_n, HEADERSIZE);
memcpy(sendBuffer + HEADERSIZE, [currentSendObject bytes], objectSize);
// update the cursor and byte counter
sendBufferCursor = sendBuffer;
bytesRemainingToSend = packetSize;
// release the sendObject
[currentSendObject release];
}
// check if we need to re-enable the stream events because we had nothing to send earlier
if (outStreamHasSpaceAvailableEventIgnored && bytesRemainingToSend)
{
// HACK
// this is a hack... not very nice :-/
[self stream:outStream handleEvent:NSStreamEventHasSpaceAvailable];
}
}
-(void)enqueueNextSendObject:(NSData *)theData;
{
[sendObjectsQueue insertObject:theData atIndex:0];
// if we are not sending, trigger a dequeue
if (bytesRemainingToSend == 0)
{
NSAssert([sendObjectsQueue count] == 1, @"We are not sending but the objectSendQueue is not empty!");
[self dequeueNextSendObject];
}
}
// TODO: READ ME!
// NOTE: This method is NOT safe for reentrancy!
// We have seen in AsyncSocket that a new stream event may be issued during the stream read: and write: calls.
// This, of course, will cause a reentrancy of the same thread into this method which will break things.. badly!
// A solution would be to lock the method conditionally and cache any pending stream events that could not be processed because of the lock.
// For now we only assert that it works. We'll implement the condition if the assertion ever fires... I promise ;)
- (void) stream:(NSStream*)stream handleEvent:(NSStreamEvent)eventCode
{
NSAssert(!threadIsPresentInMethod, @"We have been caught by the nasty AsyncSocket bug!");
threadIsPresentInMethod = YES;
switch(eventCode) {
case NSStreamEventOpenCompleted:
{
if ([inStream streamStatus] == NSStreamStatusOpen && [outStream streamStatus] == NSStreamStatusOpen && !openCallbackSent)
{
[delegate streamsDidOpenOnConnection:self];
openCallbackSent = YES;
}
break;
}
case NSStreamEventHasBytesAvailable:
{
//BOOL didCompletePacket = NO;
while (/*didCompletePacket &&*/ [inStream hasBytesAvailable])
{
//didCompletePacket = NO;
// try to read as many bytes as we need for the next subpacket
NSInteger bytesActuallyRead = [inStream read:dataBufferCursor maxLength:bytesMissingForNextSubpacket];
dataBufferCursor += bytesActuallyRead;
// if we have enough construct the subpacket and act on it
if (bytesActuallyRead == bytesMissingForNextSubpacket)
{
//didCompletePacket = YES;
[self processBufferAndPrepNextRead];
}
else if (bytesActuallyRead > 0)
{
bytesMissingForNextSubpacket -= bytesActuallyRead;
//didCompletePacket = NO;
}
else if ([stream streamStatus] == NSStreamStatusAtEnd)
{
// be careful! the delegate might kill us for the bad news...
[delegate streamEndEncountered:stream onConnection:self];
break;
}
else if ([stream streamStatus] == NSStreamStatusError)
{
// be careful! the delegate might kill us for the bad news...
[delegate streamErrorEncountered:stream onConnection:self];
break;
}
else
{
NSAssert(NO, @"Could not read bytes from network but stream reports no errors");
break;
}
}
break;
}
case NSStreamEventHasSpaceAvailable:
{
if (bytesRemainingToSend == 0)
{
outStreamHasSpaceAvailableEventIgnored = YES;
}
else
{
// out stream
outStreamHasSpaceAvailableEventIgnored = NO;
uint32_t bytesToSend = ((bytesRemainingToSend >= CHUNKSIZE) ? CHUNKSIZE : bytesRemainingToSend);
uint32_t bytesActuallySent = [outStream write:sendBufferCursor maxLength:bytesToSend];
bytesRemainingToSend -= bytesActuallySent;
if (bytesRemainingToSend == 0)
{
// packet has been sent, prepare next
[self dequeueNextSendObject];
}
else if (bytesActuallySent > 0)
{
// we need to send the rest later
sendBufferCursor += bytesActuallySent;
}
else if ([stream streamStatus] == NSStreamStatusAtEnd)
{
// be careful! the delegate might kill us for the bad news...
[delegate streamEndEncountered:stream onConnection:self];
}
else if ([stream streamStatus] == NSStreamStatusError || bytesActuallySent == -1)
{
// be careful! the delegate might kill us for the bad news...
[delegate streamErrorEncountered:stream onConnection:self];
}
else
{
NSAssert(NO, @"Could not write bytes to network but stream reports no errors");
}
}
break;
}
case NSStreamEventEndEncountered:
{
// be careful! the delegate might kill us for the bad news...
[delegate streamEndEncountered:stream onConnection:self];
break;
}
case NSStreamEventErrorOccurred:
{
// be careful! the delegate might kill us for the bad news...
[delegate streamErrorEncountered:stream onConnection:self];
break;
}
default:
break;
}
threadIsPresentInMethod = NO;
}
@end

View File

@ -0,0 +1,43 @@
/*
* ThoMoTCPConnectionDelegateProtocol.h
* ThoMoNetworkingFramework
*
* Created by Thorsten Karrer on 1.7.09.
* Copyright 2010 media computing group - RWTH Aachen University.
*
* 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.
*
*/
@class ThoMoTCPConnection;
@protocol ThoMoTCPConnectionDelegateProtocol
-(void)didReceiveData:(NSData *)theData onConnection:(ThoMoTCPConnection *)theConnection;
-(void)streamsDidOpenOnConnection:(ThoMoTCPConnection *)theConnection;
-(void)streamEndEncountered:(NSStream *)theStream onConnection:(ThoMoTCPConnection *)theConnection;
-(void)streamErrorEncountered:(NSStream *)theStream onConnection:(ThoMoTCPConnection *)theConnection;
@end

View File

@ -0,0 +1,20 @@
Copyright (c) 2010-2011 Sam Soffes
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.

View File

@ -0,0 +1,45 @@
//
// SSZipArchive.h
// SSZipArchive
//
// Created by Sam Soffes on 7/21/10.
// Copyright (c) Sam Soffes 2010-2011. All rights reserved.
//
#import <Foundation/Foundation.h>
#include "minizip/unzip.h"
@protocol SSZipArchiveDelegate;
@interface SSZipArchive : NSObject
// Unzip
+ (BOOL)unzipFileAtPath:(NSString *)path toDestination:(NSString *)destination;
+ (BOOL)unzipFileAtPath:(NSString *)path toDestination:(NSString *)destination overwrite:(BOOL)overwrite password:(NSString *)password error:(NSError **)error;
+ (BOOL)unzipFileAtPath:(NSString *)path toDestination:(NSString *)destination delegate:(id<SSZipArchiveDelegate>)delegate;
+ (BOOL)unzipFileAtPath:(NSString *)path toDestination:(NSString *)destination overwrite:(BOOL)overwrite password:(NSString *)password error:(NSError **)error delegate:(id<SSZipArchiveDelegate>)delegate;
// Zip
+ (BOOL)createZipFileAtPath:(NSString *)path withFilesAtPaths:(NSArray *)filenames;
- (id)initWithPath:(NSString *)path;
- (BOOL)open;
- (BOOL)writeFile:(NSString *)path;
- (BOOL)writeData:(NSData *)data filename:(NSString *)filename;
- (BOOL)close;
@end
@protocol SSZipArchiveDelegate <NSObject>
@optional
- (void)zipArchiveWillUnzipArchiveAtPath:(NSString *)path zipInfo:(unz_global_info)zipInfo;
- (void)zipArchiveDidUnzipArchiveAtPath:(NSString *)path zipInfo:(unz_global_info)zipInfo unzippedPath:(NSString *)unzippedPath;
- (void)zipArchiveWillUnzipFileAtIndex:(NSInteger)fileIndex totalFiles:(NSInteger)totalFiles archivePath:(NSString *)archivePath fileInfo:(unz_file_info)fileInfo;
- (void)zipArchiveDidUnzipFileAtIndex:(NSInteger)fileIndex totalFiles:(NSInteger)totalFiles archivePath:(NSString *)archivePath fileInfo:(unz_file_info)fileInfo;
@end

View File

@ -0,0 +1,421 @@
//
// SSZipArchive.m
// SSZipArchive
//
// Created by Sam Soffes on 7/21/10.
// Copyright (c) Sam Soffes 2010-2011. All rights reserved.
//
#import "SSZipArchive.h"
#include "minizip/zip.h"
#import "zlib.h"
#import "zconf.h"
#include <sys/stat.h>
#define CHUNK 16384
@interface SSZipArchive ()
+ (NSDate *)_dateWithMSDOSFormat:(UInt32)msdosDateTime;
@end
@implementation SSZipArchive {
NSString *_path;
NSString *_filename;
zipFile _zip;
}
#pragma mark - Unzipping
+ (BOOL)unzipFileAtPath:(NSString *)path toDestination:(NSString *)destination {
return [self unzipFileAtPath:path toDestination:destination delegate:nil];
}
+ (BOOL)unzipFileAtPath:(NSString *)path toDestination:(NSString *)destination overwrite:(BOOL)overwrite password:(NSString *)password error:(NSError **)error {
return [self unzipFileAtPath:path toDestination:destination overwrite:overwrite password:password error:error delegate:nil];
}
+ (BOOL)unzipFileAtPath:(NSString *)path toDestination:(NSString *)destination delegate:(id<SSZipArchiveDelegate>)delegate {
return [self unzipFileAtPath:path toDestination:destination overwrite:YES password:nil error:nil delegate:delegate];
}
+ (BOOL)unzipFileAtPath:(NSString *)path toDestination:(NSString *)destination overwrite:(BOOL)overwrite password:(NSString *)password error:(NSError **)error delegate:(id<SSZipArchiveDelegate>)delegate {
// Begin opening
zipFile zip = unzOpen((const char*)[path UTF8String]);
if (zip == NULL) {
NSDictionary *userInfo = [NSDictionary dictionaryWithObject:@"failed to open zip file" forKey:NSLocalizedDescriptionKey];
if (error) {
*error = [NSError errorWithDomain:@"SSZipArchiveErrorDomain" code:-1 userInfo:userInfo];
}
return NO;
}
unz_global_info globalInfo = {0ul, 0ul};
unzGetGlobalInfo(zip, &globalInfo);
// Begin unzipping
if (unzGoToFirstFile(zip) != UNZ_OK) {
NSDictionary *userInfo = [NSDictionary dictionaryWithObject:@"failed to open first file in zip file" forKey:NSLocalizedDescriptionKey];
if (error) {
*error = [NSError errorWithDomain:@"SSZipArchiveErrorDomain" code:-2 userInfo:userInfo];
}
return NO;
}
BOOL success = YES;
int ret = 0;
unsigned char buffer[4096] = {0};
NSFileManager *fileManager = [NSFileManager defaultManager];
NSMutableSet *directoriesModificationDates = [[NSMutableSet alloc] init];
// Message delegate
if ([delegate respondsToSelector:@selector(zipArchiveWillUnzipArchiveAtPath:zipInfo:)]) {
[delegate zipArchiveWillUnzipArchiveAtPath:path zipInfo:globalInfo];
}
NSInteger currentFileNumber = 0;
do {
if ([password length] == 0) {
ret = unzOpenCurrentFile(zip);
} else {
ret = unzOpenCurrentFilePassword(zip, [password cStringUsingEncoding:NSASCIIStringEncoding]);
}
if (ret != UNZ_OK) {
success = NO;
break;
}
// Reading data and write to file
unz_file_info fileInfo;
memset(&fileInfo, 0, sizeof(unz_file_info));
ret = unzGetCurrentFileInfo(zip, &fileInfo, NULL, 0, NULL, 0, NULL, 0);
if (ret != UNZ_OK) {
success = NO;
unzCloseCurrentFile(zip);
break;
}
// Message delegate
if ([delegate respondsToSelector:@selector(zipArchiveWillUnzipFileAtIndex:totalFiles:archivePath:fileInfo:)]) {
[delegate zipArchiveWillUnzipFileAtIndex:currentFileNumber totalFiles:(NSInteger)globalInfo.number_entry
archivePath:path fileInfo:fileInfo];
}
char *filename = (char *)malloc(fileInfo.size_filename + 1);
unzGetCurrentFileInfo(zip, &fileInfo, filename, fileInfo.size_filename + 1, NULL, 0, NULL, 0);
filename[fileInfo.size_filename] = '\0';
//
// NOTE
// I used the ZIP spec from here:
// http://www.pkware.com/documents/casestudies/APPNOTE.TXT
//
// ...to deduce this method of detecting whether the file in the ZIP is a symbolic link.
// If it is, it is listed as a directory but has a data size greater than zero (real
// directories have it equal to 0) and the included, uncompressed data is the symbolic link path.
//
// ZIP files did not originally include support for symbolic links so the specification
// doesn't include anything in them that isn't part of a unix extension that isn't being used
// by the archivers we're testing. Most of this is figured out through trial and error and
// reading ZIP headers in hex editors. This seems to do the trick though.
//
const uLong ZipCompressionMethodStore = 0;
BOOL fileIsSymbolicLink = NO;
if((fileInfo.compression_method == ZipCompressionMethodStore) && // Is it compressed?
(S_ISDIR(fileInfo.external_fa)) && // Is it marked as a directory
(fileInfo.compressed_size > 0)) // Is there any data?
{
fileIsSymbolicLink = YES;
}
// Check if it contains directory
NSString *strPath = [NSString stringWithCString:filename encoding:NSUTF8StringEncoding];
BOOL isDirectory = NO;
if (filename[fileInfo.size_filename-1] == '/' || filename[fileInfo.size_filename-1] == '\\') {
isDirectory = YES;
}
free(filename);
// Contains a path
if ([strPath rangeOfCharacterFromSet:[NSCharacterSet characterSetWithCharactersInString:@"/\\"]].location != NSNotFound) {
strPath = [strPath stringByReplacingOccurrencesOfString:@"\\" withString:@"/"];
}
NSString *fullPath = [destination stringByAppendingPathComponent:strPath];
NSError *err = nil;
NSDate *modDate = [[self class] _dateWithMSDOSFormat:(UInt32)fileInfo.dosDate];
NSDictionary *directoryAttr = [NSDictionary dictionaryWithObjectsAndKeys:modDate, NSFileCreationDate, modDate, NSFileModificationDate, nil];
if (isDirectory) {
[fileManager createDirectoryAtPath:fullPath withIntermediateDirectories:YES attributes:directoryAttr error:&err];
} else {
[fileManager createDirectoryAtPath:[fullPath stringByDeletingLastPathComponent] withIntermediateDirectories:YES attributes:directoryAttr error:&err];
}
if (nil != err) {
NSLog(@"[SSZipArchive] Error: %@", err.localizedDescription);
}
if(!fileIsSymbolicLink)
[directoriesModificationDates addObject: [NSDictionary dictionaryWithObjectsAndKeys:fullPath, @"path", modDate, @"modDate", nil]];
if ([fileManager fileExistsAtPath:fullPath] && !isDirectory && !overwrite) {
unzCloseCurrentFile(zip);
ret = unzGoToNextFile(zip);
continue;
}
if(!fileIsSymbolicLink)
{
FILE *fp = fopen((const char*)[fullPath UTF8String], "wb");
while (fp) {
int readBytes = unzReadCurrentFile(zip, buffer, 4096);
if (readBytes > 0) {
fwrite(buffer, readBytes, 1, fp );
} else {
break;
}
}
if (fp) {
fclose(fp);
// Set the original datetime property
if (fileInfo.dosDate != 0) {
NSDate *orgDate = [[self class] _dateWithMSDOSFormat:(UInt32)fileInfo.dosDate];
NSDictionary *attr = [NSDictionary dictionaryWithObject:orgDate forKey:NSFileModificationDate];
if (attr) {
if ([fileManager setAttributes:attr ofItemAtPath:fullPath error:nil] == NO) {
// Can't set attributes
NSLog(@"[SSZipArchive] Failed to set attributes");
}
}
}
}
}
else
{
// Get the path for the symbolic link
NSURL* symlinkURL = [NSURL fileURLWithPath:fullPath];
NSMutableString* destinationPath = [NSMutableString string];
int bytesRead = 0;
while((bytesRead = unzReadCurrentFile(zip, buffer, 4096)) > 0)
{
buffer[bytesRead] = 0;
[destinationPath appendString:[NSString stringWithUTF8String:(const char*)buffer]];
}
//NSLog(@"Symlinking to: %@", destinationPath);
NSURL* destinationURL = [NSURL fileURLWithPath:destinationPath];
// Create the symbolic link
NSError* symlinkError = nil;
[fileManager createSymbolicLinkAtURL:symlinkURL withDestinationURL:destinationURL error:&symlinkError];
if(symlinkError != nil)
{
NSLog(@"Failed to create symbolic link at \"%@\" to \"%@\". Error: %@", symlinkURL.absoluteString, destinationURL.absoluteString, symlinkError.localizedDescription);
}
}
unzCloseCurrentFile( zip );
ret = unzGoToNextFile( zip );
// Message delegate
if ([delegate respondsToSelector:@selector(zipArchiveDidUnzipFileAtIndex:totalFiles:archivePath:fileInfo:)]) {
[delegate zipArchiveDidUnzipFileAtIndex:currentFileNumber totalFiles:(NSInteger)globalInfo.number_entry
archivePath:path fileInfo:fileInfo];
}
currentFileNumber++;
} while(ret == UNZ_OK && UNZ_OK != UNZ_END_OF_LIST_OF_FILE);
// Close
unzClose(zip);
// The process of decompressing the .zip archive causes the modification times on the folders
// to be set to the present time. So, when we are done, they need to be explicitly set.
// set the modification date on all of the directories.
NSError * err = nil;
for (NSDictionary * d in directoriesModificationDates) {
if (![[NSFileManager defaultManager] setAttributes:[NSDictionary dictionaryWithObjectsAndKeys:[d objectForKey:@"modDate"], NSFileModificationDate, nil] ofItemAtPath:[d objectForKey:@"path"] error:&err]) {
NSLog(@"[SSZipArchive] Set attributes failed for directory: %@.", [d objectForKey:@"path"]);
}
if (err) {
NSLog(@"[SSZipArchive] Error setting directory file modification date attribute: %@",err.localizedDescription);
}
}
#if !__has_feature(objc_arc)
[directoriesModificationDates release];
#endif
// Message delegate
if (success && [delegate respondsToSelector:@selector(zipArchiveDidUnzipArchiveAtPath:zipInfo:unzippedPath:)]) {
[delegate zipArchiveDidUnzipArchiveAtPath:path zipInfo:globalInfo unzippedPath:destination];
}
return success;
}
#pragma mark - Zipping
+ (BOOL)createZipFileAtPath:(NSString *)path withFilesAtPaths:(NSArray *)paths {
BOOL success = NO;
SSZipArchive *zipArchive = [[SSZipArchive alloc] initWithPath:path];
if ([zipArchive open]) {
for (NSString *path in paths) {
[zipArchive writeFile:path];
}
success = [zipArchive close];
}
#if !__has_feature(objc_arc)
[zipArchive release];
#endif
return success;
}
- (id)initWithPath:(NSString *)path {
if ((self = [super init])) {
_path = [path copy];
}
return self;
}
#if !__has_feature(objc_arc)
- (void)dealloc {
[_path release];
[super dealloc];
}
#endif
- (BOOL)open {
NSAssert((_zip == NULL), @"Attempting open an archive which is already open");
_zip = zipOpen([_path UTF8String], APPEND_STATUS_CREATE);
return (NULL != _zip);
}
- (void)zipInfo:(zip_fileinfo*)zipInfo setDate:(NSDate*)date {
NSCalendar *currentCalendar = [NSCalendar currentCalendar];
uint flags = NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit | NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit;
NSDateComponents *components = [currentCalendar components:flags fromDate:date];
zipInfo->tmz_date.tm_sec = (unsigned int)components.second;
zipInfo->tmz_date.tm_min = (unsigned int)components.minute;
zipInfo->tmz_date.tm_hour = (unsigned int)components.hour;
zipInfo->tmz_date.tm_mday = (unsigned int)components.day;
zipInfo->tmz_date.tm_mon = (unsigned int)components.month - 1;
zipInfo->tmz_date.tm_year = (unsigned int)components.year;
}
- (BOOL)writeFile:(NSString *)path {
NSAssert((_zip != NULL), @"Attempting to write to an archive which was never opened");
FILE *input = fopen([path UTF8String], "r");
if (NULL == input) {
return NO;
}
zipOpenNewFileInZip(_zip, [[path lastPathComponent] UTF8String], NULL, NULL, 0, NULL, 0, NULL, Z_DEFLATED,
Z_DEFAULT_COMPRESSION);
void *buffer = malloc(CHUNK);
unsigned int len = 0;
while (!feof(input)) {
len = (unsigned int) fread(buffer, 1, CHUNK, input);
zipWriteInFileInZip(_zip, buffer, len);
}
zipCloseFileInZip(_zip);
free(buffer);
return YES;
}
- (BOOL)writeData:(NSData *)data filename:(NSString *)filename {
if (!_zip) {
return NO;
}
if (!data) {
return NO;
}
zip_fileinfo zipInfo = {{0,0,0,0,0,0},0,0,0};
[self zipInfo:&zipInfo setDate:[NSDate date]];
zipOpenNewFileInZip(_zip, [filename UTF8String], &zipInfo, NULL, 0, NULL, 0, NULL, Z_DEFLATED, Z_DEFAULT_COMPRESSION);
zipWriteInFileInZip(_zip, data.bytes, (unsigned int)data.length);
zipCloseFileInZip(_zip);
return YES;
}
- (BOOL)close {
NSAssert((_zip != NULL), @"[SSZipArchive] Attempting to close an archive which was never opened");
zipClose(_zip, NULL);
return YES;
}
#pragma mark - Private
// Format from http://newsgroups.derkeiler.com/Archive/Comp/comp.os.msdos.programmer/2009-04/msg00060.html
// Two consecutive words, or a longword, YYYYYYYMMMMDDDDD hhhhhmmmmmmsssss
// YYYYYYY is years from 1980 = 0
// sssss is (seconds/2).
//
// 3658 = 0011 0110 0101 1000 = 0011011 0010 11000 = 27 2 24 = 2007-02-24
// 7423 = 0111 0100 0010 0011 - 01110 100001 00011 = 14 33 2 = 14:33:06
+ (NSDate *)_dateWithMSDOSFormat:(UInt32)msdosDateTime {
static const UInt32 kYearMask = 0xFE000000;
static const UInt32 kMonthMask = 0x1E00000;
static const UInt32 kDayMask = 0x1F0000;
static const UInt32 kHourMask = 0xF800;
static const UInt32 kMinuteMask = 0x7E0;
static const UInt32 kSecondMask = 0x1F;
NSCalendar *gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
NSDateComponents *components = [[NSDateComponents alloc] init];
NSAssert(0xFFFFFFFF == (kYearMask | kMonthMask | kDayMask | kHourMask | kMinuteMask | kSecondMask), @"[SSZipArchive] MSDOS date masks don't add up");
[components setYear:1980 + ((msdosDateTime & kYearMask) >> 25)];
[components setMonth:(msdosDateTime & kMonthMask) >> 21];
[components setDay:(msdosDateTime & kDayMask) >> 16];
[components setHour:(msdosDateTime & kHourMask) >> 11];
[components setMinute:(msdosDateTime & kMinuteMask) >> 5];
[components setSecond:(msdosDateTime & kSecondMask) * 2];
NSDate *date = [NSDate dateWithTimeInterval:0 sinceDate:[gregorian dateFromComponents:components]];
#if !__has_feature(objc_arc)
[gregorian release];
[components release];
#endif
return date;
}
@end

View File

@ -0,0 +1,131 @@
/* crypt.h -- base code for crypt/uncrypt ZIPfile
Version 1.01e, February 12th, 2005
Copyright (C) 1998-2005 Gilles Vollant
This code is a modified version of crypting code in Infozip distribution
The encryption/decryption parts of this source code (as opposed to the
non-echoing password parts) were originally written in Europe. The
whole source package can be freely distributed, including from the USA.
(Prior to January 2000, re-export from the US was a violation of US law.)
This encryption code is a direct transcription of the algorithm from
Roger Schlafly, described by Phil Katz in the file appnote.txt. This
file (appnote.txt) is distributed with the PKZIP program (even in the
version without encryption capabilities).
If you don't need crypting in your application, just define symbols
NOCRYPT and NOUNCRYPT.
This code support the "Traditional PKWARE Encryption".
The new AES encryption added on Zip format by Winzip (see the page
http://www.winzip.com/aes_info.htm ) and PKWare PKZip 5.x Strong
Encryption is not supported.
*/
#define CRC32(c, b) ((*(pcrc_32_tab+(((int)(c) ^ (b)) & 0xff))) ^ ((c) >> 8))
/***********************************************************************
* Return the next byte in the pseudo-random sequence
*/
static int decrypt_byte(unsigned long* pkeys, const unsigned long* pcrc_32_tab)
{
unsigned temp; /* POTENTIAL BUG: temp*(temp^1) may overflow in an
* unpredictable manner on 16-bit systems; not a problem
* with any known compiler so far, though */
temp = ((unsigned)(*(pkeys+2)) & 0xffff) | 2;
return (int)(((temp * (temp ^ 1)) >> 8) & 0xff);
}
/***********************************************************************
* Update the encryption keys with the next byte of plain text
*/
static int update_keys(unsigned long* pkeys,const unsigned long* pcrc_32_tab,int c)
{
(*(pkeys+0)) = CRC32((*(pkeys+0)), c);
(*(pkeys+1)) += (*(pkeys+0)) & 0xff;
(*(pkeys+1)) = (*(pkeys+1)) * 134775813L + 1;
{
register int keyshift = (int)((*(pkeys+1)) >> 24);
(*(pkeys+2)) = CRC32((*(pkeys+2)), keyshift);
}
return c;
}
/***********************************************************************
* Initialize the encryption keys and the random header according to
* the given password.
*/
static void init_keys(const char* passwd,unsigned long* pkeys,const unsigned long* pcrc_32_tab)
{
*(pkeys+0) = 305419896L;
*(pkeys+1) = 591751049L;
*(pkeys+2) = 878082192L;
while (*passwd != '\0') {
update_keys(pkeys,pcrc_32_tab,(int)*passwd);
passwd++;
}
}
#define zdecode(pkeys,pcrc_32_tab,c) \
(update_keys(pkeys,pcrc_32_tab,c ^= decrypt_byte(pkeys,pcrc_32_tab)))
#define zencode(pkeys,pcrc_32_tab,c,t) \
(t=decrypt_byte(pkeys,pcrc_32_tab), update_keys(pkeys,pcrc_32_tab,c), t^(c))
#ifdef INCLUDECRYPTINGCODE_IFCRYPTALLOWED
#define RAND_HEAD_LEN 12
/* "last resort" source for second part of crypt seed pattern */
# ifndef ZCR_SEED2
# define ZCR_SEED2 3141592654UL /* use PI as default pattern */
# endif
static int crypthead(const char* passwd, /* password string */
unsigned char* buf, /* where to write header */
int bufSize,
unsigned long* pkeys,
const unsigned long* pcrc_32_tab,
unsigned long crcForCrypting)
{
int n; /* index in random header */
int t; /* temporary */
int c; /* random byte */
unsigned char header[RAND_HEAD_LEN-2]; /* random header */
static unsigned calls = 0; /* ensure different random header each time */
if (bufSize<RAND_HEAD_LEN)
return 0;
/* First generate RAND_HEAD_LEN-2 random bytes. We encrypt the
* output of rand() to get less predictability, since rand() is
* often poorly implemented.
*/
if (++calls == 1)
{
srand((unsigned)(time(NULL) ^ ZCR_SEED2));
}
init_keys(passwd, pkeys, pcrc_32_tab);
for (n = 0; n < RAND_HEAD_LEN-2; n++)
{
c = (rand() >> 7) & 0xff;
header[n] = (unsigned char)zencode(pkeys, pcrc_32_tab, c, t);
}
/* Encrypt random header (last two bytes is high word of crc) */
init_keys(passwd, pkeys, pcrc_32_tab);
for (n = 0; n < RAND_HEAD_LEN-2; n++)
{
buf[n] = (unsigned char)zencode(pkeys, pcrc_32_tab, header[n], t);
}
buf[n++] = (unsigned char)zencode(pkeys, pcrc_32_tab, (int)(crcForCrypting >> 16) & 0xff, t);
buf[n++] = (unsigned char)zencode(pkeys, pcrc_32_tab, (int)(crcForCrypting >> 24) & 0xff, t);
return n;
}
#endif

View File

@ -0,0 +1,239 @@
/* ioapi.h -- IO base function header for compress/uncompress .zip
part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html )
Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html )
Modifications for Zip64 support
Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com )
For more info read MiniZip_info.txt
*/
#if (defined(_WIN32))
#define _CRT_SECURE_NO_WARNINGS
#endif
#include "ioapi.h"
voidpf call_zopen64 (const zlib_filefunc64_32_def* pfilefunc,const void*filename,int mode)
{
if (pfilefunc->zfile_func64.zopen64_file != NULL)
return (*(pfilefunc->zfile_func64.zopen64_file)) (pfilefunc->zfile_func64.opaque,filename,mode);
else
{
return (*(pfilefunc->zopen32_file))(pfilefunc->zfile_func64.opaque,(const char*)filename,mode);
}
}
long call_zseek64 (const zlib_filefunc64_32_def* pfilefunc,voidpf filestream, ZPOS64_T offset, int origin)
{
if (pfilefunc->zfile_func64.zseek64_file != NULL)
return (*(pfilefunc->zfile_func64.zseek64_file)) (pfilefunc->zfile_func64.opaque,filestream,offset,origin);
else
{
uLong offsetTruncated = (uLong)offset;
if (offsetTruncated != offset)
return -1;
else
return (*(pfilefunc->zseek32_file))(pfilefunc->zfile_func64.opaque,filestream,offsetTruncated,origin);
}
}
ZPOS64_T call_ztell64 (const zlib_filefunc64_32_def* pfilefunc,voidpf filestream)
{
if (pfilefunc->zfile_func64.zseek64_file != NULL)
return (*(pfilefunc->zfile_func64.ztell64_file)) (pfilefunc->zfile_func64.opaque,filestream);
else
{
uLong tell_uLong = (*(pfilefunc->ztell32_file))(pfilefunc->zfile_func64.opaque,filestream);
if ((tell_uLong) == ((uLong)-1))
return (ZPOS64_T)-1;
else
return tell_uLong;
}
}
void fill_zlib_filefunc64_32_def_from_filefunc32(zlib_filefunc64_32_def* p_filefunc64_32,const zlib_filefunc_def* p_filefunc32)
{
p_filefunc64_32->zfile_func64.zopen64_file = NULL;
p_filefunc64_32->zopen32_file = p_filefunc32->zopen_file;
p_filefunc64_32->zfile_func64.zerror_file = p_filefunc32->zerror_file;
p_filefunc64_32->zfile_func64.zread_file = p_filefunc32->zread_file;
p_filefunc64_32->zfile_func64.zwrite_file = p_filefunc32->zwrite_file;
p_filefunc64_32->zfile_func64.ztell64_file = NULL;
p_filefunc64_32->zfile_func64.zseek64_file = NULL;
p_filefunc64_32->zfile_func64.zclose_file = p_filefunc32->zclose_file;
#ifndef __clang_analyzer__
p_filefunc64_32->zfile_func64.zerror_file = p_filefunc32->zerror_file;
#endif
p_filefunc64_32->zfile_func64.opaque = p_filefunc32->opaque;
p_filefunc64_32->zseek32_file = p_filefunc32->zseek_file;
p_filefunc64_32->ztell32_file = p_filefunc32->ztell_file;
}
static voidpf ZCALLBACK fopen_file_func OF((voidpf opaque, const char* filename, int mode));
static uLong ZCALLBACK fread_file_func OF((voidpf opaque, voidpf stream, void* buf, uLong size));
static uLong ZCALLBACK fwrite_file_func OF((voidpf opaque, voidpf stream, const void* buf,uLong size));
static ZPOS64_T ZCALLBACK ftell64_file_func OF((voidpf opaque, voidpf stream));
static long ZCALLBACK fseek64_file_func OF((voidpf opaque, voidpf stream, ZPOS64_T offset, int origin));
static int ZCALLBACK fclose_file_func OF((voidpf opaque, voidpf stream));
static int ZCALLBACK ferror_file_func OF((voidpf opaque, voidpf stream));
static voidpf ZCALLBACK fopen_file_func (voidpf opaque, const char* filename, int mode)
{
FILE* file = NULL;
const char* mode_fopen = NULL;
if ((mode & ZLIB_FILEFUNC_MODE_READWRITEFILTER)==ZLIB_FILEFUNC_MODE_READ)
mode_fopen = "rb";
else
if (mode & ZLIB_FILEFUNC_MODE_EXISTING)
mode_fopen = "r+b";
else
if (mode & ZLIB_FILEFUNC_MODE_CREATE)
mode_fopen = "wb";
if ((filename!=NULL) && (mode_fopen != NULL))
file = fopen(filename, mode_fopen);
return file;
}
static voidpf ZCALLBACK fopen64_file_func (voidpf opaque, const void* filename, int mode)
{
FILE* file = NULL;
const char* mode_fopen = NULL;
if ((mode & ZLIB_FILEFUNC_MODE_READWRITEFILTER)==ZLIB_FILEFUNC_MODE_READ)
mode_fopen = "rb";
else
if (mode & ZLIB_FILEFUNC_MODE_EXISTING)
mode_fopen = "r+b";
else
if (mode & ZLIB_FILEFUNC_MODE_CREATE)
mode_fopen = "wb";
if ((filename!=NULL) && (mode_fopen != NULL))
file = fopen64((const char*)filename, mode_fopen);
return file;
}
static uLong ZCALLBACK fread_file_func (voidpf opaque, voidpf stream, void* buf, uLong size)
{
uLong ret;
ret = (uLong)fread(buf, 1, (size_t)size, (FILE *)stream);
return ret;
}
static uLong ZCALLBACK fwrite_file_func (voidpf opaque, voidpf stream, const void* buf, uLong size)
{
uLong ret;
ret = (uLong)fwrite(buf, 1, (size_t)size, (FILE *)stream);
return ret;
}
static long ZCALLBACK ftell_file_func (voidpf opaque, voidpf stream)
{
long ret;
ret = ftell((FILE *)stream);
return ret;
}
static ZPOS64_T ZCALLBACK ftell64_file_func (voidpf opaque, voidpf stream)
{
ZPOS64_T ret;
ret = ftello64((FILE *)stream);
return ret;
}
static long ZCALLBACK fseek_file_func (voidpf opaque, voidpf stream, uLong offset, int origin)
{
int fseek_origin=0;
long ret;
switch (origin)
{
case ZLIB_FILEFUNC_SEEK_CUR :
fseek_origin = SEEK_CUR;
break;
case ZLIB_FILEFUNC_SEEK_END :
fseek_origin = SEEK_END;
break;
case ZLIB_FILEFUNC_SEEK_SET :
fseek_origin = SEEK_SET;
break;
default: return -1;
}
ret = 0;
if (fseek((FILE *)stream, offset, fseek_origin) != 0)
ret = -1;
return ret;
}
static long ZCALLBACK fseek64_file_func (voidpf opaque, voidpf stream, ZPOS64_T offset, int origin)
{
int fseek_origin=0;
long ret;
switch (origin)
{
case ZLIB_FILEFUNC_SEEK_CUR :
fseek_origin = SEEK_CUR;
break;
case ZLIB_FILEFUNC_SEEK_END :
fseek_origin = SEEK_END;
break;
case ZLIB_FILEFUNC_SEEK_SET :
fseek_origin = SEEK_SET;
break;
default: return -1;
}
ret = 0;
if(fseeko64((FILE *)stream, offset, fseek_origin) != 0)
ret = -1;
return ret;
}
static int ZCALLBACK fclose_file_func (voidpf opaque, voidpf stream)
{
int ret;
ret = fclose((FILE *)stream);
return ret;
}
static int ZCALLBACK ferror_file_func (voidpf opaque, voidpf stream)
{
int ret;
ret = ferror((FILE *)stream);
return ret;
}
void fill_fopen_filefunc (pzlib_filefunc_def)
zlib_filefunc_def* pzlib_filefunc_def;
{
pzlib_filefunc_def->zopen_file = fopen_file_func;
pzlib_filefunc_def->zread_file = fread_file_func;
pzlib_filefunc_def->zwrite_file = fwrite_file_func;
pzlib_filefunc_def->ztell_file = ftell_file_func;
pzlib_filefunc_def->zseek_file = fseek_file_func;
pzlib_filefunc_def->zclose_file = fclose_file_func;
pzlib_filefunc_def->zerror_file = ferror_file_func;
pzlib_filefunc_def->opaque = NULL;
}
void fill_fopen64_filefunc (zlib_filefunc64_def* pzlib_filefunc_def)
{
pzlib_filefunc_def->zopen64_file = fopen64_file_func;
pzlib_filefunc_def->zread_file = fread_file_func;
pzlib_filefunc_def->zwrite_file = fwrite_file_func;
pzlib_filefunc_def->ztell64_file = ftell64_file_func;
pzlib_filefunc_def->zseek64_file = fseek64_file_func;
pzlib_filefunc_def->zclose_file = fclose_file_func;
pzlib_filefunc_def->zerror_file = ferror_file_func;
pzlib_filefunc_def->opaque = NULL;
}

View File

@ -0,0 +1,201 @@
/* ioapi.h -- IO base function header for compress/uncompress .zip
part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html )
Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html )
Modifications for Zip64 support
Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com )
For more info read MiniZip_info.txt
Changes
Oct-2009 - Defined ZPOS64_T to fpos_t on windows and u_int64_t on linux. (might need to find a better why for this)
Oct-2009 - Change to fseeko64, ftello64 and fopen64 so large files would work on linux.
More if/def section may be needed to support other platforms
Oct-2009 - Defined fxxxx64 calls to normal fopen/ftell/fseek so they would compile on windows.
(but you should use iowin32.c for windows instead)
*/
#ifndef _ZLIBIOAPI64_H
#define _ZLIBIOAPI64_H
#if (!defined(_WIN32)) && (!defined(WIN32))
// Linux needs this to support file operation on files larger then 4+GB
// But might need better if/def to select just the platforms that needs them.
#ifndef __USE_FILE_OFFSET64
#define __USE_FILE_OFFSET64
#endif
#ifndef __USE_LARGEFILE64
#define __USE_LARGEFILE64
#endif
#ifndef _LARGEFILE64_SOURCE
#define _LARGEFILE64_SOURCE
#endif
#ifndef _FILE_OFFSET_BIT
#define _FILE_OFFSET_BIT 64
#endif
#endif
#include <stdio.h>
#include <stdlib.h>
#include "zlib.h"
#define USE_FILE32API
#if defined(USE_FILE32API)
#define fopen64 fopen
#define ftello64 ftell
#define fseeko64 fseek
#else
#ifdef _MSC_VER
#define fopen64 fopen
#if (_MSC_VER >= 1400) && (!(defined(NO_MSCVER_FILE64_FUNC)))
#define ftello64 _ftelli64
#define fseeko64 _fseeki64
#else // old MSC
#define ftello64 ftell
#define fseeko64 fseek
#endif
#endif
#endif
/*
#ifndef ZPOS64_T
#ifdef _WIN32
#define ZPOS64_T fpos_t
#else
#include <stdint.h>
#define ZPOS64_T uint64_t
#endif
#endif
*/
#ifdef HAVE_MINIZIP64_CONF_H
#include "mz64conf.h"
#endif
/* a type choosen by DEFINE */
#ifdef HAVE_64BIT_INT_CUSTOM
typedef 64BIT_INT_CUSTOM_TYPE ZPOS64_T;
#else
#ifdef HAS_STDINT_H
#include "stdint.h"
typedef uint64_t ZPOS64_T;
#else
#if defined(_MSC_VER) || defined(__BORLANDC__)
typedef unsigned __int64 ZPOS64_T;
#else
typedef unsigned long long int ZPOS64_T;
#endif
#endif
#endif
#ifdef __cplusplus
extern "C" {
#endif
#define ZLIB_FILEFUNC_SEEK_CUR (1)
#define ZLIB_FILEFUNC_SEEK_END (2)
#define ZLIB_FILEFUNC_SEEK_SET (0)
#define ZLIB_FILEFUNC_MODE_READ (1)
#define ZLIB_FILEFUNC_MODE_WRITE (2)
#define ZLIB_FILEFUNC_MODE_READWRITEFILTER (3)
#define ZLIB_FILEFUNC_MODE_EXISTING (4)
#define ZLIB_FILEFUNC_MODE_CREATE (8)
#ifndef ZCALLBACK
#if (defined(WIN32) || defined(_WIN32) || defined (WINDOWS) || defined (_WINDOWS)) && defined(CALLBACK) && defined (USEWINDOWS_CALLBACK)
#define ZCALLBACK CALLBACK
#else
#define ZCALLBACK
#endif
#endif
typedef voidpf (ZCALLBACK *open_file_func) OF((voidpf opaque, const char* filename, int mode));
typedef uLong (ZCALLBACK *read_file_func) OF((voidpf opaque, voidpf stream, void* buf, uLong size));
typedef uLong (ZCALLBACK *write_file_func) OF((voidpf opaque, voidpf stream, const void* buf, uLong size));
typedef int (ZCALLBACK *close_file_func) OF((voidpf opaque, voidpf stream));
typedef int (ZCALLBACK *testerror_file_func) OF((voidpf opaque, voidpf stream));
typedef long (ZCALLBACK *tell_file_func) OF((voidpf opaque, voidpf stream));
typedef long (ZCALLBACK *seek_file_func) OF((voidpf opaque, voidpf stream, uLong offset, int origin));
/* here is the "old" 32 bits structure structure */
typedef struct zlib_filefunc_def_s
{
open_file_func zopen_file;
read_file_func zread_file;
write_file_func zwrite_file;
tell_file_func ztell_file;
seek_file_func zseek_file;
close_file_func zclose_file;
testerror_file_func zerror_file;
voidpf opaque;
} zlib_filefunc_def;
typedef ZPOS64_T (ZCALLBACK *tell64_file_func) OF((voidpf opaque, voidpf stream));
typedef long (ZCALLBACK *seek64_file_func) OF((voidpf opaque, voidpf stream, ZPOS64_T offset, int origin));
typedef voidpf (ZCALLBACK *open64_file_func) OF((voidpf opaque, const void* filename, int mode));
typedef struct zlib_filefunc64_def_s
{
open64_file_func zopen64_file;
read_file_func zread_file;
write_file_func zwrite_file;
tell64_file_func ztell64_file;
seek64_file_func zseek64_file;
close_file_func zclose_file;
testerror_file_func zerror_file;
voidpf opaque;
} zlib_filefunc64_def;
void fill_fopen64_filefunc OF((zlib_filefunc64_def* pzlib_filefunc_def));
void fill_fopen_filefunc OF((zlib_filefunc_def* pzlib_filefunc_def));
/* now internal definition, only for zip.c and unzip.h */
typedef struct zlib_filefunc64_32_def_s
{
zlib_filefunc64_def zfile_func64;
open_file_func zopen32_file;
tell_file_func ztell32_file;
seek_file_func zseek32_file;
} zlib_filefunc64_32_def;
#define ZREAD64(filefunc,filestream,buf,size) ((*((filefunc).zfile_func64.zread_file)) ((filefunc).zfile_func64.opaque,filestream,buf,size))
#define ZWRITE64(filefunc,filestream,buf,size) ((*((filefunc).zfile_func64.zwrite_file)) ((filefunc).zfile_func64.opaque,filestream,buf,size))
//#define ZTELL64(filefunc,filestream) ((*((filefunc).ztell64_file)) ((filefunc).opaque,filestream))
//#define ZSEEK64(filefunc,filestream,pos,mode) ((*((filefunc).zseek64_file)) ((filefunc).opaque,filestream,pos,mode))
#define ZCLOSE64(filefunc,filestream) ((*((filefunc).zfile_func64.zclose_file)) ((filefunc).zfile_func64.opaque,filestream))
#define ZERROR64(filefunc,filestream) ((*((filefunc).zfile_func64.zerror_file)) ((filefunc).zfile_func64.opaque,filestream))
voidpf call_zopen64 OF((const zlib_filefunc64_32_def* pfilefunc,const void*filename,int mode));
long call_zseek64 OF((const zlib_filefunc64_32_def* pfilefunc,voidpf filestream, ZPOS64_T offset, int origin));
ZPOS64_T call_ztell64 OF((const zlib_filefunc64_32_def* pfilefunc,voidpf filestream));
void fill_zlib_filefunc64_32_def_from_filefunc32(zlib_filefunc64_32_def* p_filefunc64_32,const zlib_filefunc_def* p_filefunc32);
#define ZOPEN64(filefunc,filename,mode) (call_zopen64((&(filefunc)),(filename),(mode)))
#define ZTELL64(filefunc,filestream) (call_ztell64((&(filefunc)),(filestream)))
#define ZSEEK64(filefunc,filestream,pos,mode) (call_zseek64((&(filefunc)),(filestream),(pos),(mode)))
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,284 @@
/*
Additional tools for Minizip
Code: Xavier Roche '2004
License: Same as ZLIB (www.gzip.org)
*/
/* Code */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "zlib.h"
#include "unzip.h"
#include "mztools.h"
#define READ_8(adr) ((unsigned char)*(adr))
#define READ_16(adr) ( READ_8(adr) | (READ_8(adr+1) << 8) )
#define READ_32(adr) ( READ_16(adr) | (READ_16((adr)+2) << 16) )
#define WRITE_8(buff, n) do { \
*((unsigned char*)(buff)) = (unsigned char) ((n) & 0xff); \
} while(0)
#define WRITE_16(buff, n) do { \
WRITE_8((unsigned char*)(buff), n); \
WRITE_8(((unsigned char*)(buff)) + 1, (n) >> 8); \
} while(0)
#define WRITE_32(buff, n) do { \
WRITE_16((unsigned char*)(buff), (n) & 0xffff); \
WRITE_16((unsigned char*)(buff) + 2, (n) >> 16); \
} while(0)
extern int ZEXPORT unzRepair(file, fileOut, fileOutTmp, nRecovered, bytesRecovered)
const char* file;
const char* fileOut;
const char* fileOutTmp;
uLong* nRecovered;
uLong* bytesRecovered;
{
int err = Z_OK;
FILE* fpZip = fopen(file, "rb");
FILE* fpOut = fopen(fileOut, "wb");
FILE* fpOutCD = fopen(fileOutTmp, "wb");
if (fpZip != NULL && fpOut != NULL) {
int entries = 0;
uLong totalBytes = 0;
char header[30];
char filename[256];
char extra[1024];
int offset = 0;
int offsetCD = 0;
while ( fread(header, 1, 30, fpZip) == 30 ) {
int currentOffset = offset;
/* File entry */
if (READ_32(header) == 0x04034b50) {
unsigned int version = READ_16(header + 4);
unsigned int gpflag = READ_16(header + 6);
unsigned int method = READ_16(header + 8);
unsigned int filetime = READ_16(header + 10);
unsigned int filedate = READ_16(header + 12);
unsigned int crc = READ_32(header + 14); /* crc */
unsigned int cpsize = READ_32(header + 18); /* compressed size */
unsigned int uncpsize = READ_32(header + 22); /* uncompressed sz */
unsigned int fnsize = READ_16(header + 26); /* file name length */
unsigned int extsize = READ_16(header + 28); /* extra field length */
filename[0] = extra[0] = '\0';
/* Header */
if (fwrite(header, 1, 30, fpOut) == 30) {
offset += 30;
} else {
err = Z_ERRNO;
break;
}
/* Filename */
if (fnsize > 0) {
if (fread(filename, 1, fnsize, fpZip) == fnsize) {
if (fwrite(filename, 1, fnsize, fpOut) == fnsize) {
offset += fnsize;
} else {
err = Z_ERRNO;
break;
}
} else {
err = Z_ERRNO;
break;
}
} else {
err = Z_STREAM_ERROR;
break;
}
/* Extra field */
if (extsize > 0) {
if (fread(extra, 1, extsize, fpZip) == extsize) {
if (fwrite(extra, 1, extsize, fpOut) == extsize) {
offset += extsize;
} else {
err = Z_ERRNO;
break;
}
} else {
err = Z_ERRNO;
break;
}
}
/* Data */
{
int dataSize = cpsize;
if (dataSize == 0) {
dataSize = uncpsize;
}
if (dataSize > 0) {
char* data = malloc(dataSize);
if (data != NULL) {
if ((int)fread(data, 1, dataSize, fpZip) == dataSize) {
if ((int)fwrite(data, 1, dataSize, fpOut) == dataSize) {
offset += dataSize;
totalBytes += dataSize;
} else {
err = Z_ERRNO;
}
} else {
err = Z_ERRNO;
}
free(data);
if (err != Z_OK) {
break;
}
} else {
err = Z_MEM_ERROR;
break;
}
}
}
/* Central directory entry */
{
char centralDirectoryEntryHeader[46];
//char* comment = "";
//int comsize = (int) strlen(comment);
WRITE_32(centralDirectoryEntryHeader, 0x02014b50);
WRITE_16(centralDirectoryEntryHeader + 4, version);
WRITE_16(centralDirectoryEntryHeader + 6, version);
WRITE_16(centralDirectoryEntryHeader + 8, gpflag);
WRITE_16(centralDirectoryEntryHeader + 10, method);
WRITE_16(centralDirectoryEntryHeader + 12, filetime);
WRITE_16(centralDirectoryEntryHeader + 14, filedate);
WRITE_32(centralDirectoryEntryHeader + 16, crc);
WRITE_32(centralDirectoryEntryHeader + 20, cpsize);
WRITE_32(centralDirectoryEntryHeader + 24, uncpsize);
WRITE_16(centralDirectoryEntryHeader + 28, fnsize);
WRITE_16(centralDirectoryEntryHeader + 30, extsize);
WRITE_16(centralDirectoryEntryHeader + 32, 0 /*comsize*/);
WRITE_16(centralDirectoryEntryHeader + 34, 0); /* disk # */
WRITE_16(centralDirectoryEntryHeader + 36, 0); /* int attrb */
WRITE_32(centralDirectoryEntryHeader + 38, 0); /* ext attrb */
WRITE_32(centralDirectoryEntryHeader + 42, currentOffset);
/* Header */
if (fwrite(centralDirectoryEntryHeader, 1, 46, fpOutCD) == 46) {
offsetCD += 46;
/* Filename */
if (fnsize > 0) {
if (fwrite(filename, 1, fnsize, fpOutCD) == fnsize) {
offsetCD += fnsize;
} else {
err = Z_ERRNO;
break;
}
} else {
err = Z_STREAM_ERROR;
break;
}
/* Extra field */
if (extsize > 0) {
if (fwrite(extra, 1, extsize, fpOutCD) == extsize) {
offsetCD += extsize;
} else {
err = Z_ERRNO;
break;
}
}
/* Comment field */
/*
if (comsize > 0) {
if ((int)fwrite(comment, 1, comsize, fpOutCD) == comsize) {
offsetCD += comsize;
} else {
err = Z_ERRNO;
break;
}
}
*/
} else {
err = Z_ERRNO;
break;
}
}
/* Success */
entries++;
} else {
break;
}
}
/* Final central directory */
{
int entriesZip = entries;
char finalCentralDirectoryHeader[22];
//char* comment = ""; // "ZIP File recovered by zlib/minizip/mztools";
//int comsize = (int) strlen(comment);
if (entriesZip > 0xffff) {
entriesZip = 0xffff;
}
WRITE_32(finalCentralDirectoryHeader, 0x06054b50);
WRITE_16(finalCentralDirectoryHeader + 4, 0); /* disk # */
WRITE_16(finalCentralDirectoryHeader + 6, 0); /* disk # */
WRITE_16(finalCentralDirectoryHeader + 8, entriesZip); /* hack */
WRITE_16(finalCentralDirectoryHeader + 10, entriesZip); /* hack */
WRITE_32(finalCentralDirectoryHeader + 12, offsetCD); /* size of CD */
WRITE_32(finalCentralDirectoryHeader + 16, offset); /* offset to CD */
WRITE_16(finalCentralDirectoryHeader + 20, 0 /*comsize*/); /* comment */
/* Header */
if (fwrite(finalCentralDirectoryHeader, 1, 22, fpOutCD) == 22) {
/* Comment field */
/*
if (comsize > 0) {
if ((int)fwrite(comment, 1, comsize, fpOutCD) != comsize) {
err = Z_ERRNO;
}
}
*/
} else {
err = Z_ERRNO;
}
}
/* Final merge (file + central directory) */
fclose(fpOutCD);
if (err == Z_OK) {
fpOutCD = fopen(fileOutTmp, "rb");
if (fpOutCD != NULL) {
int nRead;
char buffer[8192];
while ( (nRead = (int)fread(buffer, 1, sizeof(buffer), fpOutCD)) > 0) {
if ((int)fwrite(buffer, 1, nRead, fpOut) != nRead) {
err = Z_ERRNO;
break;
}
}
fclose(fpOutCD);
}
}
/* Close */
fclose(fpZip);
fclose(fpOut);
/* Wipe temporary file */
(void)remove(fileOutTmp);
/* Number of recovered entries */
if (err == Z_OK) {
if (nRecovered != NULL) {
*nRecovered = entries;
}
if (bytesRecovered != NULL) {
*bytesRecovered = totalBytes;
}
}
} else {
err = Z_STREAM_ERROR;
}
return err;
}

View File

@ -0,0 +1,31 @@
/*
Additional tools for Minizip
Code: Xavier Roche '2004
License: Same as ZLIB (www.gzip.org)
*/
#ifndef _zip_tools_H
#define _zip_tools_H
#ifdef __cplusplus
extern "C" {
#endif
#ifndef _ZLIB_H
#include "zlib.h"
#endif
#include "unzip.h"
/* Repair a ZIP file (missing central directory)
file: file to recover
fileOut: output file after recovery
fileOutTmp: temporary file name used for recovery
*/
extern int ZEXPORT unzRepair(const char* file,
const char* fileOut,
const char* fileOutTmp,
uLong* nRecovered,
uLong* bytesRecovered);
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,437 @@
/* unzip.h -- IO for uncompress .zip files using zlib
Version 1.1, February 14h, 2010
part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html )
Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html )
Modifications of Unzip for Zip64
Copyright (C) 2007-2008 Even Rouault
Modifications for Zip64 support on both zip and unzip
Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com )
For more info read MiniZip_info.txt
---------------------------------------------------------------------------------
Condition of use and distribution are the same than zlib :
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
---------------------------------------------------------------------------------
Changes
See header of unzip64.c
*/
#ifndef _unz64_H
#define _unz64_H
#ifdef __cplusplus
extern "C" {
#endif
#ifndef _ZLIB_H
#include "zlib.h"
#endif
#ifndef _ZLIBIOAPI_H
#include "ioapi.h"
#endif
#ifdef HAVE_BZIP2
#include "bzlib.h"
#endif
#define Z_BZIP2ED 12
#if defined(STRICTUNZIP) || defined(STRICTZIPUNZIP)
/* like the STRICT of WIN32, we define a pointer that cannot be converted
from (void*) without cast */
typedef struct TagunzFile__ { int unused; } unzFile__;
typedef unzFile__ *unzFile;
#else
typedef voidp unzFile;
#endif
#define UNZ_OK (0)
#define UNZ_END_OF_LIST_OF_FILE (-100)
#define UNZ_ERRNO (Z_ERRNO)
#define UNZ_EOF (0)
#define UNZ_PARAMERROR (-102)
#define UNZ_BADZIPFILE (-103)
#define UNZ_INTERNALERROR (-104)
#define UNZ_CRCERROR (-105)
/* tm_unz contain date/time info */
typedef struct tm_unz_s
{
uInt tm_sec; /* seconds after the minute - [0,59] */
uInt tm_min; /* minutes after the hour - [0,59] */
uInt tm_hour; /* hours since midnight - [0,23] */
uInt tm_mday; /* day of the month - [1,31] */
uInt tm_mon; /* months since January - [0,11] */
uInt tm_year; /* years - [1980..2044] */
} tm_unz;
/* unz_global_info structure contain global data about the ZIPfile
These data comes from the end of central dir */
typedef struct unz_global_info64_s
{
ZPOS64_T number_entry; /* total number of entries in
the central dir on this disk */
uLong size_comment; /* size of the global comment of the zipfile */
} unz_global_info64;
typedef struct unz_global_info_s
{
uLong number_entry; /* total number of entries in
the central dir on this disk */
uLong size_comment; /* size of the global comment of the zipfile */
} unz_global_info;
/* unz_file_info contain information about a file in the zipfile */
typedef struct unz_file_info64_s
{
uLong version; /* version made by 2 bytes */
uLong version_needed; /* version needed to extract 2 bytes */
uLong flag; /* general purpose bit flag 2 bytes */
uLong compression_method; /* compression method 2 bytes */
uLong dosDate; /* last mod file date in Dos fmt 4 bytes */
uLong crc; /* crc-32 4 bytes */
ZPOS64_T compressed_size; /* compressed size 8 bytes */
ZPOS64_T uncompressed_size; /* uncompressed size 8 bytes */
uLong size_filename; /* filename length 2 bytes */
uLong size_file_extra; /* extra field length 2 bytes */
uLong size_file_comment; /* file comment length 2 bytes */
uLong disk_num_start; /* disk number start 2 bytes */
uLong internal_fa; /* internal file attributes 2 bytes */
uLong external_fa; /* external file attributes 4 bytes */
tm_unz tmu_date;
} unz_file_info64;
typedef struct unz_file_info_s
{
uLong version; /* version made by 2 bytes */
uLong version_needed; /* version needed to extract 2 bytes */
uLong flag; /* general purpose bit flag 2 bytes */
uLong compression_method; /* compression method 2 bytes */
uLong dosDate; /* last mod file date in Dos fmt 4 bytes */
uLong crc; /* crc-32 4 bytes */
uLong compressed_size; /* compressed size 4 bytes */
uLong uncompressed_size; /* uncompressed size 4 bytes */
uLong size_filename; /* filename length 2 bytes */
uLong size_file_extra; /* extra field length 2 bytes */
uLong size_file_comment; /* file comment length 2 bytes */
uLong disk_num_start; /* disk number start 2 bytes */
uLong internal_fa; /* internal file attributes 2 bytes */
uLong external_fa; /* external file attributes 4 bytes */
tm_unz tmu_date;
} unz_file_info;
extern int ZEXPORT unzStringFileNameCompare OF ((const char* fileName1,
const char* fileName2,
int iCaseSensitivity));
/*
Compare two filename (fileName1,fileName2).
If iCaseSenisivity = 1, comparision is case sensitivity (like strcmp)
If iCaseSenisivity = 2, comparision is not case sensitivity (like strcmpi
or strcasecmp)
If iCaseSenisivity = 0, case sensitivity is defaut of your operating system
(like 1 on Unix, 2 on Windows)
*/
extern unzFile ZEXPORT unzOpen OF((const char *path));
extern unzFile ZEXPORT unzOpen64 OF((const void *path));
/*
Open a Zip file. path contain the full pathname (by example,
on a Windows XP computer "c:\\zlib\\zlib113.zip" or on an Unix computer
"zlib/zlib113.zip".
If the zipfile cannot be opened (file don't exist or in not valid), the
return value is NULL.
Else, the return value is a unzFile Handle, usable with other function
of this unzip package.
the "64" function take a const void* pointer, because the path is just the
value passed to the open64_file_func callback.
Under Windows, if UNICODE is defined, using fill_fopen64_filefunc, the path
is a pointer to a wide unicode string (LPCTSTR is LPCWSTR), so const char*
does not describe the reality
*/
extern unzFile ZEXPORT unzOpen2 OF((const char *path,
zlib_filefunc_def* pzlib_filefunc_def));
/*
Open a Zip file, like unzOpen, but provide a set of file low level API
for read/write the zip file (see ioapi.h)
*/
extern unzFile ZEXPORT unzOpen2_64 OF((const void *path,
zlib_filefunc64_def* pzlib_filefunc_def));
/*
Open a Zip file, like unz64Open, but provide a set of file low level API
for read/write the zip file (see ioapi.h)
*/
extern int ZEXPORT unzClose OF((unzFile file));
/*
Close a ZipFile opened with unzipOpen.
If there is files inside the .Zip opened with unzOpenCurrentFile (see later),
these files MUST be closed with unzipCloseCurrentFile before call unzipClose.
return UNZ_OK if there is no problem. */
extern int ZEXPORT unzGetGlobalInfo OF((unzFile file,
unz_global_info *pglobal_info));
extern int ZEXPORT unzGetGlobalInfo64 OF((unzFile file,
unz_global_info64 *pglobal_info));
/*
Write info about the ZipFile in the *pglobal_info structure.
No preparation of the structure is needed
return UNZ_OK if there is no problem. */
extern int ZEXPORT unzGetGlobalComment OF((unzFile file,
char *szComment,
uLong uSizeBuf));
/*
Get the global comment string of the ZipFile, in the szComment buffer.
uSizeBuf is the size of the szComment buffer.
return the number of byte copied or an error code <0
*/
/***************************************************************************/
/* Unzip package allow you browse the directory of the zipfile */
extern int ZEXPORT unzGoToFirstFile OF((unzFile file));
/*
Set the current file of the zipfile to the first file.
return UNZ_OK if there is no problem
*/
extern int ZEXPORT unzGoToNextFile OF((unzFile file));
/*
Set the current file of the zipfile to the next file.
return UNZ_OK if there is no problem
return UNZ_END_OF_LIST_OF_FILE if the actual file was the latest.
*/
extern int ZEXPORT unzLocateFile OF((unzFile file,
const char *szFileName,
int iCaseSensitivity));
/*
Try locate the file szFileName in the zipfile.
For the iCaseSensitivity signification, see unzStringFileNameCompare
return value :
UNZ_OK if the file is found. It becomes the current file.
UNZ_END_OF_LIST_OF_FILE if the file is not found
*/
/* ****************************************** */
/* Ryan supplied functions */
/* unz_file_info contain information about a file in the zipfile */
typedef struct unz_file_pos_s
{
uLong pos_in_zip_directory; /* offset in zip file directory */
uLong num_of_file; /* # of file */
} unz_file_pos;
extern int ZEXPORT unzGetFilePos(
unzFile file,
unz_file_pos* file_pos);
extern int ZEXPORT unzGoToFilePos(
unzFile file,
unz_file_pos* file_pos);
typedef struct unz64_file_pos_s
{
ZPOS64_T pos_in_zip_directory; /* offset in zip file directory */
ZPOS64_T num_of_file; /* # of file */
} unz64_file_pos;
extern int ZEXPORT unzGetFilePos64(
unzFile file,
unz64_file_pos* file_pos);
extern int ZEXPORT unzGoToFilePos64(
unzFile file,
const unz64_file_pos* file_pos);
/* ****************************************** */
extern int ZEXPORT unzGetCurrentFileInfo64 OF((unzFile file,
unz_file_info64 *pfile_info,
char *szFileName,
uLong fileNameBufferSize,
void *extraField,
uLong extraFieldBufferSize,
char *szComment,
uLong commentBufferSize));
extern int ZEXPORT unzGetCurrentFileInfo OF((unzFile file,
unz_file_info *pfile_info,
char *szFileName,
uLong fileNameBufferSize,
void *extraField,
uLong extraFieldBufferSize,
char *szComment,
uLong commentBufferSize));
/*
Get Info about the current file
if pfile_info!=NULL, the *pfile_info structure will contain somes info about
the current file
if szFileName!=NULL, the filemane string will be copied in szFileName
(fileNameBufferSize is the size of the buffer)
if extraField!=NULL, the extra field information will be copied in extraField
(extraFieldBufferSize is the size of the buffer).
This is the Central-header version of the extra field
if szComment!=NULL, the comment string of the file will be copied in szComment
(commentBufferSize is the size of the buffer)
*/
/** Addition for GDAL : START */
extern ZPOS64_T ZEXPORT unzGetCurrentFileZStreamPos64 OF((unzFile file));
/** Addition for GDAL : END */
/***************************************************************************/
/* for reading the content of the current zipfile, you can open it, read data
from it, and close it (you can close it before reading all the file)
*/
extern int ZEXPORT unzOpenCurrentFile OF((unzFile file));
/*
Open for reading data the current file in the zipfile.
If there is no error, the return value is UNZ_OK.
*/
extern int ZEXPORT unzOpenCurrentFilePassword OF((unzFile file,
const char* password));
/*
Open for reading data the current file in the zipfile.
password is a crypting password
If there is no error, the return value is UNZ_OK.
*/
extern int ZEXPORT unzOpenCurrentFile2 OF((unzFile file,
int* method,
int* level,
int raw));
/*
Same than unzOpenCurrentFile, but open for read raw the file (not uncompress)
if raw==1
*method will receive method of compression, *level will receive level of
compression
note : you can set level parameter as NULL (if you did not want known level,
but you CANNOT set method parameter as NULL
*/
extern int ZEXPORT unzOpenCurrentFile3 OF((unzFile file,
int* method,
int* level,
int raw,
const char* password));
/*
Same than unzOpenCurrentFile, but open for read raw the file (not uncompress)
if raw==1
*method will receive method of compression, *level will receive level of
compression
note : you can set level parameter as NULL (if you did not want known level,
but you CANNOT set method parameter as NULL
*/
extern int ZEXPORT unzCloseCurrentFile OF((unzFile file));
/*
Close the file in zip opened with unzOpenCurrentFile
Return UNZ_CRCERROR if all the file was read but the CRC is not good
*/
extern int ZEXPORT unzReadCurrentFile OF((unzFile file,
voidp buf,
unsigned len));
/*
Read bytes from the current file (opened by unzOpenCurrentFile)
buf contain buffer where data must be copied
len the size of buf.
return the number of byte copied if somes bytes are copied
return 0 if the end of file was reached
return <0 with error code if there is an error
(UNZ_ERRNO for IO error, or zLib error for uncompress error)
*/
extern z_off_t ZEXPORT unztell OF((unzFile file));
extern ZPOS64_T ZEXPORT unztell64 OF((unzFile file));
/*
Give the current position in uncompressed data
*/
extern int ZEXPORT unzeof OF((unzFile file));
/*
return 1 if the end of file was reached, 0 elsewhere
*/
extern int ZEXPORT unzGetLocalExtrafield OF((unzFile file,
voidp buf,
unsigned len));
/*
Read extra field from the current file (opened by unzOpenCurrentFile)
This is the local-header version of the extra field (sometimes, there is
more info in the local-header version than in the central-header)
if buf==NULL, it return the size of the local extra field
if buf!=NULL, len is the size of the buffer, the extra header is copied in
buf.
the return value is the number of bytes copied in buf, or (if <0)
the error code
*/
/***************************************************************************/
/* Get the current file offset */
extern ZPOS64_T ZEXPORT unzGetOffset64 (unzFile file);
extern uLong ZEXPORT unzGetOffset (unzFile file);
/* Set the current file offset */
extern int ZEXPORT unzSetOffset64 (unzFile file, ZPOS64_T pos);
extern int ZEXPORT unzSetOffset (unzFile file, uLong pos);
#ifdef __cplusplus
}
#endif
#endif /* _unz64_H */

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,362 @@
/* zip.h -- IO on .zip files using zlib
Version 1.1, February 14h, 2010
part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html )
Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html )
Modifications for Zip64 support
Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com )
For more info read MiniZip_info.txt
---------------------------------------------------------------------------
Condition of use and distribution are the same than zlib :
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
---------------------------------------------------------------------------
Changes
See header of zip.h
*/
#ifndef _zip12_H
#define _zip12_H
#ifdef __cplusplus
extern "C" {
#endif
//#define HAVE_BZIP2
#ifndef _ZLIB_H
#include "zlib.h"
#endif
#ifndef _ZLIBIOAPI_H
#include "ioapi.h"
#endif
#ifdef HAVE_BZIP2
#include "bzlib.h"
#endif
#define Z_BZIP2ED 12
#if defined(STRICTZIP) || defined(STRICTZIPUNZIP)
/* like the STRICT of WIN32, we define a pointer that cannot be converted
from (void*) without cast */
typedef struct TagzipFile__ { int unused; } zipFile__;
typedef zipFile__ *zipFile;
#else
typedef voidp zipFile;
#endif
#define ZIP_OK (0)
#define ZIP_EOF (0)
#define ZIP_ERRNO (Z_ERRNO)
#define ZIP_PARAMERROR (-102)
#define ZIP_BADZIPFILE (-103)
#define ZIP_INTERNALERROR (-104)
#ifndef DEF_MEM_LEVEL
# if MAX_MEM_LEVEL >= 8
# define DEF_MEM_LEVEL 8
# else
# define DEF_MEM_LEVEL MAX_MEM_LEVEL
# endif
#endif
/* default memLevel */
/* tm_zip contain date/time info */
typedef struct tm_zip_s
{
uInt tm_sec; /* seconds after the minute - [0,59] */
uInt tm_min; /* minutes after the hour - [0,59] */
uInt tm_hour; /* hours since midnight - [0,23] */
uInt tm_mday; /* day of the month - [1,31] */
uInt tm_mon; /* months since January - [0,11] */
uInt tm_year; /* years - [1980..2044] */
} tm_zip;
typedef struct
{
tm_zip tmz_date; /* date in understandable format */
uLong dosDate; /* if dos_date == 0, tmu_date is used */
/* uLong flag; */ /* general purpose bit flag 2 bytes */
uLong internal_fa; /* internal file attributes 2 bytes */
uLong external_fa; /* external file attributes 4 bytes */
} zip_fileinfo;
typedef const char* zipcharpc;
#define APPEND_STATUS_CREATE (0)
#define APPEND_STATUS_CREATEAFTER (1)
#define APPEND_STATUS_ADDINZIP (2)
extern zipFile ZEXPORT zipOpen OF((const char *pathname, int append));
extern zipFile ZEXPORT zipOpen64 OF((const void *pathname, int append));
/*
Create a zipfile.
pathname contain on Windows XP a filename like "c:\\zlib\\zlib113.zip" or on
an Unix computer "zlib/zlib113.zip".
if the file pathname exist and append==APPEND_STATUS_CREATEAFTER, the zip
will be created at the end of the file.
(useful if the file contain a self extractor code)
if the file pathname exist and append==APPEND_STATUS_ADDINZIP, we will
add files in existing zip (be sure you don't add file that doesn't exist)
If the zipfile cannot be opened, the return value is NULL.
Else, the return value is a zipFile Handle, usable with other function
of this zip package.
*/
/* Note : there is no delete function into a zipfile.
If you want delete file into a zipfile, you must open a zipfile, and create another
Of couse, you can use RAW reading and writing to copy the file you did not want delte
*/
extern zipFile ZEXPORT zipOpen2 OF((const char *pathname,
int append,
zipcharpc* globalcomment,
zlib_filefunc_def* pzlib_filefunc_def));
extern zipFile ZEXPORT zipOpen2_64 OF((const void *pathname,
int append,
zipcharpc* globalcomment,
zlib_filefunc64_def* pzlib_filefunc_def));
extern int ZEXPORT zipOpenNewFileInZip OF((zipFile file,
const char* filename,
const zip_fileinfo* zipfi,
const void* extrafield_local,
uInt size_extrafield_local,
const void* extrafield_global,
uInt size_extrafield_global,
const char* comment,
int method,
int level));
extern int ZEXPORT zipOpenNewFileInZip64 OF((zipFile file,
const char* filename,
const zip_fileinfo* zipfi,
const void* extrafield_local,
uInt size_extrafield_local,
const void* extrafield_global,
uInt size_extrafield_global,
const char* comment,
int method,
int level,
int zip64));
/*
Open a file in the ZIP for writing.
filename : the filename in zip (if NULL, '-' without quote will be used
*zipfi contain supplemental information
if extrafield_local!=NULL and size_extrafield_local>0, extrafield_local
contains the extrafield data the the local header
if extrafield_global!=NULL and size_extrafield_global>0, extrafield_global
contains the extrafield data the the local header
if comment != NULL, comment contain the comment string
method contain the compression method (0 for store, Z_DEFLATED for deflate)
level contain the level of compression (can be Z_DEFAULT_COMPRESSION)
zip64 is set to 1 if a zip64 extended information block should be added to the local file header.
this MUST be '1' if the uncompressed size is >= 0xffffffff.
*/
extern int ZEXPORT zipOpenNewFileInZip2 OF((zipFile file,
const char* filename,
const zip_fileinfo* zipfi,
const void* extrafield_local,
uInt size_extrafield_local,
const void* extrafield_global,
uInt size_extrafield_global,
const char* comment,
int method,
int level,
int raw));
extern int ZEXPORT zipOpenNewFileInZip2_64 OF((zipFile file,
const char* filename,
const zip_fileinfo* zipfi,
const void* extrafield_local,
uInt size_extrafield_local,
const void* extrafield_global,
uInt size_extrafield_global,
const char* comment,
int method,
int level,
int raw,
int zip64));
/*
Same than zipOpenNewFileInZip, except if raw=1, we write raw file
*/
extern int ZEXPORT zipOpenNewFileInZip3 OF((zipFile file,
const char* filename,
const zip_fileinfo* zipfi,
const void* extrafield_local,
uInt size_extrafield_local,
const void* extrafield_global,
uInt size_extrafield_global,
const char* comment,
int method,
int level,
int raw,
int windowBits,
int memLevel,
int strategy,
const char* password,
uLong crcForCrypting));
extern int ZEXPORT zipOpenNewFileInZip3_64 OF((zipFile file,
const char* filename,
const zip_fileinfo* zipfi,
const void* extrafield_local,
uInt size_extrafield_local,
const void* extrafield_global,
uInt size_extrafield_global,
const char* comment,
int method,
int level,
int raw,
int windowBits,
int memLevel,
int strategy,
const char* password,
uLong crcForCrypting,
int zip64
));
/*
Same than zipOpenNewFileInZip2, except
windowBits,memLevel,,strategy : see parameter strategy in deflateInit2
password : crypting password (NULL for no crypting)
crcForCrypting : crc of file to compress (needed for crypting)
*/
extern int ZEXPORT zipOpenNewFileInZip4 OF((zipFile file,
const char* filename,
const zip_fileinfo* zipfi,
const void* extrafield_local,
uInt size_extrafield_local,
const void* extrafield_global,
uInt size_extrafield_global,
const char* comment,
int method,
int level,
int raw,
int windowBits,
int memLevel,
int strategy,
const char* password,
uLong crcForCrypting,
uLong versionMadeBy,
uLong flagBase
));
extern int ZEXPORT zipOpenNewFileInZip4_64 OF((zipFile file,
const char* filename,
const zip_fileinfo* zipfi,
const void* extrafield_local,
uInt size_extrafield_local,
const void* extrafield_global,
uInt size_extrafield_global,
const char* comment,
int method,
int level,
int raw,
int windowBits,
int memLevel,
int strategy,
const char* password,
uLong crcForCrypting,
uLong versionMadeBy,
uLong flagBase,
int zip64
));
/*
Same than zipOpenNewFileInZip4, except
versionMadeBy : value for Version made by field
flag : value for flag field (compression level info will be added)
*/
extern int ZEXPORT zipWriteInFileInZip OF((zipFile file,
const void* buf,
unsigned len));
/*
Write data in the zipfile
*/
extern int ZEXPORT zipCloseFileInZip OF((zipFile file));
/*
Close the current file in the zipfile
*/
extern int ZEXPORT zipCloseFileInZipRaw OF((zipFile file,
uLong uncompressed_size,
uLong crc32));
extern int ZEXPORT zipCloseFileInZipRaw64 OF((zipFile file,
ZPOS64_T uncompressed_size,
uLong crc32));
/*
Close the current file in the zipfile, for file opened with
parameter raw=1 in zipOpenNewFileInZip2
uncompressed_size and crc32 are value for the uncompressed size
*/
extern int ZEXPORT zipClose OF((zipFile file,
const char* global_comment));
/*
Close the zipfile
*/
extern int ZEXPORT zipRemoveExtraInfoBlock OF((char* pData, int* dataLen, short sHeader));
/*
zipRemoveExtraInfoBlock - Added by Mathias Svensson
Remove extra information block from a extra information data for the local file header or central directory header
It is needed to remove ZIP64 extra information blocks when before data is written if using RAW mode.
0x0001 is the signature header for the ZIP64 extra information blocks
usage.
Remove ZIP64 Extra information from a central director extra field data
zipRemoveExtraInfoBlock(pCenDirExtraFieldData, &nCenDirExtraFieldDataLen, 0x0001);
Remove ZIP64 Extra information from a Local File Header extra field data
zipRemoveExtraInfoBlock(pLocalHeaderExtraFieldData, &nLocalHeaderExtraFieldDataLen, 0x0001);
*/
#ifdef __cplusplus
}
#endif
#endif /* _zip64_H */

View File

@ -0,0 +1 @@
ba6bcddd1ff6445feb931434c6a04546bc936a4e

View File

@ -0,0 +1 @@
e386fa2dd33381cb9905ec5472ba7b48bdd60808

View File

@ -0,0 +1 @@
263d1eb95a83c1320c7c988e633f348479d38fb5

View File

@ -0,0 +1 @@
823b99b16a9fdddb84bf8af36e2be53cf928ea04

View File

@ -0,0 +1 @@
7b5dab861d78877bb1354fc832404f9dfb68b0ee

View File

@ -78,6 +78,7 @@ void Java_org_cocos2dx_lib_Cocos2dxRenderer_nativeInit(JNIEnv* env, jobject thi
{
handle_disconnected();
}
void Java_org_cocos2dx_cocosplayer_CocosPlayerSocket_nativeSetOrientation(JNIEnv* env, jobject thiz, jboolean isPortrait)
{
handle_set_orient((bool)isPortrait);

View File

@ -105,7 +105,7 @@ public class CocosPlayerSocket {
for(int i =0 ; i < keys.length; ++i ) {
}
if(cmd.equalsIgnoreCase("zip")) {
//cleanCache();
// cleanCache();
try {
Log.i(TAG, "Size of NSDATA payload: "+((NSData)data.objectForKey("data")).bytes().length);
CCBFileUtilsHelper.unzipCCB(((NSData)data.objectForKey("data")).bytes(), cw);

View File

@ -0,0 +1,23 @@
#import "ServerController.h"
void startServer(bool isRetina, bool isIPhone);
void cleanCache() {
std::string path = cocos2d::CCFileUtils::sharedFileUtils()->getWritablePath();
NSString *writeablePath = [NSString stringWithCString:path.c_str() encoding:NSASCIIStringEncoding];
NSString* dirPath = [writeablePath stringByAppendingPathComponent:@"ccb"];
[[NSFileManager defaultManager] removeItemAtPath:dirPath error:NULL];
[[NSFileManager defaultManager] removeItemAtPath:writeablePath error:NULL];
}
@class RootViewController;
@interface AppController : NSObject <UIAccelerometerDelegate, UIAlertViewDelegate, UITextFieldDelegate,UIApplicationDelegate> {
UIWindow *window;
RootViewController *viewController;
}
@end

View File

@ -0,0 +1,160 @@
#import <UIKit/UIKit.h>
#import "AppController.h"
#import "cocos2d.h"
#import "EAGLView.h"
#import "AppDelegate.h"
#import "RootViewController.h"
ServerController *server;
extern "C" {
void setDeviceResolutionJNI(const char *text) {
}
void cleanCacheDirJNI(){
cleanCache();
}
void updatePairing(const char *pairing) {
NSString *code = [NSString stringWithCString:pairing encoding:NSASCIIStringEncoding];
if([code isEqual:@"Auto"]) {
[[NSUserDefaults standardUserDefaults] removeObjectForKey:@"pairing"];
} else {
[[NSUserDefaults standardUserDefaults] setObject:code forKey:@"pairing"];
}
[[NSUserDefaults standardUserDefaults] synchronize];
[server updatePairing];
}
const char * getCCBDirectoryPath() {
std::string path = cocos2d::CCFileUtils::sharedFileUtils()->getWritablePath();
NSString *writeablePath = [NSString stringWithCString:path.c_str() encoding:NSASCIIStringEncoding];
NSString* dirPath = [writeablePath stringByAppendingPathComponent:@"ccb"];
return [dirPath cStringUsingEncoding:NSASCIIStringEncoding];
}
}
@implementation AppController
#pragma mark -
#pragma mark Application lifecycle
// cocos2d application instance
static AppDelegate s_sharedApplication;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
// Add the view controller's view to the window and display.
window = [[UIWindow alloc] initWithFrame: [[UIScreen mainScreen] bounds]];
EAGLView *__glView = [EAGLView viewWithFrame: [window bounds]
pixelFormat: kEAGLColorFormatRGBA8
depthFormat: GL_DEPTH_COMPONENT16 //_OES
preserveBackbuffer: NO
sharegroup: nil
multiSampling: NO
numberOfSamples: 0 ];
// Use RootViewController manage EAGLView
viewController = [[RootViewController alloc] initWithNibName:nil bundle:nil];
viewController.wantsFullScreenLayout = YES;
viewController.view = __glView;
// Set RootViewController to window
if ( [[UIDevice currentDevice].systemVersion floatValue] < 6.0)
{
// warning: addSubView doesn't work on iOS6
[window addSubview: viewController.view];
}
else
{
// use this method on ios6
[window setRootViewController:viewController];
}
[window makeKeyAndVisible];
[[UIApplication sharedApplication] setStatusBarHidden: YES];
cocos2d::CCApplication::sharedApplication()->run();
BOOL isRetina = s_sharedApplication.isRetina;
BOOL isIPhone = s_sharedApplication.isIPhone;
NSLog(@"ISIphone: %d %d", isIPhone, isRetina);
startServer(s_sharedApplication.isRetina, s_sharedApplication.isIPhone);
return YES;
}
- (void)applicationWillResignActive:(UIApplication *)application {
/*
Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
*/
cocos2d::CCDirector::sharedDirector()->pause();
}
- (void)applicationDidBecomeActive:(UIApplication *)application {
/*
Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
*/
cocos2d::CCDirector::sharedDirector()->resume();
}
- (void)applicationDidEnterBackground:(UIApplication *)application {
/*
Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
If your application supports background execution, called instead of applicationWillTerminate: when the user quits.
*/
cocos2d::CCApplication::sharedApplication()->applicationDidEnterBackground();
}
- (void)applicationWillEnterForeground:(UIApplication *)application {
/*
Called as part of transition from the background to the inactive state: here you can undo many of the changes made on entering the background.
*/
cocos2d::CCApplication::sharedApplication()->applicationWillEnterForeground();
}
- (void)applicationWillTerminate:(UIApplication *)application {
/*
Called when the application is about to terminate.
See also applicationDidEnterBackground:.
*/
}
#pragma mark -
#pragma mark Memory management
- (void)applicationDidReceiveMemoryWarning:(UIApplication *)application {
/*
Free up as much memory as possible by purging cached data objects that can be recreated (or reloaded from disk) later.
*/
cocos2d::CCDirector::sharedDirector()->purgeCachedData();
}
void startServer(bool isRetina, bool isIPhone) {
server = [[ServerController alloc] init];
server.isIPhone = isIPhone;
server.isRetina = isRetina;
[server start];
[server setNetworkStatus:kCCBNetworkStatusWaiting];
}
- (void)dealloc {
[super dealloc];
}
@end

View File

@ -0,0 +1 @@
1ff25700351d8711c5dab5fc4428f896956b1eea

View File

@ -0,0 +1 @@
66c6d1cead373b45218424f6a82f370897e443e4

View File

@ -0,0 +1 @@
84689888a14a2123d2b39f7f2f61be8c15207479

View File

@ -0,0 +1,8 @@
//
// Prefix header for all source files of the 'HelloJavascript' target in the 'HelloJavascript' project
//
#ifdef __OBJC__
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#endif

View File

@ -0,0 +1,33 @@
/****************************************************************************
Copyright (c) 2010-2011 cocos2d-x.org
Copyright (c) 2010 Ricardo Quesada
http://www.cocos2d-x.org
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 <UIKit/UIKit.h>
@interface RootViewController : UIViewController {
}
@end

View File

@ -0,0 +1,66 @@
#import "RootViewController.h"
@implementation RootViewController
/*
// The designated initializer. Override if you create the controller programmatically and want to perform customization that is not appropriate for viewDidLoad.
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
if ((self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil])) {
// Custom initialization
}
return self;
}
*/
/*
// Implement loadView to create a view hierarchy programmatically, without using a nib.
- (void)loadView {
}
*/
/*
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad {
[super viewDidLoad];
}
*/
// Override to allow orientations other than the default portrait orientation.
// This method is deprecated on ios6
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
return UIInterfaceOrientationIsLandscape( interfaceOrientation );
}
// For ios6, use supportedInterfaceOrientations & shouldAutorotate instead
- (NSUInteger) supportedInterfaceOrientations{
#ifdef __IPHONE_6_0
return UIInterfaceOrientationMaskAllButUpsideDown;
#endif
}
- (BOOL) shouldAutorotate {
return YES;
}
- (void)didReceiveMemoryWarning {
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
- (void)viewDidUnload {
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (void)dealloc {
[super dealloc];
}
@end

View File

@ -0,0 +1,11 @@
#import <UIKit/UIKit.h>
int main(int argc, char *argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
int retVal = UIApplicationMain(argc, argv, nil, @"AppController");
[pool release];
return retVal;
}