From 16304944b423f6daed7804422395406599a7d576 Mon Sep 17 00:00:00 2001 From: Travis Bradshaw Date: Tue, 31 Jan 2012 16:57:34 -0600 Subject: [PATCH] Source release of Wolfenstein 3D Classic Platinum for iOS, 1.2 --- wolf3d/code/env/common.h | 1 + wolf3d/code/env/fileio.c | 14 +- wolf3d/code/env/files.c | 18 +- wolf3d/code/env/filesystem.h | 1 + wolf3d/code/env/sound.c | 2 +- wolf3d/code/env/sound_sfx_id.c | 36 +- wolf3d/code/env/texture_manager.c | 47 +- wolf3d/code/iphone/EAGLView.h | 15 + wolf3d/code/iphone/EAGLView.m | 126 +- wolf3d/code/iphone/FSCopyObject.c | 1986 +++++++++++ wolf3d/code/iphone/FSCopyObject.h | 258 ++ wolf3d/code/iphone/GenLinkedList.c | 212 ++ wolf3d/code/iphone/GenLinkedList.h | 93 + wolf3d/code/iphone/Info.plist | 30 +- wolf3d/code/iphone/arialGlyphRects.h | 104 + wolf3d/code/iphone/default.png | Bin 182887 -> 186836 bytes wolf3d/code/iphone/gles_glue.c | 8 + wolf3d/code/iphone/hud.c | 334 ++ wolf3d/code/iphone/iphone_alerts.h | 39 + wolf3d/code/iphone/iphone_alerts.m | 110 + wolf3d/code/iphone/iphone_downloadSOD.m | 359 ++ wolf3d/code/iphone/iphone_downloadUserMap.m | 223 ++ wolf3d/code/iphone/iphone_loop.c | 603 +++- wolf3d/code/iphone/iphone_main.c | 41 +- wolf3d/code/iphone/iphone_mapselector.c | 478 +++ wolf3d/code/iphone/iphone_menus.c | 3040 ++++++++++++++++- wolf3d/code/iphone/iphone_wolf.h | 50 +- .../wolf3d.xcodeproj/greghodges.mode1v3 | 1409 ++++++++ .../wolf3d.xcodeproj/greghodges.pbxuser | 578 ++++ .../iphone/wolf3d.xcodeproj/project.pbxproj | 85 +- wolf3d/code/iphone/wolf3dAppDelegate.h | 4 +- wolf3d/code/iphone/wolf3dAppDelegate.m | 228 +- wolf3d/code/iphone/wolf3d_icon.png | Bin 5935 -> 8921 bytes wolf3d/code/wolf/wolf_actor_ai.c | 16 +- wolf3d/code/wolf/wolf_ai_com.c | 9 +- wolf3d/code/wolf/wolf_client_main.c | 61 +- wolf3d/code/wolf/wolf_level.c | 114 +- wolf3d/code/wolf/wolf_local.h | 3 +- wolf3d/code/wolf/wolf_main.c | 4 +- wolf3d/code/wolf/wolf_player.c | 76 +- wolf3d/code/wolf/wolf_player.h | 1 + wolf3d/code/wolf/wolf_powerups.c | 19 +- wolf3d/code/wolf/wolf_pushwalls.c | 2 +- wolf3d/code/wolf/wolf_renderer.c | 13 +- wolf3d/code/wolfiphone.h | 2 + 45 files changed, 10681 insertions(+), 171 deletions(-) create mode 100644 wolf3d/code/iphone/FSCopyObject.c create mode 100644 wolf3d/code/iphone/FSCopyObject.h create mode 100644 wolf3d/code/iphone/GenLinkedList.c create mode 100644 wolf3d/code/iphone/GenLinkedList.h create mode 100644 wolf3d/code/iphone/arialGlyphRects.h create mode 100644 wolf3d/code/iphone/iphone_alerts.h create mode 100644 wolf3d/code/iphone/iphone_alerts.m create mode 100644 wolf3d/code/iphone/iphone_downloadSOD.m create mode 100644 wolf3d/code/iphone/iphone_downloadUserMap.m create mode 100644 wolf3d/code/iphone/iphone_mapselector.c create mode 100644 wolf3d/code/iphone/wolf3d.xcodeproj/greghodges.mode1v3 create mode 100644 wolf3d/code/iphone/wolf3d.xcodeproj/greghodges.pbxuser diff --git a/wolf3d/code/env/common.h b/wolf3d/code/env/common.h index ad0d486..44f5121 100644 --- a/wolf3d/code/env/common.h +++ b/wolf3d/code/env/common.h @@ -181,6 +181,7 @@ extern void Client_Init( void ); #define BUTTON_ATTACK 1 #define BUTTON_USE 2 #define BUTTON_CHANGE_WEAPON 4 +#define BUTTON_ALTERNATE_ATTACK 8 //gsh #define BUTTON_ANY 128 // any key whatsoever diff --git a/wolf3d/code/env/fileio.c b/wolf3d/code/env/fileio.c index 1848e55..de8917b 100644 --- a/wolf3d/code/env/fileio.c +++ b/wolf3d/code/env/fileio.c @@ -216,8 +216,6 @@ PUBLIC filehandle_t *FS_OpenFile( const char *filename, W32 FlagsAndAttributes ) pathBase = iphoneDocDirectory; my_snprintf( netpath, sizeof( netpath ), "%s/%s", pathBase, filename ); } else { -// extern char iphoneAppDirectory[1024]; -// pathBase = iphoneAppDirectory; pathBase = FS_Gamedir(); my_snprintf( netpath, sizeof( netpath ), "%s/%s", pathBase, filename ); } @@ -225,7 +223,17 @@ PUBLIC filehandle_t *FS_OpenFile( const char *filename, W32 FlagsAndAttributes ) // high performance file mapping path, avoiding stdio fd = open( netpath, O_RDONLY ); if ( fd == -1 ) { - return NULL; +// return NULL; + //if it couldn't be found in that path then check again in the document directory + //gsh + //pathBase = FS_ForceGamedir(); + extern char iphoneDocDirectory[1024]; + pathBase = iphoneDocDirectory; + my_snprintf( netpath, sizeof( netpath ), "%s/%s", pathBase, filename ); + fd = open( netpath, O_RDONLY ); + if ( fd == -1 ) { //okay, couldn't find it there either... return null + return NULL; + } } fstat( fd, &s ); diff --git a/wolf3d/code/env/files.c b/wolf3d/code/env/files.c index acd92e2..cb9fba4 100644 --- a/wolf3d/code/env/files.c +++ b/wolf3d/code/env/files.c @@ -60,7 +60,7 @@ PRIVATE char fs_gamedir[ MAX_OSPATH ]; - +//PRIVATE char fs_soddir[ MAX_OSPATH ]; //gsh /* @@ -77,8 +77,24 @@ PRIVATE char fs_gamedir[ MAX_OSPATH ]; */ PUBLIC char *FS_Gamedir( void ) { + /* + //gsh... this is a trick to load in where the iphoneDocDirectory is + if (currentMap.episode >= 6) + { +// sprintf( fs_soddir, "%s/SODbase", iphoneDocDirectory ); //if you're downloading the SOD data + sprintf( fs_soddir, "%s/", iphoneDocDirectory ); //if you're only downloading the spear maps + return fs_soddir; + }*/ + return fs_gamedir; } +/* +//gsh this is so that we can force a non-SOD folder +//it's only getting used in the FSOpenFile() of fileio.c +PUBLIC char *FS_ForceGamedir( void ) +{ + return fs_gamedir; +}*/ /* diff --git a/wolf3d/code/env/filesystem.h b/wolf3d/code/env/filesystem.h index 59bde77..b158fec 100644 --- a/wolf3d/code/env/filesystem.h +++ b/wolf3d/code/env/filesystem.h @@ -45,6 +45,7 @@ extern void FS_InitFilesystem(void); extern char *FS_Gamedir(void); +//extern char *FS_ForceGamedir(void); //gsh diff --git a/wolf3d/code/env/sound.c b/wolf3d/code/env/sound.c index 7ed3524..bd7ecc2 100644 --- a/wolf3d/code/env/sound.c +++ b/wolf3d/code/env/sound.c @@ -690,7 +690,7 @@ PRIVATE void Sound_Register( void ) { s_initSound = Cvar_Get( "s_initSound", "1", CVAR_INIT ); - s_masterVolume = Cvar_Get( "s_masterVolume", "1.0", CVAR_ARCHIVE ); + s_masterVolume = Cvar_Get( "s_masterVolume", "0.3", CVAR_ARCHIVE ); //gsh changed this from "1.0" to "0.3" for the volume hack... otherwise it's too loud s_sfxVolume = Cvar_Get( "s_sfxVolume", "1.0", CVAR_ARCHIVE ); s_musicVolume = Cvar_Get( "s_musicVolume", "1.0", CVAR_ARCHIVE ); s_minDistance = Cvar_Get( "s_minDistance", "0.0", CVAR_ARCHIVE ); diff --git a/wolf3d/code/env/sound_sfx_id.c b/wolf3d/code/env/sound_sfx_id.c index 4c8d11b..6175529 100644 --- a/wolf3d/code/env/sound_sfx_id.c +++ b/wolf3d/code/env/sound_sfx_id.c @@ -248,33 +248,57 @@ PUBLIC void Sound_BeginRegistration( void ) s_registering = true; } + PUBLIC sfx_t *Sound_RegisterSound( const char *name ) { sfx_t *sfx; + bool isSpearSound = false; if( ! sound_initialized ) { return NULL; } - - if( g_version->value == 1 ) + + if( g_version->value == SPEAROFDESTINY && currentMap.episode >= 6 && strncmp(name, "iphone", 6) && currentMap.episode < 9)//added the episode & iphone check... gsh { + isSpearSound = true; + char tempname[ 256 ]; - my_snprintf( tempname, sizeof( tempname ), "sod%s", name ); - sfx = Sound_FindSound( tempname ); + + //gsh + //Com_Printf("Finding Sound: %s\n", tempname); } else { sfx = Sound_FindSound( name ); + + //gsh + //Com_Printf("Finding Sound: %s\n", name); } - +/* + //original if( ! s_registering ) { Sound_LoadSound( sfx ); } - +*/ + //gsh + if( ! s_registering ) + { + //if it couldn't be found and we tried finding it in sod + //then it might exist in wolf3d + if( !Sound_LoadSound( sfx ) && isSpearSound) + { + sfx = Sound_FindSound( name ); + //Com_Printf("Finding Sound Again: %s\n", name); + + if( ! s_registering ) + Sound_LoadSound( sfx ); //try loading again + } + } + return sfx; } diff --git a/wolf3d/code/env/texture_manager.c b/wolf3d/code/env/texture_manager.c index 18c16a8..d960d1d 100644 --- a/wolf3d/code/env/texture_manager.c +++ b/wolf3d/code/env/texture_manager.c @@ -464,14 +464,14 @@ PUBLIC texture_t *TM_FindTexture( const char *name, texturetype_t type ) { return r_notexture; } - + // Check for file extension len = strlen( name ); if( len < 5 ) { return r_notexture; } - + // look for it in the texture cache for( i = 0, tex = ttextures; i < numttextures; ++i, ++tex ) { @@ -490,7 +490,8 @@ PUBLIC texture_t *TM_FindTexture( const char *name, texturetype_t type ) return r_notexture; } -// Com_Printf( "Loading texture: %s\n", name ); + //gsh + //Com_Printf( "Loading texture: %s\n", name ); // look for the pre-digested 5551 version strcpy( digested, name ); @@ -514,7 +515,6 @@ PUBLIC texture_t *TM_FindTexture( const char *name, texturetype_t type ) { GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG, GL_UNSIGNED_BYTE, 2 }, }; - picHeader_t *ph = (picHeader_t *)fh->filedata; int noMips = 0; @@ -606,7 +606,7 @@ PUBLIC texture_t *TM_FindTexture( const char *name, texturetype_t type ) if ( fh == NULL ) { Com_Printf( "Failed to find texture %s\n", name ); return r_notexture; - } + } //else { //added the else...gsh jpgSize = FS_GetFileSize( fh ); jpgData = fh->ptrStart; @@ -616,13 +616,48 @@ PUBLIC texture_t *TM_FindTexture( const char *name, texturetype_t type ) if ( ! data ) { free( jpgData ); return r_notexture; - } + } //else { //added the else tex = TM_LoadTexture( name, data, width, height, type, bytes ); MM_FREE( data ); tex->maxS = tex->maxT = 1.0f; return tex; + + + } + + /* + Com_Printf("Trying to find texture made it to the end\n"); + + //gsh.. couldn't find it... try doing it again, but looking in a new location + if (spritelocation == SODSPRITESDIRNAME && spritelocation != WL6SPRITESDIRNAME) + { + //need to remove the 'sod' + if (strncmp(spritelocation, name, strlen(spritelocation)) == 0) + { + char buffer[64]; + char tempName[64]; + int offset = strlen(spritelocation) + 1; + for (int i = 0; i < strlen(name) - offset; ++i) + { + buffer[i] = name[i+offset]; + } + buffer[i] = '\0'; //just in case + + spritelocation = WL6SPRITESDIRNAME; + + //TODO: + my_snprintf(tempName, sizeof(tempName), "%s/%s", spritelocation, buffer); + + Com_Printf("tempName: %s\n", tempName); + Com_Printf("buffer: %s\n", buffer); + + spritelocation = SODSPRITESDIRNAME; //return to sodsprites + tex = TM_FindTexture( tempName, type); + return tex; + } } + return r_notexture;*/ return NULL; } diff --git a/wolf3d/code/iphone/EAGLView.h b/wolf3d/code/iphone/EAGLView.h index 80e8881..dd2c39a 100644 --- a/wolf3d/code/iphone/EAGLView.h +++ b/wolf3d/code/iphone/EAGLView.h @@ -24,6 +24,13 @@ #import #import + + + +#ifdef VOLUMEHACK +#import +#endif + /* This class wraps the CAEAGLLayer from CoreAnimation into a convenient UIView subclass. The view content is basically an EAGL surface you render your OpenGL scene into. @@ -49,6 +56,14 @@ Note that setting the view non-opaque will only work if the EAGL surface has an NSTimer *animationTimer; NSTimeInterval animationInterval; + //gsh... an attempt at hacking the volume button +#ifdef VOLUMEHACK + MPVolumeView *volumeView; + UISlider *volumeViewSlider; + float lastFramesVolume; +#endif +// NSThread *pLoadThread; + } @property NSTimeInterval animationInterval; diff --git a/wolf3d/code/iphone/EAGLView.m b/wolf3d/code/iphone/EAGLView.m index 13cb97f..7fde3ec 100644 --- a/wolf3d/code/iphone/EAGLView.m +++ b/wolf3d/code/iphone/EAGLView.m @@ -16,6 +16,11 @@ #include "wolfiphone.h" + + +//gsh +//#define NOTIFYLISTEN //uncomment to receive notifications + struct AVSystemControllerPrivate; @interface AVSystemController : NSObject @@ -144,6 +149,40 @@ EAGLView *eaglview; ((void(*)(int))eglSwapInterval)( 2 ); } #endif + +#ifdef VOLUMEHACK + //------------------- + // Volume Button Hack + //gsh + // Note: MediaPlayer framework required for this trick + //create a MPVolumeView to hack the volume button + CGRect frame = CGRectMake(0, -30, 180, 10); //put this thing offscreen + volumeView = [[[MPVolumeView alloc] initWithFrame:frame] autorelease]; + [volumeView sizeToFit]; + [self addSubview:volumeView]; + + // Find the volume view slider + for (UIView *view in [volumeView subviews]){ + if ([[[view class] description] isEqualToString:@"MPVolumeSlider"]) { + volumeViewSlider = (UISlider *)view; + } + } + + //listen for volume changes + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(volumeListener:) + name:@"AVSystemController_SystemVolumeDidChangeNotification" + object:nil]; + + //--------------------- +#endif + +#ifdef NOTIFYLISTEN //gsh + //this is a general purpose listener + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(notificationListener:) + name:nil + object:nil]; +#endif // with swapinterval, we want to update as fast as possible float interval = 1.0 / 30.0f; @@ -155,7 +194,14 @@ EAGLView *eaglview; return self; } +//gsh +- (void)viewDidLoad { + + Com_Printf("\n---------------\nviewDidLoad() called\n---------------\n\n"); +} + - (void)drawView { + [ (wolf3dAppDelegate *)[UIApplication sharedApplication].delegate restartAccelerometerIfNeeded]; #if 0 @@ -175,11 +221,28 @@ EAGLView *eaglview; } } #endif + +#ifdef VOLUMEHACK //------------------ + // volume hack + + + //check for volume adjustments gsh + if ( menuState == IPM_CONTROLS) + { + if (lastFramesVolume != s_masterVolume->value) + { + lastFramesVolume = s_masterVolume->value; + [volumeViewSlider setValue:lastFramesVolume animated:NO]; + [volumeViewSlider _commitVolumeChange]; + } + } +#endif iphoneFrame(); // swapBuffers() will be called from here } + - (void)swapBuffers { // glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer); loggedTimes[iphoneFrameNum&(MAX_LOGGED_TIMES-1)].beforeSwap = Sys_Milliseconds(); @@ -215,7 +278,11 @@ EAGLView *eaglview; - (void) handleTouches:(NSSet*)touches withEvent:(UIEvent*)event { int touchCount = 0; int points[16]; - static int previousTouchCount; + static int previousTouchCount = 0; + + //gsh + if (previousTouchCount == 0) + isTouchMoving = 0; NSSet *t = [event allTouches]; for (UITouch *myTouch in t) @@ -232,10 +299,12 @@ EAGLView *eaglview; } if (myTouch.phase == UITouchPhaseMoved) { // touch moved handler + //gsh, use this for swipe events in the scrolling menus + isTouchMoving = 1; } if (myTouch.phase == UITouchPhaseEnded) { touchCount--; - } + } } // toggle the console with four touches @@ -281,11 +350,62 @@ EAGLView *eaglview; [self handleTouches:touches withEvent:event]; } - - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event { [self handleTouches:touches withEvent:event]; } +// gsh +#ifdef NOTIFYLISTEN +- (void) notificationListener:(NSNotification *)notify +{ + Com_Printf("notificationListener: %s\n", [notify.name UTF8String]); +//NSString + + if ([notify.name isEqualToString:@"CPDistributedMessagingCenterServerDidTerminateNotification"] && menuState == IPM_STOREKIT) + { + iphoneMessageBox("Apple Store Failed", "Connection to app store has terminated. Please try again later."); + menuState = IPM_MAIN; + } + //sometimes after requrestProductData() is called we get these notifications + //and the storekit no longer responds (making it appear as a crash) + /* +notificationListener: CPDistributedMessagingCenterServerDidTerminateNotification +notificationListener: SKNotificationRequestFinished +notificationListener: SKNotificationTransactionsRefreshed +notificationListener: CPDistributedMessagingCenterServerDidTerminateNotification +*/ +} +#endif + +#ifdef VOLUMEHACK +//------------------- +// Volume Button Hack +// gsh +// currently this is problematic... +// it's slow if the user holds the volume button +// let's see if inlining this with the normal game loop is faster +// than listening for the event... it's not +// Note: MediaPlayer framework required for this trick +//------------------- +- (void) volumeListener:(NSNotification *)notify +{ + //TODO: provide left/right click attacks + if (volumeViewSlider.value < s_masterVolume->value) + { + + [volumeViewSlider setValue:s_masterVolume->value animated:NO];//volumeSetting animated:NO]; + [volumeViewSlider _commitVolumeChange]; //again, ignoring compiler warning complaints + //this might have a warning because it's currently undocumented? + Cvar_Set("volumeFireDown", "1"); + } + else if (volumeViewSlider.value > s_masterVolume->value)//volumeSetting) + { + [volumeViewSlider setValue:s_masterVolume->value animated:NO];//volumeSetting animated:NO]; + [volumeViewSlider _commitVolumeChange]; //again, ignoring compiler warning complaints + Cvar_Set("volumeFireUp", "1"); + } +} +#endif @end diff --git a/wolf3d/code/iphone/FSCopyObject.c b/wolf3d/code/iphone/FSCopyObject.c new file mode 100644 index 0000000..d4580be --- /dev/null +++ b/wolf3d/code/iphone/FSCopyObject.c @@ -0,0 +1,1986 @@ +/* + File: FSCopyObject.c + + Contains: A Copy/Delete Files/Folders engine which uses HFS+ API's. + This code takes some tricks/techniques from MoreFilesX and + MPFileCopy, wraps them all up into an easy to use API, and + adds a bunch of features. It will run on Mac OS 9.1 through + 9.2.x and 10.1.x and up (Classic, Carbon and Mach-O) + + Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. + ("Apple") in consideration of your agreement to the following terms, and your + use, installation, modification or redistribution of this Apple software + constitutes acceptance of these terms. If you do not agree with these terms, + please do not use, install, modify or redistribute this Apple software. + + In consideration of your agreement to abide by the following terms, and subject + to these terms, Apple grants you a personal, non-exclusive license, under AppleÕs + copyrights in this original Apple software (the "Apple Software"), to use, + reproduce, modify and redistribute the Apple Software, with or without + modifications, in source and/or binary forms; provided that if you redistribute + the Apple Software in its entirety and without modifications, you must retain + this notice and the following text and disclaimers in all such redistributions of + the Apple Software. Neither the name, trademarks, service marks or logos of + Apple Computer, Inc. may be used to endorse or promote products derived from the + Apple Software without specific prior written permission from Apple. Except as + expressly stated in this notice, no other rights or licenses, express or implied, + are granted by Apple herein, including but not limited to any patent rights that + may be infringed by your derivative works or by other works in which the Apple + Software may be incorporated. + + The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO + WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED + WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN + COMBINATION WITH YOUR PRODUCTS. + + IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION + OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT + (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN + ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + Copyright © 2002-2004 Apple Computer, Inc., All Rights Reserved +*/ + +#include "FSCopyObject.h" +#include "GenLinkedList.h" +#if !TARGET_API_MAC_OSX +#include +#endif +#include +#include + +#pragma mark ----- Tunable Parameters ----- + +/* The following constants control the behavior of the copy engine. */ + +enum { /* BufferSizeForVolSpeed */ +/* kDefaultCopyBufferSize = 2L * 1024 * 1024,*/ /* 2MB, Fast but not very responsive. */ + kDefaultCopyBufferSize = 256L * 1024, /* 256kB, Slower but can still use machine. */ + kMaximumCopyBufferSize = 2L * 1024 * 1024, + kMinimumCopyBufferSize = 1024 +}; + +enum { /* CheckForDestInsideSrc */ + errFSDestInsideSource = -1234 +}; + +enum { + /* for use with PBHGetDirAccess in IsDropBox */ + kPrivilegesMask = kioACAccessUserWriteMask | kioACAccessUserReadMask | kioACAccessUserSearchMask, + + /* for use with FSGetCatalogInfo and FSPermissionInfo->mode */ + /* from sys/stat.h... note -- sys/stat.h definitions are in octal */ + /* */ + /* You can use these values to adjust the users/groups permissions */ + /* on a file/folder with FSSetCatalogInfo and extracting the */ + /* kFSCatInfoPermissions field. See code below for examples */ + kRWXUserAccessMask = 0x01C0, + kReadAccessUser = 0x0100, + kWriteAccessUser = 0x0080, + kExecuteAccessUser = 0x0040, + + kRWXGroupAccessMask = 0x0038, + kReadAccessGroup = 0x0020, + kWriteAccessGroup = 0x0010, + kExecuteAccessGroup = 0x0008, + + kRWXOtherAccessMask = 0x0007, + kReadAccessOther = 0x0004, + kWriteAccessOther = 0x0002, + kExecuteAccessOther = 0x0001, + + kDropFolderValue = kWriteAccessOther | kExecuteAccessOther +}; + +#define kNumObjects 80 + +#define VolHasCopyFile(volParms) (((volParms)->vMAttrib & (1L << bHasCopyFile)) != 0) + +#pragma mark ----- Struct Definitions ----- + + /* The CopyParams data structure holds the copy buffer used */ + /* when copying the forks over, as well as special case */ + /* info on the destination */ +struct CopyParams { + void *copyBuffer; + ByteCount copyBufferSize; + Boolean copyingToDropFolder; + Boolean copyingToLocalVolume; + Boolean volHasCopyFile; + DupeAction dupeAction; +}; +typedef struct CopyParams CopyParams; + + /* The FilterParams data structure holds the date and info */ + /* that the caller wants passed into the Filter Proc, as well */ + /* as the Filter Proc Pointer itself */ +struct FilterParams { + FSCatalogInfoBitmap whichInfo; + CopyObjectFilterProcPtr filterProcPtr; + Boolean containerChanged; + Boolean wantSpec; + Boolean wantName; + void *yourDataPtr; +}; +typedef struct FilterParams FilterParams; + + /* The ForkTracker data structure holds information about a specific fork, */ + /* specifically the name and the refnum. We use this to build a list of */ + /* all the forks before we start copying them. We need to do this because */ + /* if we're copying into a drop folder, we must open all the forks before */ + /* we start copying data into any of them. */ + /* Plus it's a convenient way to keep track of all the forks... */ +struct ForkTracker { + HFSUniStr255 forkName; + SInt64 forkSize; + SInt16 forkDestRefNum; +}; +typedef struct ForkTracker ForkTracker; +typedef ForkTracker *ForkTrackerPtr; + + /* The FolderListData data structure holds FSRefs to the source and */ + /* coorisponding destination folder, as well as which level its on */ + /* for use in ProcessFolderList. */ +struct FolderListData +{ + FSRef sourceDirRef; + FSRef destDirRef; + UInt32 level; +}; +typedef struct FolderListData FolderListData; + + /* The FSCopyFolderGlobals data structure holds the information needed to */ + /* copy a directory */ +struct FSCopyFolderGlobals +{ + FSRef *sourceDirRef; + FSRef *destDirRef; + + FSCatalogInfo *catInfoList; + FSRef *srcRefList; + HFSUniStr255 *nameList; + + GenLinkedList folderList; + GenIteratorPtr folderListIter; + + CopyParams *copyParams; + FilterParams *filterParams; + Boolean containerChanged; + + ItemCount maxLevels; + ItemCount currentLevel; +}; +typedef struct FSCopyFolderGlobals FSCopyFolderGlobals; + + /* The FSDeleteObjectGlobals data structure holds information needed to */ + /* recursively delete a directory */ +struct FSDeleteObjectGlobals +{ + FSCatalogInfo catalogInfo; /* FSCatalogInfo */ + ItemCount actualObjects; /* number of objects returned */ + OSErr result; /* result */ +}; +typedef struct FSDeleteObjectGlobals FSDeleteObjectGlobals; + +#pragma mark ----- Local Prototypes ----- + +static OSErr FSCopyObjectPreflight ( const FSRef *source, + const FSRef *destDir, + const DupeAction dupeAction, + FSCatalogInfo *sourceCatInfo, + CopyParams *copyParams, /* can be NULL */ + HFSUniStr255 *newObjectName, + FSRef *deleteMeRef, + Boolean *isReplacing, + Boolean *isDirectory ); + +static OSErr FSCopyFile ( const FSRef *source, + const FSRef *destDir, + const FSCatalogInfo *sourceCatInfo, + const HFSUniStr255 *newFileName, + CopyParams *copyParams, + FilterParams *filterParams, + FSRef *newFileRef, /* can be NULL */ + FSSpec *newFileSpec ); /* can be NULL */ + +static OSErr CopyFile ( const FSRef *source, + FSCatalogInfo *sourceCatInfo, + const FSRef *destDir, + const HFSUniStr255 *destName, /* can be NULL */ + CopyParams *copyParams, + FSRef *newRef, /* can be NULL */ + FSSpec *newSpec ); /* can be NULL */ + +static OSErr FSUsePBHCopyFile ( const FSRef *srcFileRef, + const FSRef *dstDirectoryRef, + const HFSUniStr255 *destName, /* can be NULL (no rename during copy) */ + TextEncoding textEncodingHint, + FSRef *newRef, /* can be NULL */ + FSSpec *newSpec ); /* can be NULL */ + +static OSErr DoCopyFile ( const FSRef *source, + FSCatalogInfo *sourceCatInfo, + const FSRef *destDir, + const HFSUniStr255 *destName, + CopyParams *params, + FSRef *newRef, /* can be NULL */ + FSSpec *newSpec ); /* can be NULL */ + +static OSErr FSCopyFolder ( const FSRef *source, + const FSRef *destDir, + const FSCatalogInfo *sourceCatInfo, + const HFSUniStr255 *newFoldName, + CopyParams *copyParams, + FilterParams *filterParams, + ItemCount maxLevels, + FSRef *outDirRef, /* can be NULL */ + FSSpec *outDirSpec ); /* can be NULL */ + +static OSErr ProcessFolderList ( FSCopyFolderGlobals *folderGlobals ); + +static OSErr CopyFolder ( FSCopyFolderGlobals *folderGlobals ); + +static OSErr CheckForDestInsideSrc ( const FSRef *source, + const FSRef *destDir ); + +static OSErr CopyForks ( const FSRef *source, + const FSRef *dest, + CopyParams *params ); + +static OSErr CopyForksToDisk ( const FSRef *source, + const FSRef *dest, + CopyParams *params ); + +static OSErr CopyForksToDropBox ( const FSRef *source, + const FSRef *dest, + CopyParams *params ); + +static OSErr OpenAllForks ( const FSRef *dest, + GenLinkedList *forkList ); + +static OSErr WriteFork ( const SInt16 srcRefNum, + const SInt16 destRefNum, + const CopyParams *params, + const SInt64 forkSize ); + +static UInt32 CalcBufferSizeForVol ( const GetVolParmsInfoBuffer *volParms, + UInt32 volParmsSize ); + +static UInt32 BufferSizeForVolSpeed ( UInt32 volumeBytesPerSecond ); + +static OSErr FSDeleteFolder ( const FSRef *container ); + +static void FSDeleteFolderLevel ( const FSRef *container, + FSDeleteObjectGlobals *theGlobals ); + +static OSErr IsDropBox ( const FSRef *source, + Boolean *isDropBox ); + +static OSErr GetMagicBusyCreateDate( UTCDateTime *date ); + +static OSErr FSGetVRefNum ( const FSRef *ref, + FSVolumeRefNum *vRefNum ); + +static OSErr FSGetVolParms ( FSVolumeRefNum volRefNum, + UInt32 bufferSize, + GetVolParmsInfoBuffer*volParmsInfo, + UInt32 *actualInfoSize ); /* Can Be NULL */ + +static OSErr UniStrToPStr ( const HFSUniStr255 *uniStr, + TextEncoding textEncodingHint, + Boolean isVolumeName, + Str255 pStr ); + +static OSErr FSMakeFSRef ( FSVolumeRefNum volRefNum, + SInt32 dirID, + ConstStr255Param name, + FSRef *ref ); + +static OSErr SetupDestination ( const FSRef *destDir, + const DupeAction dupeAction, + HFSUniStr255 *sourceName, + FSRef *deleteMeRef, + Boolean *isReplacing); + +static OSErr GetUniqueName ( const FSRef *destDir, + HFSUniStr255 *sourceName ); + +static OSErr GetObjectName ( const FSRef *sourceRef, + HFSUniStr255 *sourceName, + TextEncoding *sourceEncoding ); + +static OSErr CreateFolder ( const FSRef *sourceRef, + const FSRef *destDirRef, + const FSCatalogInfo *catalogInfo, + const HFSUniStr255 *folderName, + CopyParams *params, + FSRef *newFSRefPtr, + FSSpec *newFSSpecPtr ); + +static OSErr DoCreateFolder ( const FSRef *sourceRef, + const FSRef *destDirRef, + const FSCatalogInfo *catalogInfo, + const HFSUniStr255 *folderName, + CopyParams *params, + FSRef *newFSRefPtr, + FSSpec *newFSSpecPtr); + +static pascal void MyDisposeDataProc ( void *pData ); + +static pascal void MyCloseForkProc ( void *pData ); + +/*****************************************************************************/ +/*****************************************************************************/ +/*****************************************************************************/ + +#pragma mark ----- Copy Objects ----- + + /* This routine acts as the top level of the copy engine. */ +OSErr FSCopyObject( const FSRef *source, + const FSRef *destDir, + ItemCount maxLevels, + FSCatalogInfoBitmap whichInfo, + DupeAction dupeAction, + const HFSUniStr255 *newObjectName, /* can be NULL */ + Boolean wantFSSpec, + Boolean wantName, + CopyObjectFilterProcPtr filterProcPtr, /* can be NULL */ + void *yourDataPtr, /* can be NULL */ + FSRef *newObjectRef, /* can be NULL */ + FSSpec *newObjectSpec) /* can be NULL */ +{ + CopyParams copyParams; + FilterParams filterParams; + FSCatalogInfo sourceCatInfo; + HFSUniStr255 sourceName, + tmpObjectName; + FSRef tmpObjectRef, + deleteMeRef; + Boolean isDirectory, + isReplacing = false; + OSErr err = ( source != NULL && destDir != NULL ) ? noErr : paramErr; + + /* Zero out these two FSRefs in case an error occurs before or */ + /* inside FSCopyObjectPreflight. Paranoia mainly... */ + BlockZero( &deleteMeRef, sizeof( FSRef ) ); + BlockZero( &tmpObjectRef, sizeof( FSRef ) ); + + /* setup filterParams */ + filterParams.whichInfo = whichInfo; + filterParams.filterProcPtr = filterProcPtr; + filterParams.wantSpec = ( filterProcPtr && wantFSSpec ); /* only get this info if */ + filterParams.wantName = ( filterProcPtr && wantName ); /* a filterProc is provied */ + filterParams.yourDataPtr = yourDataPtr; + + /* Get and store away the name of the source object */ + /* and setup the initial name of the new object */ + if( err == noErr ) + err = GetObjectName( source, &sourceName, NULL ); + if( err == noErr ) + tmpObjectName = (newObjectName != NULL) ? *newObjectName : sourceName; + + if( err == noErr ) /* preflight/prep the destination and our internal variables */ + err = FSCopyObjectPreflight( source, destDir, dupeAction, &sourceCatInfo, ©Params, &tmpObjectName, &deleteMeRef, &isReplacing, &isDirectory ); + + /* now that we have some info, lets print it */ + if( err == noErr ) + { + dwarning(( "%s -- err: %d, maxLevels: %u, whichInfo: %08x,\n", __FUNCTION__, err, (unsigned int)maxLevels, (int)whichInfo )); + dwarning(( "\t\t\t\tdupeAction: %s, wantSpec: %s, wantName: %s,\n", ((dupeAction == kDupeActionReplace) ? "replace" : ((dupeAction == kDupeActionRename) ? "rename" : "standard")), (filterParams.wantSpec)?"yes":"no", (filterParams.wantName)?"yes":"no" )); + dwarning(( "\t\t\t\tfilterProcPtr: 0x%08x, yourDataPtr: 0x%08x,\n", (unsigned int)filterProcPtr, (unsigned int)yourDataPtr )); + dwarning(( "\t\t\t\tnewObjectRef: 0x%08x, newObjectSpec: 0x%08x,\n", (unsigned int)newObjectRef, (unsigned int)newObjectSpec )); + dwarning(( "\t\t\t\tcopyBufferSize: %dkB, isDirectory: %s, isLocal: %s,\n", (int)copyParams.copyBufferSize/1024, (isDirectory)?"yes":"no", (copyParams.copyingToLocalVolume)?"yes":"no" )); + dwarning(( "\t\t\t\tisDropBox: %s, PBHCopyFileSync supported: %s\n\n", (copyParams.copyingToDropFolder)?"yes":"no", (copyParams.volHasCopyFile)?"yes":"no" )); + } + + if( err == noErr ) /* now copy the file/folder... */ + { /* is it a folder? */ + if ( isDirectory ) + { /* yes */ + err = CheckForDestInsideSrc(source, destDir); + if( err == noErr ) + err = FSCopyFolder( source, destDir, &sourceCatInfo, &tmpObjectName, ©Params, &filterParams, maxLevels, &tmpObjectRef, newObjectSpec ); + } + else /* no */ + err = FSCopyFile(source, destDir, &sourceCatInfo, &tmpObjectName, ©Params, &filterParams, &tmpObjectRef, newObjectSpec); + } + + /* if an object existed in the destination with the same name as */ + /* the source and the caller wants to replace it, we had renamed it */ + /* to ".DeleteMe" earlier. If no errors, we delete it, else delete */ + /* the one we just created and rename the origenal back to its */ + /* origenal name. */ + /* */ + /* This is done mainly to cover the case of the source being in the */ + /* destination directory when kDupeActionReplace is selected */ + /* (3188701) */ + if( copyParams.dupeAction == kDupeActionReplace && isReplacing == true ) + { + dwarning(("%s -- Cleaning up, this might take a moment. err : %d\n", __FUNCTION__, err)); + + if( err == noErr ) + err = FSDeleteObjects( &deleteMeRef ); + else + { /* not much we can do if the delete or rename fails, we need to preserve */ + /* the origenal error code that got us here. */ + /* */ + /* If an error occurs before or inside SetupDestination, newFileRef and */ + /* deleteMeRef will be invalid so the delete and rename will simply fail */ + /* leaving the source and destination unchanged */ + myverify_noerr( FSDeleteObjects( &tmpObjectRef ) ); + myverify_noerr( FSRenameUnicode( &deleteMeRef, sourceName.length, sourceName.unicode, sourceCatInfo.textEncodingHint, NULL ) ); + } + } + + if( err == noErr && newObjectRef != NULL ) + *newObjectRef = tmpObjectRef; + + /* Clean up for space and safety... Who me? */ + if( copyParams.copyBuffer != NULL ) + DisposePtr((char*)copyParams.copyBuffer); + + mycheck_noerr( err ); + + return err; +} + +/*****************************************************************************/ + + /* Does a little preflighting (as the name suggests) to figure out the optimal */ + /* buffer size, if its a drop box, on a remote volume etc */ +static OSErr FSCopyObjectPreflight( const FSRef *source, + const FSRef *destDir, + const DupeAction dupeAction, + FSCatalogInfo *sourceCatInfo, + CopyParams *copyParams, + HFSUniStr255 *newObjectName, + FSRef *deleteMeRef, + Boolean *isReplacing, + Boolean *isDirectory) +{ + GetVolParmsInfoBuffer srcVolParms, + destVolParms; + UInt32 srcVolParmsSize = 0, + destVolParmsSize = 0; + FSVolumeRefNum srcVRefNum, + destVRefNum; + OSErr err = ( source != NULL && destDir != NULL && + sourceCatInfo != NULL && copyParams != NULL && + newObjectName != NULL && deleteMeRef != NULL && + isDirectory != NULL ) ? noErr : paramErr; + + BlockZero( copyParams, sizeof( CopyParams ) ); + + copyParams->dupeAction = dupeAction; + + if( err == noErr ) /* Get the info we will need later about the source object */ + err = FSGetCatalogInfo( source, kFSCatInfoSettableInfo, sourceCatInfo, NULL, NULL, NULL ); + if( err == noErr ) /* get the source's vRefNum */ + err = FSGetVRefNum( source, &srcVRefNum ); + if( err == noErr ) /* get the source's volParams */ + err = FSGetVolParms( srcVRefNum, sizeof(GetVolParmsInfoBuffer), &srcVolParms, &srcVolParmsSize ); + if( err == noErr ) /* get the destination's vRefNum */ + err = FSGetVRefNum( destDir, &destVRefNum ); + if( err == noErr ) + { + /* Calculate the optimal copy buffer size for the src vol */ + copyParams->copyBufferSize = CalcBufferSizeForVol( &srcVolParms, srcVolParmsSize ); + + /* if src and dest on different volumes, get its vol parms */ + /* and calculate its optimal buffer size */ + /* else destVolParms = srcVolParms */ + if( srcVRefNum != destVRefNum ) + { + err = FSGetVolParms( destVRefNum, sizeof(GetVolParmsInfoBuffer), &destVolParms, &destVolParmsSize ); + if( err == noErr ) + { + ByteCount tmpBufferSize = CalcBufferSizeForVol( &destVolParms, destVolParmsSize ); + if( tmpBufferSize < copyParams->copyBufferSize ) + copyParams->copyBufferSize = tmpBufferSize; + } + } + else + destVolParms = srcVolParms; + } + if( err == noErr ) + err = ((copyParams->copyBuffer = NewPtr( copyParams->copyBufferSize )) != NULL ) ? noErr : MemError(); + + /* figure out if source is a file or folder */ + /* if it is on a local volume, */ + /* if destination is a drop box */ + /* if source and dest are on same server */ + /* and if it supports PBHCopyFile */ + if( err == noErr ) /* is the destination a Drop Box */ + err = IsDropBox( destDir, ©Params->copyingToDropFolder ); + if( err == noErr ) + { + /* Is it a directory */ + *isDirectory = ((sourceCatInfo->nodeFlags & kFSNodeIsDirectoryMask) != 0); + /* destVolParms.vMServerAdr is non-zero for remote volumes */ + copyParams->copyingToLocalVolume = (destVolParms.vMServerAdr == 0); + if( !copyParams->copyingToLocalVolume ) + { + /* If the destination is on a remote volume, and source and dest are on */ + /* the same server, then it might support PBHCopyFileSync */ + /* If not, then PBHCopyFileSync won't work */ + + /* figure out if the volumes support PBHCopyFileSync */ + copyParams->volHasCopyFile = ( err == noErr && destVolParms.vMServerAdr == srcVolParms.vMServerAdr ) ? + VolHasCopyFile(&srcVolParms) : false; + } + } + + if( err == noErr ) + err = SetupDestination( destDir, copyParams->dupeAction, newObjectName, deleteMeRef, isReplacing ); + + return err; +} + +#pragma mark ----- Copy Files ----- + +/*****************************************************************************/ + +static OSErr FSCopyFile( const FSRef *source, + const FSRef *destDir, + const FSCatalogInfo *sourceCatInfo, + const HFSUniStr255 *newFileName, + CopyParams *copyParams, + FilterParams *filterParams, + FSRef *outFileRef, + FSSpec *outFileSpec ) +{ + FSCatalogInfo catInfo = *sourceCatInfo; + FSRef newFileRef; + FSSpec newFileSpec; + OSErr err = ( source != NULL && destDir != NULL && + copyParams != NULL && filterParams != NULL ) ? noErr : paramErr; + + /* If you would like a Pre-Copy filter (i.e to weed out objects */ + /* you don't want to copy) you should add it here */ + + if( err == noErr ) /* copy the file over */ + err = CopyFile( source, &catInfo, destDir, newFileName, copyParams, &newFileRef, (filterParams->wantSpec || outFileSpec) ? &newFileSpec : NULL ); + + /* Call the IterateFilterProc _after_ the new file was created even if an error occured. */ + /* Note: if an error occured above, the FSRef and other info might not be valid */ + if( filterParams->filterProcPtr != NULL ) + { + /* get the extra info the user wanted on the new file that we don't have */ + if( err == noErr && (filterParams->whichInfo & ~kFSCatInfoSettableInfo) != kFSCatInfoNone ) + err = FSGetCatalogInfo( &newFileRef, filterParams->whichInfo & ~kFSCatInfoSettableInfo, &catInfo, NULL, NULL, NULL ); + + err = CallCopyObjectFilterProc( filterParams->filterProcPtr, false, 0, err, &catInfo, &newFileRef, + (filterParams->wantSpec) ? &newFileSpec : NULL, + (filterParams->wantName) ? newFileName : NULL, + filterParams->yourDataPtr); + } + + if( err == noErr ) + { + if( outFileRef != NULL ) + *outFileRef = newFileRef; + if( outFileSpec != NULL ) + *outFileSpec = newFileSpec; + } + + mycheck_noerr(err); + + return err; +} + +/*****************************************************************************/ + +static OSErr CopyFile( const FSRef *source, + FSCatalogInfo *sourceCatInfo, + const FSRef *destDir, + const HFSUniStr255 *destName, /* can be NULL */ + CopyParams *params, + FSRef *newFile, /* can be NULL */ + FSSpec *newSpec ) /* can be NULL */ +{ + OSErr err = paramErr; + + /* Clear the "inited" bit so that the Finder positions the icon for us. */ + ((FInfo *)(sourceCatInfo->finderInfo))->fdFlags &= ~kHasBeenInited; + + /* if the volumes support PBHCopyFileSync, try to use it */ + if( params->volHasCopyFile == true ) + err = FSUsePBHCopyFile( source, destDir, destName, kTextEncodingUnknown, newFile, newSpec ); + + /* if PBHCopyFile didn't work or not supported, */ + if( err != noErr ) /* then try old school file transfer */ + err = DoCopyFile( source, sourceCatInfo, destDir, destName, params, newFile, newSpec ); + + mycheck_noerr(err); + + return err; +} + +/*****************************************************************************/ + + /* Wrapper function for PBHCopyFileSync */ +static OSErr FSUsePBHCopyFile( const FSRef *srcFileRef, + const FSRef *dstDirectoryRef, + const HFSUniStr255 *destName, /* can be NULL */ + TextEncoding textEncodingHint, + FSRef *newRef, /* can be NULL */ + FSSpec *newSpec) /* can be NULL */ +{ + FSSpec srcFileSpec; + FSCatalogInfo catalogInfo; + HParamBlockRec pb; + Str255 hfsName; + OSErr err = ( srcFileRef != NULL && dstDirectoryRef != NULL ) ? noErr : paramErr; + + if( err == noErr ) /* get FSSpec of source FSRef */ + err = FSGetCatalogInfo(srcFileRef, kFSCatInfoNone, NULL, NULL, &srcFileSpec, NULL); + if( err == noErr ) /* get the destination vRefNum and nodeID (nodeID is the dirID) */ + err = FSGetCatalogInfo(dstDirectoryRef, kFSCatInfoVolume | kFSCatInfoNodeID, &catalogInfo, NULL, NULL, NULL); + if( err == noErr ) /* gather all the info needed */ + { + pb.copyParam.ioVRefNum = srcFileSpec.vRefNum; + pb.copyParam.ioDirID = srcFileSpec.parID; + pb.copyParam.ioNamePtr = (StringPtr)srcFileSpec.name; + pb.copyParam.ioDstVRefNum = catalogInfo.volume; + pb.copyParam.ioNewDirID = (long)catalogInfo.nodeID; + pb.copyParam.ioNewName = NULL; + if( destName != NULL ) + err = UniStrToPStr( destName, textEncodingHint, false, hfsName ); + pb.copyParam.ioCopyName = ( destName != NULL && err == noErr ) ? hfsName : NULL; + } + if( err == noErr ) /* tell the server to copy the object */ + err = PBHCopyFileSync(&pb); + + if( err == noErr ) + { + if( newSpec != NULL ) /* caller wants an FSSpec, so make it */ + myverify_noerr(FSMakeFSSpec( pb.copyParam.ioDstVRefNum, pb.copyParam.ioNewDirID, pb.copyParam.ioCopyName, newSpec)); + if( newRef != NULL ) /* caller wants an FSRef, so make it */ + myverify_noerr(FSMakeFSRef( pb.copyParam.ioDstVRefNum, pb.copyParam.ioNewDirID, pb.copyParam.ioCopyName, newRef)); + } + + if( err != paramErr ) /* returning paramErr is ok, it means PBHCopyFileSync was not supported */ + mycheck_noerr(err); + + return err; +} + +/*****************************************************************************/ + + /* Copies a file referenced by source to the directory referenced by */ + /* destDir. destName is the name the file we are going to copy to the */ + /* destination. sourceCatInfo is the catalog info of the file, which */ + /* is passed in as an optimization (we could get it by doing a */ + /* FSGetCatalogInfo but the caller has already done that so we might as */ + /* well take advantage of that). */ + /* */ +static OSErr DoCopyFile(const FSRef *source, + FSCatalogInfo *sourceCatInfo, + const FSRef *destDir, + const HFSUniStr255 *destName, + CopyParams *params, + FSRef *newRef, + FSSpec *newSpec ) +{ + FSRef dest; + FSSpec tmpSpec; + FSPermissionInfo originalPermissions; + OSType originalFileType = 'xxxx'; + UInt16 originalNodeFlags = kFSCatInfoNone; + Boolean getSpec; + OSErr err = noErr; + + /* If we're copying to a drop folder, we won't be able to reset this */ + /* information once the copy is done, so we don't mess it up in */ + /* the first place. We still clear the locked bit though; items dropped */ + /* into a drop folder always become unlocked. */ + if (!params->copyingToDropFolder) + { + /* Remember to clear the file's type, so the Finder doesn't */ + /* look at the file until we're done. */ + originalFileType = ((FInfo *) &sourceCatInfo->finderInfo)->fdType; + ((FInfo *) &sourceCatInfo->finderInfo)->fdType = kFirstMagicBusyFiletype; + + /* Remember and clear the file's locked status, so that we can */ + /* actually write the forks we're about to create. */ + originalNodeFlags = sourceCatInfo->nodeFlags; + } + sourceCatInfo->nodeFlags &= ~kFSNodeLockedMask; + + /* figure out if we should get the FSSpec to the new file or not */ + /* If the caller asked for it, or if we need it for symlinks */ + getSpec = ( ( newSpec != NULL ) || ( !params->copyingToDropFolder && originalFileType == 'slnk' && ((FInfo *) &sourceCatInfo->finderInfo)->fdCreator == 'rhap' ) ); + + /* we need to have user level read/write/execute access to the file we are */ + /* going to create otherwise FSCreateFileUnicode will return */ + /* -5000 (afpAccessDenied), and the FSRef returned will be invalid, yet */ + /* the file is created (size 0k)... bug? */ + originalPermissions = *((FSPermissionInfo*)sourceCatInfo->permissions); + ((FSPermissionInfo*)sourceCatInfo->permissions)->mode |= kRWXUserAccessMask; + + /* Classic only supports 9.1 and higher, so we don't have to worry */ + /* about 2397324 */ + if( err == noErr ) + err = FSCreateFileUnicode(destDir, destName->length, destName->unicode, kFSCatInfoSettableInfo, sourceCatInfo, &dest, ( getSpec ) ? &tmpSpec : NULL ); + if( err == noErr ) /* Copy the forks over to the new file */ + err = CopyForks(source, &dest, params); + + /* Restore the original file type, creation and modification dates, */ + /* locked status and permissions. */ + /* This is one of the places where we need to handle drop */ + /* folders as a special case because this FSSetCatalogInfo will fail for */ + /* an item in a drop folder, so we don't even attempt it. */ + if (err == noErr && !params->copyingToDropFolder) + { + ((FInfo *) &sourceCatInfo->finderInfo)->fdType = originalFileType; + sourceCatInfo->nodeFlags = originalNodeFlags; + *((FSPermissionInfo*)sourceCatInfo->permissions) = originalPermissions; + + /* 2796751, FSSetCatalogInfo returns -36 when setting the Finder Info */ + /* for a symlink. To workaround this, when the file is a */ + /* symlink (slnk/rhap) we will finish the copy in two steps. First */ + /* setting everything but the Finder Info on the file, then calling */ + /* FSpSetFInfo to set the Finder Info for the file. I would rather use */ + /* an FSRef function to set the Finder Info, but FSSetCatalogInfo is */ + /* the only one... catch-22... */ + /* */ + /* The Carbon File Manager always sets the type/creator of a symlink to */ + /* slnk/rhap if the file is a symlink we do the two step, if it isn't */ + /* we use FSSetCatalogInfo to do all the work. */ + if ((originalFileType == 'slnk') && (((FInfo *) &sourceCatInfo->finderInfo)->fdCreator == 'rhap')) + { /* Its a symlink */ + /* set all the info, except the Finder info */ + err = FSSetCatalogInfo(&dest, kFSCatInfoNodeFlags | kFSCatInfoPermissions, sourceCatInfo); + if ( err == noErr ) /* set the Finder Info to that file */ + err = FSpSetFInfo( &tmpSpec, ((FInfo *) &sourceCatInfo->finderInfo) ); + } + else /* its a regular file */ + err = FSSetCatalogInfo(&dest, kFSCatInfoNodeFlags | kFSCatInfoFinderInfo | kFSCatInfoPermissions, sourceCatInfo); + } + + /* If we created the file and the copy failed, try to clean up by */ + /* deleting the file we created. We do this because, while it's */ + /* possible for the copy to fail halfway through and the File Manager */ + /* doesn't really clean up that well in that case, we *really* don't want */ + /* any half-created files being left around. */ + /* if the file already existed, we don't want to delete it */ + if( err == noErr || err == dupFNErr ) + { /* if everything was fine, then return the new file Spec/Ref */ + if( newRef != NULL ) + *newRef = dest; + if( newSpec != NULL ) + *newSpec = tmpSpec; + } + else + myverify_noerr( FSDeleteObjects(&dest) ); + + mycheck_noerr(err); + + return err; +} + +/*****************************************************************************/ + +#pragma mark ----- Copy Folders ----- + +static OSErr FSCopyFolder( const FSRef *source, + const FSRef *destDir, + const FSCatalogInfo *sourceCatInfo, + const HFSUniStr255 *newObjectName, + CopyParams *copyParams, + FilterParams *filterParams, + ItemCount maxLevels, + FSRef *outDirRef, + FSSpec *outDirSpec ) +{ + FSCopyFolderGlobals folderGlobals; + FolderListData *tmpListData = NULL; + FSCatalogInfo catInfo = *sourceCatInfo; + FSRef newDirRef; + FSSpec newDirSpec; + OSErr err; + + /* setup folder globals */ + folderGlobals.catInfoList = (FSCatalogInfo*) NewPtr( sizeof( FSCatalogInfo ) * kNumObjects ); + folderGlobals.srcRefList = (FSRef*) NewPtr( sizeof( FSRef ) * kNumObjects ); + folderGlobals.nameList = (HFSUniStr255*) NewPtr( sizeof( HFSUniStr255 ) * kNumObjects ); + folderGlobals.folderListIter = NULL; + folderGlobals.copyParams = copyParams; + folderGlobals.filterParams = filterParams; + folderGlobals.maxLevels = maxLevels; + folderGlobals.currentLevel = 0; + + /* if any of the NewPtr calls failed, we MUST bail */ + err = ( folderGlobals.catInfoList != NULL && + folderGlobals.srcRefList != NULL && + folderGlobals.nameList != NULL ) ? noErr : memFullErr; + + /* init the linked list we will use to keep track of the folders */ + InitLinkedList( &folderGlobals.folderList, MyDisposeDataProc ); + + if( err == noErr && !copyParams->copyingToDropFolder ) + err = GetMagicBusyCreateDate( &catInfo.createDate ); + if( err == noErr ) /* create the directory */ + err = DoCreateFolder( source, destDir, &catInfo, newObjectName, folderGlobals.copyParams, &newDirRef, (filterParams->wantSpec || outDirSpec ) ? &newDirSpec : NULL ); + + /* Note: if an error occured above, the FSRef and other info might not be valid */ + if( filterParams->filterProcPtr != NULL ) + { + /* get the info the user wanted about the source directory we don't have */ + if( err == noErr && (filterParams->whichInfo & ~kFSCatInfoSettableInfo) != kFSCatInfoNone ) + err = FSGetCatalogInfo(&newDirRef, filterParams->whichInfo & ~kFSCatInfoSettableInfo, &catInfo, NULL, NULL, NULL); + + err = CallCopyObjectFilterProc(filterParams->filterProcPtr, false, folderGlobals.currentLevel, + err, &catInfo, &newDirRef, + ( filterParams->wantSpec ) ? &newDirSpec : NULL, + ( filterParams->wantName ) ? newObjectName : NULL, + filterParams->yourDataPtr); + } + if( err == noErr ) /* create the memory for this folder */ + err = ( ( tmpListData = (FolderListData*) NewPtr( sizeof( FolderListData ) ) ) != NULL ) ? noErr : MemError(); + if( err == noErr ) + { /* setup the folder info */ + tmpListData->sourceDirRef = *source; + tmpListData->destDirRef = newDirRef; + tmpListData->level = folderGlobals.currentLevel; + /* add this folder to the list to give ProcessFolderList something to chew on */ + err = AddToTail( &folderGlobals.folderList, tmpListData ); + if( err == noErr ) /* tmpListData added successfully */ + err = ProcessFolderList( &folderGlobals ); + else /* error occured, so dispose of memory */ + DisposePtr( (char*) tmpListData ); + } + + dwarning(("\n%s -- %u folders were found\n", __FUNCTION__, (unsigned int)GetNumberOfItems( &folderGlobals.folderList ) )); + + /* when we're done destroy the list and free up any memory we allocated */ + DestroyList( &folderGlobals.folderList ); + + /* now that the copy is complete, we can set things back to normal */ + /* for the directory we just created. */ + /* We have to do this only for the top directory of the copy */ + /* all subdirectories were created all at once */ + if( err == noErr && !folderGlobals.copyParams->copyingToDropFolder ) + err = FSSetCatalogInfo( &newDirRef, kFSCatInfoCreateDate | kFSCatInfoPermissions, sourceCatInfo ); + + /* Copy went as planned, and caller wants an FSRef/FSSpec to the new directory */ + if( err == noErr ) + { + if( outDirRef != NULL) + *outDirRef = newDirRef; + if( outDirSpec != NULL ) + *outDirSpec = newDirSpec; + } + + /* clean up for space and safety, who me? */ + if( folderGlobals.catInfoList ) + DisposePtr( (char*) folderGlobals.catInfoList ); + if( folderGlobals.srcRefList ) + DisposePtr( (char*) folderGlobals.srcRefList ); + if( folderGlobals.nameList ) + DisposePtr( (char*) folderGlobals.nameList ); + + mycheck_noerr(err); + + return ( err ); +} + +/*****************************************************************************/ + + /* We now store a list of all the folders/subfolders we encounter in the source */ + /* Each node in the list contains an FSRef to the source, an FSRef to the */ + /* mirror folder in the destination, and the level in the source that folder */ + /* is on. This is done so that we can use FSGetCatalogInfoBulk to its full */ + /* potential (getting items in bulk). We copy the source one folder at a time. */ + /* Copying over the contents of each folder before we continue on to the next */ + /* folder in the list. This allows us to use the File Manager's own caching */ + /* system to our advantage. */ +static OSErr ProcessFolderList( FSCopyFolderGlobals *folderGlobals ) +{ + FolderListData *folderListData; + OSErr err = noErr; + + /* iterate through the list of folders and copy over each one individually */ + for( InitIterator( &folderGlobals->folderList, &folderGlobals->folderListIter ); folderGlobals->folderListIter != NULL && err == noErr; Next( &folderGlobals->folderListIter ) ) + { + /* Get the data for this folder */ + folderListData = (FolderListData*) GetData( folderGlobals->folderListIter ); + if( folderListData != NULL ) + { + #if DEBUG && !TARGET_API_MAC_OS8 + { + char path[1024]; + myverify_noerr(FSRefMakePath( &(folderListData->sourceDirRef), (unsigned char*)path, 1024 )); + dwarning(("\n\n%s -- Copying contents of\n\t%s\n", __FUNCTION__, path)); + myverify_noerr(FSRefMakePath( &(folderListData->destDirRef), (unsigned char*)path, 1024 )); + dwarning(("\t\tto\n\t%s\n", path)); + } + #endif + + /* stuff the data into our globals */ + folderGlobals->sourceDirRef = &(folderListData->sourceDirRef); + folderGlobals->destDirRef = &(folderListData->destDirRef); + folderGlobals->currentLevel = folderListData->level; + + /* Copy over this folder and add any subfolders to our list of folders */ + /* so they will get processed later */ + err = CopyFolder( folderGlobals ); + } + } + + return err; +} + +/*****************************************************************************/ + + /* Copy the contents of the source into the destination. If any subfolders */ + /* are found, add them to a local list of folders during the loop stage */ + /* Once the copy is done, insert the local list into the global list right */ + /* after the current position in the list. This is done so we don't jump */ + /* all over the disk getting the different folders to copy */ +static OSErr CopyFolder( FSCopyFolderGlobals *folderGlobals ) +{ + GenLinkedList tmpList; + FolderListData *tmpListData = NULL; + FilterParams *filterPtr = folderGlobals->filterParams; + FSIterator iterator; + FSRef newRef; + FSSpec newSpec; + UInt32 actualObjects; + OSErr err, + junkErr; + int i; + + /* Init the local list */ + InitLinkedList( &tmpList, MyDisposeDataProc); + + err = FSOpenIterator( folderGlobals->sourceDirRef, kFSIterateFlat, &iterator ); + if( err == noErr ) + { + do + { + /* grab a bunch of objects (kNumObjects) from this folder and copy them over */ + err = FSGetCatalogInfoBulk( iterator, kNumObjects, &actualObjects, &filterPtr->containerChanged, + kFSCatInfoSettableInfo, folderGlobals->catInfoList, folderGlobals->srcRefList, + NULL, folderGlobals->nameList ); + if( ( err == noErr || err == errFSNoMoreItems ) && + ( actualObjects != 0 ) ) + { + dwarning(("%s -- actualObjects retrieved from FSGetCatalogInfoBulk: %u\n",__FUNCTION__, (unsigned int)actualObjects )); + + /* iterate over the objects actually returned */ + for( i = 0; i < actualObjects; i++ ) + { + /* Any errors in here will be passed to the filter proc */ + /* we don't want an error in here to prematurely cancel the copy */ + + /* If you would like a Pre-Copy filter (i.e to weed out objects */ + /* you don't want to copy) you should add it here */ + + /* Is the new object a directory? */ + if( ( folderGlobals->catInfoList[i].nodeFlags & kFSNodeIsDirectoryMask ) != 0 ) + { /* yes */ + junkErr = CreateFolder( &folderGlobals->srcRefList[i], folderGlobals->destDirRef, + &folderGlobals->catInfoList[i], &folderGlobals->nameList[i], + folderGlobals->copyParams, &newRef, (filterPtr->wantSpec) ? &newSpec : NULL ); + /* If maxLevels is zero, we aren't checking levels */ + /* If currentLevel+1 < maxLevels, add this folder to the list */ + if( folderGlobals->maxLevels == 0 || (folderGlobals->currentLevel + 1) < folderGlobals->maxLevels ) + { + if( junkErr == noErr ) /* Create memory for folder list data */ + junkErr = ( ( tmpListData = (FolderListData*) NewPtr( sizeof( FolderListData ) ) ) != NULL ) ? noErr : MemError(); + if( junkErr == noErr ) + { /* Setup the folder list data */ + tmpListData->sourceDirRef = folderGlobals->srcRefList[i]; + tmpListData->destDirRef = newRef; + tmpListData->level = folderGlobals->currentLevel + 1; + + /* Add it to the local list */ + junkErr = AddToTail( &tmpList, tmpListData ); + } + /* If an error occured and memory was created, we need to dispose of it */ + /* since it was not added to the list */ + if( junkErr != noErr && tmpListData != NULL ) + DisposePtr( (char*) tmpListData ); + } + } + else + { /* no */ + junkErr = CopyFile( &folderGlobals->srcRefList[i], &folderGlobals->catInfoList[i], + folderGlobals->destDirRef, &folderGlobals->nameList[i], + folderGlobals->copyParams, &newRef, ( filterPtr->wantSpec ) ? &newSpec : NULL ); + } + + /* Note: if an error occured above, the FSRef and other info might not be valid */ + if( filterPtr->filterProcPtr != NULL ) + { + if( junkErr == noErr && (filterPtr->whichInfo & ~kFSCatInfoSettableInfo) != kFSCatInfoNone ) /* get the extra info about the new object that the user wanted that we don't already have */ + junkErr = FSGetCatalogInfo( &newRef, filterPtr->whichInfo & ~kFSCatInfoSettableInfo, &folderGlobals->catInfoList[i], NULL, NULL, NULL ); + + err = CallCopyObjectFilterProc( filterPtr->filterProcPtr, filterPtr->containerChanged, + folderGlobals->currentLevel, junkErr, + &folderGlobals->catInfoList[i], &newRef, + ( filterPtr->wantSpec ) ? &newSpec : NULL, + ( filterPtr->wantName ) ? &folderGlobals->nameList[i] : NULL, + filterPtr->yourDataPtr); + } + } + } + }while( err == noErr ); + + /* errFSNoMoreItems is OK - it only means we hit the end of this level */ + /* afpAccessDenied is OK too - it only means we cannot see inside the directory */ + if( err == errFSNoMoreItems || err == afpAccessDenied ) + err = noErr; + + /* Insert the local list of folders from the current folder into our global list. Even */ + /* if no items were added to the local list (due to error, or empty folder), InsertList */ + /* handles it correctly. We add the local list even if an error occurred. It will get */ + /* disposed of when the global list is destroyed. Doesn't hurt to have a couple extra */ + /* steps when we're going to bail anyways. */ + InsertList( &folderGlobals->folderList, &tmpList, folderGlobals->folderListIter ); + + /* Close the FSIterator (closing an open iterator should never fail) */ + (void) FSCloseIterator(iterator); + } + + mycheck_noerr( err ); + + return err; +} + +/*****************************************************************************/ + + /* Determines whether the destination directory is equal to the source */ + /* item, or whether it's nested inside the source item. Returns a */ + /* errFSDestInsideSource if that's the case. We do this to prevent */ + /* endless recursion while copying. */ + /* */ +static OSErr CheckForDestInsideSrc( const FSRef *source, + const FSRef *destDir) +{ + FSRef thisDir = *destDir; + FSCatalogInfo thisDirInfo; + Boolean done = false; + OSErr err; + + do + { + err = FSCompareFSRefs(source, &thisDir); + if (err == noErr) + err = errFSDestInsideSource; + else if (err == diffVolErr) + { + err = noErr; + done = true; + } + else if (err == errFSRefsDifferent) + { + /* This is somewhat tricky. We can ask for the parent of thisDir */ + /* by setting the parentRef parameter to FSGetCatalogInfo but, if */ + /* thisDir is the volume's FSRef, this will give us back junk. */ + /* So we also ask for the parent's dir ID to be returned in the */ + /* FSCatalogInfo record, and then check that against the node */ + /* ID of the root's parent (ie 1). If we match that, we've made */ + /* it to the top of the hierarchy without hitting source, so */ + /* we leave with no error. */ + + err = FSGetCatalogInfo(&thisDir, kFSCatInfoParentDirID, &thisDirInfo, NULL, NULL, &thisDir); + if( ( err == noErr ) && ( thisDirInfo.parentDirID == fsRtParID ) ) + done = true; + } + } while ( err == noErr && ! done ); + + mycheck_noerr( err ); + + return err; +} + +/*****************************************************************************/ + +#pragma mark ----- Copy Forks ----- + + /* This is where the majority of the work is done. I special cased */ + /* DropBoxes in order to use FSIterateForks to its full potential for */ + /* the more common case (read/write permissions). It also simplifies */ + /* the code to have it seperate. */ +static OSErr CopyForks( const FSRef *source, + const FSRef *dest, + CopyParams *params) +{ + OSErr err; + + err = ( !params->copyingToDropFolder ) ? CopyForksToDisk ( source, dest, params ) : + CopyForksToDropBox ( source, dest, params ); + + mycheck_noerr( err ); + + return err; +} + + /* Open each fork individually and copy them over to the destination */ +static OSErr CopyForksToDisk( const FSRef *source, + const FSRef *dest, + CopyParams *params ) +{ + HFSUniStr255 forkName; + CatPositionRec iterator; + SInt64 forkSize; + SInt16 srcRefNum, + destRefNum; + OSErr err; + + /* need to initialize the iterator before using it */ + iterator.initialize = 0; + + do + { + err = FSIterateForks( source, &iterator, &forkName, &forkSize, NULL ); + + /* Create the fork. Note: Data and Resource forks are automatically */ + /* created when the file is created. FSCreateFork returns noErr for them */ + /* We also want to create the fork even if there is no data to preserve */ + /* empty forks */ + if( err == noErr ) + err = FSCreateFork( dest, forkName.length, forkName.unicode ); + + /* Mac OS 9.0 has a bug (in the AppleShare external file system, */ + /* I think) [2410374] that causes FSCreateFork to return an errFSForkExists */ + /* error even though the fork is empty. The following code swallows */ + /* the error (which is harmless) in that case. */ + if( err == errFSForkExists && !params->copyingToLocalVolume ) + err = noErr; + + /* The remainder of this code only applies if there is actual data */ + /* in the source fork. */ + + if( err == noErr && forkSize > 0 ) + { + destRefNum = srcRefNum = 0; + + /* Open the destination fork */ + err = FSOpenFork(dest, forkName.length, forkName.unicode, fsWrPerm, &destRefNum); + if( err == noErr ) /* Open the source fork */ + err = FSOpenFork(source, forkName.length, forkName.unicode, fsRdPerm, &srcRefNum); + if( err == noErr ) /* Write the fork to disk */ + err = WriteFork( srcRefNum, destRefNum, params, forkSize ); + + if( destRefNum != 0 ) /* Close the destination fork */ + myverify_noerr( FSCloseFork( destRefNum ) ); + if( srcRefNum != 0 ) /* Close the source fork */ + myverify_noerr( FSCloseFork( srcRefNum ) ); + } + } + while( err == noErr ); + + if( err == errFSNoMoreItems ) + err = noErr; + + mycheck_noerr( err ); + + return err; +} + + /* If we're copying to a DropBox, we have to handle the copy process a little */ + /* differently then when we are copying to a regular folder. */ +static OSErr CopyForksToDropBox( const FSRef *source, + const FSRef *dest, + CopyParams *params ) +{ + GenLinkedList forkList; + GenIteratorPtr pIter; + ForkTrackerPtr forkPtr; + SInt16 srcRefNum; + OSErr err; + + InitLinkedList( &forkList, MyCloseForkProc ); + + /* If we're copying into a drop folder, open up all of those forks. */ + /* We have to do this because once we've started writing to a fork */ + /* in a drop folder, we can't open any more forks. */ + err = OpenAllForks( dest, &forkList ); + + /* Copy each fork over to the destination */ + for( InitIterator( &forkList, &pIter ); pIter != NULL && err == noErr; Next( &pIter ) ) + { + srcRefNum = 0; + forkPtr = GetData( pIter ); + /* Open the source fork */ + err = FSOpenFork(source, forkPtr->forkName.length, forkPtr->forkName.unicode, fsRdPerm, &srcRefNum); + if( err == noErr ) /* Write the data over */ + err = WriteFork( srcRefNum, forkPtr->forkDestRefNum, params, forkPtr->forkSize ); + + if( srcRefNum != 0 ) /* Close the source fork */ + myverify_noerr( FSCloseFork( srcRefNum ) ); + } + /* we're done, so destroy the list even if an error occured */ + /* the DisposeDataProc will close any open forks */ + DestroyList( &forkList ); + + mycheck_noerr( err ); + + return err; +} + +/*****************************************************************************/ + + /* Create and open all the forks in the destination file. We need to do this when */ + /* we're copying into a drop folder, where you must open all the forks before starting */ + /* to write to any of them. */ + /* */ + /* IMPORTANT: If it fails, this routine won't close forks that opened successfully. */ + /* Make sure that the DisposeDataProc for the forkList closed any open forks */ + /* Or you close each one manually before destroying the list */ +static OSErr OpenAllForks( const FSRef *dest, + GenLinkedList *forkList ) +{ + ForkTrackerPtr forkPtr; + HFSUniStr255 forkName; + CatPositionRec iterator; + SInt64 forkSize; + OSErr err = ( dest != NULL && forkList != NULL ) ? noErr : paramErr; + + /* need to initialize the iterator before using it */ + iterator.initialize = 0; + + /* Iterate over the list of forks */ + while( err == noErr ) + { + forkPtr = NULL; /* init forkPtr */ + + err = FSIterateForks( dest, &iterator, &forkName, &forkSize, NULL ); + if( err == noErr ) + err = ( forkPtr = (ForkTrackerPtr) NewPtr( sizeof( ForkTracker ) ) ) != NULL ? noErr : MemError(); + if( err == noErr ) + { + forkPtr->forkName = forkName; + forkPtr->forkSize = forkSize; + forkPtr->forkDestRefNum = 0; + + /* Create the fork. Note: Data and Resource forks are automatically */ + /* created when the file is created. FSCreateFork returns noErr for them */ + /* We also want to create the fork even if there is no data to preserve */ + /* empty forks */ + err = FSCreateFork( dest, forkName.length, forkName.unicode ); + + /* Swallow afpAccessDenied because this operation causes the external file */ + /* system compatibility shim in Mac OS 9 to generate a GetCatInfo request */ + /* to the AppleShare external file system, which in turn causes an AFP */ + /* GetFileDirParms request on the wire, which the AFP server bounces with */ + /* afpAccessDenied because the file is in a drop folder. As there's no */ + /* native support for non-classic forks in current AFP, there's no way I */ + /* can decide how I should handle this in a non-test case. So I just */ + /* swallow the error and hope that when native AFP support arrives, the */ + /* right thing will happen. */ + if( err == afpAccessDenied ) + err = noErr; + + /* only open the fork if the fork has some data */ + if( err == noErr && forkPtr->forkSize > 0 ) + err = FSOpenFork( dest, forkPtr->forkName.length, forkPtr->forkName.unicode, fsWrPerm, &forkPtr->forkDestRefNum ); + + /* if everything is ok, add this fork to the list */ + if( err == noErr ) + err = AddToTail( forkList, forkPtr ); + } + + if( err != noErr && forkPtr != NULL ) + DisposePtr( (char*) forkPtr ); + } + + if( err == errFSNoMoreItems ) + err = noErr; + + mycheck_noerr( err ); + + return err; +} + +/*****************************************************************************/ + + /* Writes the fork from the source, references by srcRefNum, to the destination fork */ + /* references by destRefNum */ +static OSErr WriteFork( const SInt16 srcRefNum, + const SInt16 destRefNum, + const CopyParams *params, + const SInt64 forkSize ) +{ + UInt64 bytesRemaining; + UInt64 bytesToReadThisTime; + UInt64 bytesToWriteThisTime; + OSErr err; + + + /* Here we create space for the entire fork on the destination volume. */ + /* FSAllocateFork has the right semantics on both traditional Mac OS */ + /* and Mac OS X. On traditional Mac OS it will allocate space for the */ + /* file in one hit without any other special action. On Mac OS X, */ + /* FSAllocateFork is preferable to FSSetForkSize because it prevents */ + /* the system from zero filling the bytes that were added to the end */ + /* of the fork (which would be waste because we're about to write over */ + /* those bytes anyway. */ + err = FSAllocateFork(destRefNum, kFSAllocNoRoundUpMask, fsFromStart, 0, forkSize, NULL); + + /* Copy the file from the source to the destination in chunks of */ + /* no more than params->copyBufferSize bytes. This is fairly */ + /* boring code except for the bytesToReadThisTime/bytesToWriteThisTime */ + /* distinction. On the last chunk, we round bytesToWriteThisTime */ + /* up to the next 512 byte boundary and then, after we exit the loop, */ + /* we set the file's EOF back to the real location (if the fork size */ + /* is not a multiple of 512 bytes). */ + /* */ + /* This technique works around a 'bug' in the traditional Mac OS File Manager, */ + /* where the File Manager will put the last 512-byte block of a large write into */ + /* the cache (even if we specifically request no caching) if that block is not */ + /* full. If the block goes into the cache it will eventually have to be */ + /* flushed, which causes sub-optimal disk performance. */ + /* */ + /* This is only done if the destination volume is local. For a network */ + /* volume, it's better to just write the last bytes directly. */ + /* */ + /* This is extreme over-optimization given the other limits of this */ + /* sample, but I will hopefully get to the other limits eventually. */ + bytesRemaining = forkSize; + while( err == noErr && bytesRemaining != 0 ) + { + if( bytesRemaining > params->copyBufferSize ) + { + bytesToReadThisTime = params->copyBufferSize; + bytesToWriteThisTime = bytesToReadThisTime; + } + else + { + bytesToReadThisTime = bytesRemaining; + bytesToWriteThisTime = ( params->copyingToLocalVolume ) ? + ( (bytesRemaining + 0x01FF ) & ~0x01FF ) : bytesRemaining; + } + + err = FSReadFork( srcRefNum, fsAtMark + noCacheMask, 0, bytesToReadThisTime, params->copyBuffer, NULL ); + if( err == noErr ) + err = FSWriteFork( destRefNum, fsAtMark + noCacheMask, 0, bytesToWriteThisTime, params->copyBuffer, NULL ); + if( err == noErr ) + bytesRemaining -= bytesToReadThisTime; + } + + if (err == noErr && params->copyingToLocalVolume && ( forkSize & 0x01FF ) != 0 ) + err = FSSetForkSize( destRefNum, fsFromStart, forkSize ); + + return err; +} + +/*****************************************************************************/ + +#pragma mark ----- Calculate Buffer Size ----- + + /* This routine calculates the appropriate buffer size for */ + /* the given volParms. It's a simple composition of FSGetVolParms */ + /* BufferSizeForVolSpeed. */ +static UInt32 CalcBufferSizeForVol(const GetVolParmsInfoBuffer *volParms, UInt32 volParmsSize) +{ + UInt32 volumeBytesPerSecond = 0; + + /* Version 1 of the GetVolParmsInfoBuffer included the vMAttrib */ + /* field, so we don't really need to test actualSize. A noErr */ + /* result indicates that we have the info we need. This is */ + /* just a paranoia check. */ + + mycheck(volParmsSize >= offsetof(GetVolParmsInfoBuffer, vMVolumeGrade)); + + /* On the other hand, vMVolumeGrade was not introduced until */ + /* version 2 of the GetVolParmsInfoBuffer, so we have to explicitly */ + /* test whether we got a useful value. */ + + if( ( volParmsSize >= offsetof(GetVolParmsInfoBuffer, vMForeignPrivID) ) && + ( volParms->vMVolumeGrade <= 0 ) ) + { + volumeBytesPerSecond = -volParms->vMVolumeGrade; + } + + return BufferSizeForVolSpeed(volumeBytesPerSecond); +} + +/*****************************************************************************/ + + /* Calculate an appropriate copy buffer size based on the volumes */ + /* rated speed. Our target is to use a buffer that takes 0.25 */ + /* seconds to fill. This is necessary because the volume might be */ + /* mounted over a very slow link (like ARA), and if we do a 256 KB */ + /* read over an ARA link we'll block the File Manager queue for */ + /* so long that other clients (who might have innocently just */ + /* called PBGetCatInfoSync) will block for a noticeable amount of time. */ + /* */ + /* Note that volumeBytesPerSecond might be 0, in which case we assume */ + /* some default value. */ +static UInt32 BufferSizeForVolSpeed(UInt32 volumeBytesPerSecond) +{ + ByteCount bufferSize; + + if (volumeBytesPerSecond == 0) + bufferSize = kDefaultCopyBufferSize; + else + { /* We want to issue a single read that takes 0.25 of a second, */ + /* so devide the bytes per second by 4. */ + bufferSize = volumeBytesPerSecond / 4; + } + + /* Round bufferSize down to 512 byte boundary. */ + bufferSize &= ~0x01FF; + + /* Clip to sensible limits. */ + if (bufferSize < kMinimumCopyBufferSize) + bufferSize = kMinimumCopyBufferSize; + else if (bufferSize > kMaximumCopyBufferSize) + bufferSize = kMaximumCopyBufferSize; + + return bufferSize; +} + +/*****************************************************************************/ + +#pragma mark ----- Delete Objects ----- + +OSErr FSDeleteObjects( const FSRef *source ) +{ + FSCatalogInfo catalogInfo; + OSErr err = ( source != NULL ) ? noErr : paramErr; + + #if DEBUG && !TARGET_API_MAC_OS8 + if( err == noErr ) + { + char path[1024]; + myverify_noerr(FSRefMakePath( source, (unsigned char*)path, 1024 )); + dwarning(("\n%s -- Deleting %s\n", __FUNCTION__, path)); + } + #endif + + /* get nodeFlags for container */ + if( err == noErr ) + err = FSGetCatalogInfo(source, kFSCatInfoNodeFlags, &catalogInfo, NULL, NULL,NULL); + if( err == noErr && (catalogInfo.nodeFlags & kFSNodeIsDirectoryMask) != 0 ) + { /* its a directory, so delete its contents before we delete it */ + err = FSDeleteFolder(source); + } + if( err == noErr && (catalogInfo.nodeFlags & kFSNodeLockedMask) != 0 ) /* is object locked? */ + { /* then attempt to unlock the object (ignore err since FSDeleteObject will set it correctly) */ + catalogInfo.nodeFlags &= ~kFSNodeLockedMask; + (void) FSSetCatalogInfo(source, kFSCatInfoNodeFlags, &catalogInfo); + } + if( err == noErr ) /* delete the object (if it was a directory it is now empty, so we can delete it) */ + err = FSDeleteObject(source); + + mycheck_noerr( err ); + + return ( err ); +} + +/*****************************************************************************/ + +#pragma mark ----- Delete Folders ----- + +static OSErr FSDeleteFolder( const FSRef *container ) +{ + FSDeleteObjectGlobals theGlobals; + + theGlobals.result = ( container != NULL ) ? noErr : paramErr; + + /* delete container's contents */ + if( theGlobals.result == noErr ) + FSDeleteFolderLevel(container, &theGlobals); + + mycheck_noerr( theGlobals.result ); + + return ( theGlobals.result ); +} + +/*****************************************************************************/ + +static void FSDeleteFolderLevel(const FSRef *container, + FSDeleteObjectGlobals *theGlobals ) +{ + FSIterator iterator; + FSRef itemToDelete; + UInt16 nodeFlags; + + /* Open FSIterator for flat access and give delete optimization hint */ + theGlobals->result = FSOpenIterator(container, kFSIterateFlat + kFSIterateDelete, &iterator); + if ( theGlobals->result == noErr ) + { + do /* delete the contents of the directory */ + { + /* get 1 item to delete */ + theGlobals->result = FSGetCatalogInfoBulk( iterator, 1, &theGlobals->actualObjects, + NULL, kFSCatInfoNodeFlags, &theGlobals->catalogInfo, + &itemToDelete, NULL, NULL); + if ( (theGlobals->result == noErr) && (theGlobals->actualObjects == 1) ) + { + /* save node flags in local in case we have to recurse */ + nodeFlags = theGlobals->catalogInfo.nodeFlags; + + /* is it a directory? */ + if ( (nodeFlags & kFSNodeIsDirectoryMask) != 0 ) + { /* yes -- delete its contents before attempting to delete it */ + FSDeleteFolderLevel(&itemToDelete, theGlobals); + } + if ( theGlobals->result == noErr) /* are we still OK to delete? */ + { + if ( (nodeFlags & kFSNodeLockedMask) != 0 ) /* is item locked? */ + { /* then attempt to unlock it (ignore result since FSDeleteObject will set it correctly) */ + theGlobals->catalogInfo.nodeFlags = nodeFlags & ~kFSNodeLockedMask; + (void) FSSetCatalogInfo(&itemToDelete, kFSCatInfoNodeFlags, &theGlobals->catalogInfo); + } + /* delete the item */ + theGlobals->result = FSDeleteObject(&itemToDelete); + } + } + } while ( theGlobals->result == noErr ); + + /* we found the end of the items normally, so return noErr */ + if ( theGlobals->result == errFSNoMoreItems ) + theGlobals->result = noErr; + + /* close the FSIterator (closing an open iterator should never fail) */ + myverify_noerr(FSCloseIterator(iterator)); + } + + mycheck_noerr( theGlobals->result ); + + return; +} + +/*****************************************************************************/ + +#pragma mark ----- Utilities ----- + + /* Figures out if the given directory is a drop box or not */ + /* if it is, the Copy Engine will behave slightly differently */ +static OSErr IsDropBox( const FSRef* source, + Boolean *isDropBox ) +{ + FSCatalogInfo tmpCatInfo; + FSSpec sourceSpec; + Boolean isDrop = false; + OSErr err; + + /* get info about the destination, and an FSSpec to it for PBHGetDirAccess */ + err = FSGetCatalogInfo(source, kFSCatInfoNodeFlags | kFSCatInfoPermissions, &tmpCatInfo, NULL, &sourceSpec, NULL); + if( err == noErr ) /* make sure the source is a directory */ + err = ((tmpCatInfo.nodeFlags & kFSNodeIsDirectoryMask) != 0) ? noErr : errFSNotAFolder; + if( err == noErr ) + { + HParamBlockRec hPB; + + BlockZero( &hPB, sizeof( HParamBlockRec ) ); + + hPB.accessParam.ioNamePtr = sourceSpec.name; + hPB.accessParam.ioVRefNum = sourceSpec.vRefNum; + hPB.accessParam.ioDirID = sourceSpec.parID; + + /* This is the official way (reads: the way X Finder does it) to figure */ + /* out the current users access privileges to a given directory */ + err = PBHGetDirAccessSync(&hPB); + if( err == noErr ) /* its a drop folder if the current user only has write access */ + isDrop = (hPB.accessParam.ioACAccess & kPrivilegesMask) == kioACAccessUserWriteMask; + else if ( err == paramErr ) + { + /* There is a bug (2908703) in the Classic File System (not OS 9.x or Carbon) */ + /* on 10.1.x where PBHGetDirAccessSync sometimes returns paramErr even when the */ + /* data passed in is correct. This is a workaround/hack for that problem, */ + /* but is not as accurate. */ + /* Basically, if "Everyone" has only Write/Search access then its a drop folder */ + /* that is the most common case when its a drop folder */ + FSPermissionInfo *tmpPerm = (FSPermissionInfo *)tmpCatInfo.permissions; + isDrop = ((tmpPerm->mode & kRWXOtherAccessMask) == kDropFolderValue); + err = noErr; + } + } + + *isDropBox = isDrop; + + mycheck_noerr( err ); + + return err; +} + +/*****************************************************************************/ + + /* The copy engine is going to set the item's creation date */ + /* to kMagicBusyCreationDate while it's copying the item. */ + /* But kMagicBusyCreationDate is an old-style 32-bit date/time, */ + /* while the HFS Plus APIs use the new 64-bit date/time. So */ + /* we have to call a happy UTC utilities routine to convert from */ + /* the local time kMagicBusyCreationDate to a UTCDateTime */ + /* gMagicBusyCreationDate, which the File Manager will store */ + /* on disk and which the Finder we read back using the old */ + /* APIs, whereupon the File Manager will convert it back */ + /* to local time (and hopefully get the kMagicBusyCreationDate */ + /* back!). */ +static OSErr GetMagicBusyCreateDate( UTCDateTime *date ) +{ + static UTCDateTime magicDate = { 0, 0xDEADBEEF, 0 }; + OSErr err = ( date != NULL ) ? noErr : paramErr; + + if( err == noErr && magicDate.lowSeconds == 0xDEADBEEF ) + err = ConvertLocalTimeToUTC( kMagicBusyCreationDate, &magicDate.lowSeconds ); + if( err == noErr ) + *date = magicDate; + + mycheck_noerr( err ); + + return err; +} + +/*****************************************************************************/ + +static OSErr FSGetVRefNum( const FSRef *ref, + FSVolumeRefNum *vRefNum) +{ + FSCatalogInfo catalogInfo; + OSErr err = ( ref != NULL && vRefNum != NULL ) ? noErr : paramErr; + + if( err == noErr ) /* get the volume refNum from the FSRef */ + err = FSGetCatalogInfo(ref, kFSCatInfoVolume, &catalogInfo, NULL, NULL, NULL); + if( err == noErr ) + *vRefNum = catalogInfo.volume; + + mycheck_noerr( err ); + + return err; +} + +/*****************************************************************************/ + +static OSErr FSGetVolParms( FSVolumeRefNum volRefNum, + UInt32 bufferSize, + GetVolParmsInfoBuffer *volParmsInfo, + UInt32 *actualInfoSize) /* Can Be NULL */ +{ + HParamBlockRec pb; + OSErr err = ( volParmsInfo != NULL ) ? noErr : paramErr; + + if( err == noErr ) + { + pb.ioParam.ioNamePtr = NULL; + pb.ioParam.ioVRefNum = volRefNum; + pb.ioParam.ioBuffer = (Ptr)volParmsInfo; + pb.ioParam.ioReqCount = (SInt32)bufferSize; + err = PBHGetVolParmsSync(&pb); + } + /* return number of bytes the file system returned in volParmsInfo buffer */ + if( err == noErr && actualInfoSize != NULL) + *actualInfoSize = (UInt32)pb.ioParam.ioActCount; + + mycheck_noerr( err ); + + return ( err ); +} + +/*****************************************************************************/ + +/* Converts a unicode string to a PString */ +/* If your code is only for OS X, you can use CFString functions to do all this */ +/* Since this sample code supports OS 9.1 -> OS X, I have to do this the */ +/* old fashioned way. */ +static OSErr UniStrToPStr( const HFSUniStr255 *uniStr, + TextEncoding textEncodingHint, + Boolean isVolumeName, + Str255 pStr ) +{ + UnicodeMapping uMapping; + UnicodeToTextInfo utInfo; + ByteCount unicodeByteLength = 0; + ByteCount unicodeBytesConverted; + ByteCount actualPascalBytes; + OSErr err = (uniStr != NULL && pStr != NULL) ? noErr : paramErr; + + /* make sure output is valid in case we get errors or there's nothing to convert */ + pStr[0] = 0; + + if( err == noErr ) + unicodeByteLength = uniStr->length * sizeof(UniChar); /* length can be zero, which is fine */ + if( err == noErr && unicodeByteLength != 0 ) + { + /* if textEncodingHint is kTextEncodingUnknown, get a "default" textEncodingHint */ + if ( kTextEncodingUnknown == textEncodingHint ) + { + ScriptCode script; + RegionCode region; + + script = (ScriptCode)GetScriptManagerVariable(smSysScript); + region = (RegionCode)GetScriptManagerVariable(smRegionCode); + err = UpgradeScriptInfoToTextEncoding(script, kTextLanguageDontCare, + region, NULL, &textEncodingHint ); + if ( err == paramErr ) + { /* ok, ignore the region and try again */ + err = UpgradeScriptInfoToTextEncoding(script, kTextLanguageDontCare, + kTextRegionDontCare, NULL, + &textEncodingHint ); + } + if ( err != noErr ) /* ok... try something */ + textEncodingHint = kTextEncodingMacRoman; + } + + uMapping.unicodeEncoding = CreateTextEncoding( kTextEncodingUnicodeV2_0, + kUnicodeCanonicalDecompVariant, + kUnicode16BitFormat); + uMapping.otherEncoding = GetTextEncodingBase(textEncodingHint); + uMapping.mappingVersion = kUnicodeUseHFSPlusMapping; + + err = CreateUnicodeToTextInfo(&uMapping, &utInfo); + if( err == noErr ) + { + err = ConvertFromUnicodeToText( utInfo, unicodeByteLength, uniStr->unicode, kUnicodeLooseMappingsMask, + 0, NULL, 0, NULL, /* offsetCounts & offsetArrays */ + isVolumeName ? kHFSMaxVolumeNameChars : kHFSPlusMaxFileNameChars, + &unicodeBytesConverted, &actualPascalBytes, &pStr[1]); + } + if( err == noErr ) + pStr[0] = actualPascalBytes; + + /* verify the result in debug builds -- there's really not anything you can do if it fails */ + myverify_noerr(DisposeUnicodeToTextInfo(&utInfo)); + } + + mycheck_noerr( err ); + + return ( err ); +} + +/*****************************************************************************/ + + /* Yeah I know there is FSpMakeFSRef, but this way I don't have to */ + /* actually have an FSSpec created to make the FSRef, and this is */ + /* what FSpMakeFSRef does anyways */ +static OSErr FSMakeFSRef( FSVolumeRefNum volRefNum, + SInt32 dirID, + ConstStr255Param name, + FSRef *ref ) +{ + FSRefParam pb; + OSErr err = ( ref != NULL ) ? noErr : paramErr; + + if( err == noErr ) + { + pb.ioVRefNum = volRefNum; + pb.ioDirID = dirID; + pb.ioNamePtr = (StringPtr)name; + pb.newRef = ref; + err = PBMakeFSRefSync(&pb); + } + + mycheck_noerr( err ); + + return ( err ); +} + +/*****************************************************************************/ + + /* This checks the destination to see if an object of the same name as the source */ + /* exists or not. If it does we have to special handle the DupeActions */ + /* */ + /* If kDupeActionReplace we move aside the object by renameing it to ".DeleteMe" */ + /* so that it will be invisible (X only), and give a suggestion on what to do with */ + /* it if for some unknown reason it survives the copy and the user finds it. This */ + /* rename is mainly done to handle the case where the source is in the destination */ + /* and the user wants to replace. Basically keeping the source around throughout */ + /* the copy, deleting it afterwards. Its also done cause its a good idea not to */ + /* dispose of the existing object in case the copy fails */ + /* */ + /* If kDupeActionRename, we create a unique name for the new object and pass */ + /* it back to the caller */ +static OSErr SetupDestination( const FSRef *destDir, + const DupeAction dupeAction, + HFSUniStr255 *sourceName, + FSRef *deleteMeRef, + Boolean *isReplacing ) +{ + FSRef tmpRef; + OSErr err; + + /* check if an object of the same name already exists in the destination */ + err = FSMakeFSRefUnicode( destDir, sourceName->length, sourceName->unicode, kTextEncodingUnknown, &tmpRef ); + if( err == noErr ) + { /* if the user wants to replace the existing */ + /* object, rename it to .DeleteMe first. Delete it */ + if( dupeAction == kDupeActionReplace ) /* only after copying the new one successfully */ + { + err = FSRenameUnicode( &tmpRef, 9, (UniChar*)"\0.\0D\0e\0l\0e\0t\0e\0M\0e", kTextEncodingMacRoman, deleteMeRef ); + *isReplacing = ( err == noErr ) ? true : false; + } + else if( dupeAction == kDupeActionRename ) /* if the user wants to just rename it */ + err = GetUniqueName( destDir, sourceName ); /* then we get a unique name for the new object */ + } + else if ( err == fnfErr ) /* if no object exists then */ + err = noErr; /* continue with no error */ + + return err; +} + +/*****************************************************************************/ + + /* Given a directory and a name, GetUniqueName will check if an object */ + /* with the same name already exists, and if it does it will create */ + /* a new, unique name for and return it. */ + /* it simply appends a number to the end of the name. It is not */ + /* fool proof, and it is limited... I'll take care of that in a */ + /* later release */ + /* If anyone has any suggestions/better techniques I would love to hear */ + /* about them */ +static OSErr GetUniqueName( const FSRef *destDir, + HFSUniStr255 *sourceName ) +{ + HFSUniStr255 tmpName = *sourceName; + FSRef tmpRef; + unsigned char hexStr[17] = "123456789"; /* yeah, only 9... I'm lazy, sosumi */ + long count = 0; + int index; + OSErr err; + + /* find the dot, if there is one */ + for( index = tmpName.length; index >= 0 && tmpName.unicode[index] != (UniChar) '.'; index-- ) { /* Do Nothing */ } + + if( index <= 0) /* no dot or first char is a dot (invisible file), so append to end of name */ + index = tmpName.length; + else /* shift the extension up two spots to make room for our digits */ + BlockMoveData( tmpName.unicode + index, tmpName.unicode + index + 2, (tmpName.length - index) * 2 ); + + /* add the space to the name */ + tmpName.unicode[ index ] = (UniChar)' '; + /* we're adding two characters to the name */ + tmpName.length += 2; + + do { /* add the digit to the name */ + tmpName.unicode[ index + 1 ] = hexStr[count]; + /* check if the file with this new name already exists */ + err = FSMakeFSRefUnicode( destDir, tmpName.length, tmpName.unicode, kTextEncodingUnknown, &tmpRef ); + count++; + } while( err == noErr && count < 10 ); + + if( err == fnfErr ) + { + err = noErr; + *sourceName = tmpName; + } + + return err; +} + +/*****************************************************************************/ + +static OSErr GetObjectName( const FSRef *sourceRef, + HFSUniStr255 *sourceName, /* can be NULL */ + TextEncoding *sourceEncoding ) /* can be NULL */ +{ + FSCatalogInfo catInfo; + FSCatalogInfoBitmap whichInfo = (sourceEncoding != NULL) ? kFSCatInfoTextEncoding : kFSCatInfoNone; + OSErr err; + + err = FSGetCatalogInfo( sourceRef, whichInfo, &catInfo, sourceName, NULL, NULL ); + if( err == noErr && sourceEncoding != NULL ) + *sourceEncoding = catInfo.textEncodingHint; + + return err; +} + +/*****************************************************************************/ + +static OSErr CreateFolder( const FSRef *sourceRef, + const FSRef *destDirRef, + const FSCatalogInfo *catalogInfo, + const HFSUniStr255 *folderName, + CopyParams *params, + FSRef *newFSRefPtr, + FSSpec *newFSSpecPtr ) +{ + FSCatalogInfo tmpCatInfo; + FSPermissionInfo origPermissions; + OSErr err = ( sourceRef != NULL && destDirRef != NULL && catalogInfo != NULL && + folderName != NULL && newFSRefPtr != NULL ) ? noErr : paramErr; + + if( err == noErr ) + { /* store away the catInfo, create date and permissions on the orig folder */ + tmpCatInfo = *catalogInfo; + origPermissions = *((FSPermissionInfo*)catalogInfo->permissions); + } + if( err == noErr ) /* create the new folder */ + err = DoCreateFolder( sourceRef, destDirRef, &tmpCatInfo, folderName, params, newFSRefPtr, newFSSpecPtr ); + if( err == noErr && !params->copyingToDropFolder ) + { /* if its not a drop box, set the permissions on the new folder */ + *((FSPermissionInfo*)tmpCatInfo.permissions) = origPermissions; + err = FSSetCatalogInfo( newFSRefPtr, kFSCatInfoPermissions, &tmpCatInfo ); + } + + mycheck_noerr( err ); + + return err; +} + +/*****************************************************************************/ + +static OSErr DoCreateFolder(const FSRef *sourceRef, + const FSRef *destDirRef, + const FSCatalogInfo *catalogInfo, + const HFSUniStr255 *folderName, + CopyParams *params, + FSRef *newFSRefPtr, + FSSpec *newFSSpecPtr) +{ + FSCatalogInfo catInfo = *catalogInfo; + OSErr err; + + /* Clear the "inited" bit so that the Finder positions the icon for us. */ + ((FInfo *)(catInfo.finderInfo))->fdFlags &= ~kHasBeenInited; + + /* we need to have user level read/write/execute access to the folder we are going to create, */ + /* otherwise FSCreateDirectoryUnicode will return -5000 (afpAccessDenied), */ + /* and the FSRef returned will be invalid, yet the folder is created... bug? */ + ((FSPermissionInfo*) catInfo.permissions)->mode |= kRWXUserAccessMask; + + err = FSCreateDirectoryUnicode( destDirRef, folderName->length, + folderName->unicode, kFSCatInfoSettableInfo, + &catInfo, newFSRefPtr, + newFSSpecPtr, NULL); + + /* With the new APIs, folders can have forks as well as files. Before */ + /* we start copying items in the folder, we must copy over the forks */ + /* Currently, MacOS doesn't support any file systems that have forks in */ + /* folders, but the API supports it so (for possible future */ + /* compatability) I kept this in here. */ + if( err == noErr ) + err = CopyForks( sourceRef, newFSRefPtr, params ); + + mycheck_noerr( err ); + + return err; +} + +/*****************************************************************************/ + + /* This is the DisposeDataProc that is used by the GenLinkedList in FSCopyFolder */ + /* Simply disposes of the data we created and returns */ +static pascal void MyDisposeDataProc( void *pData ) +{ + if( pData != NULL ) + DisposePtr( (char*) pData ); +} + +/*****************************************************************************/ + + /* This is the DisposeDataProc that is used by the GenLinkedList in CopyItemForks */ + /* Simply closes the resource fork (if opened, != 0) and disposes of the memory */ +static pascal void MyCloseForkProc( void *pData ) +{ + SInt16 refNum; + + if( pData == NULL ) + return; + + refNum = ((ForkTrackerPtr)pData)->forkDestRefNum; + if( refNum != 0 ) + myverify_noerr( FSCloseFork( refNum ) ); /* the fork was opened, so close it */ + + DisposePtr( (char*) pData ); +} diff --git a/wolf3d/code/iphone/FSCopyObject.h b/wolf3d/code/iphone/FSCopyObject.h new file mode 100644 index 0000000..3189f00 --- /dev/null +++ b/wolf3d/code/iphone/FSCopyObject.h @@ -0,0 +1,258 @@ +/* + File: FSCopyObject.h + + Contains: A Copy/Delete Files/Folders engine which uses the HFS+ API's + + Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. + ("Apple") in consideration of your agreement to the following terms, and your + use, installation, modification or redistribution of this Apple software + constitutes acceptance of these terms. If you do not agree with these terms, + please do not use, install, modify or redistribute this Apple software. + + In consideration of your agreement to abide by the following terms, and subject + to these terms, Apple grants you a personal, non-exclusive license, under AppleÕs + copyrights in this original Apple software (the "Apple Software"), to use, + reproduce, modify and redistribute the Apple Software, with or without + modifications, in source and/or binary forms; provided that if you redistribute + the Apple Software in its entirety and without modifications, you must retain + this notice and the following text and disclaimers in all such redistributions of + the Apple Software. Neither the name, trademarks, service marks or logos of + Apple Computer, Inc. may be used to endorse or promote products derived from the + Apple Software without specific prior written permission from Apple. Except as + expressly stated in this notice, no other rights or licenses, express or implied, + are granted by Apple herein, including but not limited to any patent rights that + may be infringed by your derivative works or by other works in which the Apple + Software may be incorporated. + + The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO + WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED + WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN + COMBINATION WITH YOUR PRODUCTS. + + IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION + OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT + (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN + ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + Copyright © 2002-2004 Apple Computer, Inc., All Rights Reserved +*/ + + +#ifndef __FSCOPYOBJECT_H__ +#define __FSCOPYOBJECT_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#if TARGET_API_MAC_OSX || defined( __APPLE_CC__ ) +#include +#endif + +#define DEBUG 1 /* set to zero if you don't want debug spew */ + +#if DEBUG + #include + + #define QuoteExceptionString(x) #x + + #define dwarning(s) do { printf s; fflush(stderr); } while( 0 ) + + #define mycheck_noerr( error ) \ + do { \ + if( (OSErr) error != noErr ) { \ + dwarning((QuoteExceptionString(error) " != noErr in File: %s, Function: %s, Line: %d, Error: %d\n", \ + __FILE__, __FUNCTION__, __LINE__, (OSErr) error)); \ + } \ + } while( false ) + + #define mycheck( assertion ) \ + do { \ + if( ! assertion ) { \ + dwarning((QuoteExceptionString(assertion) " failed in File: %s, Function: %s, Line: %d\n", \ + __FILE__, __FUNCTION__, __LINE__)); \ + } \ + } while( false ) + + #define myverify(assertion) mycheck(assertion) + #define myverify_noerr(assertion) mycheck_noerr( (assertion) ) +#else + #define dwarning(s) + + #define mycheck(assertion) + #define mycheck_noerr(err) + #define myverify(assertion) do { (void) (assertion); } while (0) + #define myverify_noerr(assertion) myverify(assertion) +#endif + +/* + This code takes some tricks/techniques from MoreFilesX (by Jim Luther) and + MPFileCopy (by Quinn), wraps them all up into an easy to use API, and adds a bunch of + features and bug fixes. It will run on Mac OS 9.1 through 9.2.x and 10.1.x + and up (Classic, Carbon and Mach-O) +*/ + + /* Different options that FSCopyObject can take during a copy */ +typedef UInt32 DupeAction; +enum { + kDupeActionStandard, /* will do the copy with no frills */ + kDupeActionReplace, /* will delete the existing object and then copy over the new one */ + kDupeActionRename /* will rename the new object if an object of the same name exists */ +}; + +/*****************************************************************************/ + +#pragma mark CopyObjectFilterProcPtr + +/* + This is the prototype for the CallCopyObjectFilterProc function which + is called once for each file and directory found by FSCopyObject. + The CallCopyObjectFilterProc can use the read-only data it receives for + whatever it wants. + + The result of the CallCopyObjectFilterProc function indicates if + the copy should be stopped. To stop the copy, return an error; to continue + the copy, return noErr. + + The yourDataPtr parameter can point to whatever data structure you might + want to access from within the CallCopyObjectFilterProc. + + Note: If an error had occured during the copy of the current object + (currentOSErr != noErr) the FSRef etc might not be valid + + containerChanged --> Set to true if the container's contents changed + during iteration. + currentLevel --> The current recursion level into the container. + 1 = the container, 2 = the container's immediate + subdirectories, etc. + currentOSErr --> The current error code, shows the results of the + copy of the current object (ref) + catalogInfo --> The catalog information for the current object. + Only the fields requested by the whichInfo + parameter passed to FSIterateContainer are valid. + ref --> The FSRef to the current object. + spec --> The FSSpec to the current object if the wantFSSpec + parameter passed to FSCopyObject is true. + name --> The name of the current object if the wantName + parameter passed to FSCopyObject is true. + yourDataPtr --> An optional pointer to whatever data structure you + might want to access from within the + CallCopyObjectFilterProc. + result <-- To continue the copy, return noErr + + __________ + + Also see: FSCopyObject +*/ + +typedef CALLBACK_API( OSErr , CopyObjectFilterProcPtr ) ( + Boolean containerChanged, + ItemCount currentLevel, + OSErr currentOSErr, + const FSCatalogInfo *catalogInfo, + const FSRef *ref, + const FSSpec *spec, + const HFSUniStr255 *name, + void *yourDataPtr); + + +/*****************************************************************************/ + +#pragma mark CallCopyObjectFilterProc + +#define CallCopyObjectFilterProc(userRoutine, containerChanged, currentLevel, currentOSErr, catalogInfo, ref, spec, name, yourDataPtr) \ + (*(userRoutine))((containerChanged), (currentLevel), (currentOSErr), (catalogInfo), (ref), (spec), (name), (yourDataPtr)) + +/*****************************************************************************/ + +#pragma mark FSCopyObject + +/* + The FSCopyObject function takes a source object (can be a file or directory) + and copies it (and its contents if it's a directory) to the new destination + directory. + + It will call your CopyObjectFilterProcPtr once for each object copied + + The maxLevels parameter is only used when the object is a directory, + ignored otherwise. + It lets you control how deep the recursion goes. + If maxLevels is 1, FSCopyObject only scans the specified directory; + if maxLevels is 2, FSCopyObject scans the specified directory and + one subdirectory below the specified directory; etc. Set maxLevels to + zero to scan all levels. + + The yourDataPtr parameter can point to whatever data structure you might + want to access from within your CopyObjectFilterProcPtr. + + source --> The FSRef to the object you want to copy + destDir --> The FSRef to the directory you wish to copy source to + maxLevels --> Maximum number of directory levels to scan or + zero to scan all directory levels, ignored if the + object is a file + whichInfo --> The fields of the FSCatalogInfo you wish passed + to you in your CopyObjectFilterProc + dupeAction --> The action to take if an object of the same name exists + in the destination + newName --> The name you want the new object to have. If you pass + in NULL, the source object name will be used. + wantFSSpec --> Set to true if you want the FSSpec to each + object passed to your CopyObjectFilterProc. + wantName --> Set to true if you want the name of each + object passed to your CopyObjectFilterProc. + filterProcPtr --> A pointer to the CopyObjectFilterProc you + want called once for each object found + by FSCopyObject. + yourDataPtr --> An optional pointer to whatever data structure you + might want to access from within the + CopyObjectFilterProc. + newObjectRef --> An optional pointer to an FSRef that, on return, + references the new object. If you don't want this + info returned, pass in NULL + newObjectSpec --> An optional pointer to an FSSPec that, on return, + references the new object. If you don't want this + info returned, pass in NULL +*/ + +OSErr FSCopyObject( const FSRef *source, + const FSRef *destDir, + ItemCount maxLevels, + FSCatalogInfoBitmap whichInfo, + DupeAction dupeAction, + const HFSUniStr255 *newName, /* can be NULL */ + Boolean wantFSSpec, + Boolean wantName, + CopyObjectFilterProcPtr filterProcPtr, /* can be NULL */ + void *yourDataPtr, /* can be NULL */ + FSRef *newObjectRef, /* can be NULL */ + FSSpec *newObjectSpec); /* can be NULL */ + +/*****************************************************************************/ + +#pragma mark FSDeleteObjects + +/* + The FSDeleteObjects function takes an FSRef to a file or directory + and attempts to delete it. If the object is a directory, all files + and subdirectories in the specified directory are deleted. If a + locked file or directory is encountered, it is unlocked and then + deleted. After deleting the directory's contents, the directory + is deleted. If any unexpected errors are encountered, + FSDeleteContainer quits and returns to the caller. + + source --> FSRef to an object (can be file or directory). + + __________ +*/ + +OSErr FSDeleteObjects( const FSRef *source ); + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/wolf3d/code/iphone/GenLinkedList.c b/wolf3d/code/iphone/GenLinkedList.c new file mode 100644 index 0000000..50a3761 --- /dev/null +++ b/wolf3d/code/iphone/GenLinkedList.c @@ -0,0 +1,212 @@ +/* + File: GenLinkedList.c + + Contains: Linked List utility routines + + Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. + ("Apple") in consideration of your agreement to the following terms, and your + use, installation, modification or redistribution of this Apple software + constitutes acceptance of these terms. If you do not agree with these terms, + please do not use, install, modify or redistribute this Apple software. + + In consideration of your agreement to abide by the following terms, and subject + to these terms, Apple grants you a personal, non-exclusive license, under AppleÕs + copyrights in this original Apple software (the "Apple Software"), to use, + reproduce, modify and redistribute the Apple Software, with or without + modifications, in source and/or binary forms; provided that if you redistribute + the Apple Software in its entirety and without modifications, you must retain + this notice and the following text and disclaimers in all such redistributions of + the Apple Software. Neither the name, trademarks, service marks or logos of + Apple Computer, Inc. may be used to endorse or promote products derived from the + Apple Software without specific prior written permission from Apple. Except as + expressly stated in this notice, no other rights or licenses, express or implied, + are granted by Apple herein, including but not limited to any patent rights that + may be infringed by your derivative works or by other works in which the Apple + Software may be incorporated. + + The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO + WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED + WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN + COMBINATION WITH YOUR PRODUCTS. + + IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION + OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT + (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN + ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + Copyright © 2003-2004 Apple Computer, Inc., All Rights Reserved +*/ + +#include "GenLinkedList.h" + +#pragma mark --- Data Structures --- + + /* This is the internal data structure for the nodes in the linked list. */ + /* */ + /* Note: The memory pointed to by pNext is owned by the list and is disposed of */ + /* in DestroyList. It should not be disposed of in any other way. */ + /* */ + /* Note: The memory pointed to by pData is owned by the caller of the linked */ + /* list. The caller is responsible for disposing of this memory. This can be */ + /* done by simply implementing a DisposeDataProc that will be called on each */ + /* node in the list, giving the caller a chance to dispose of any memory */ + /* created. The DisposeDataProc is called from DestroyList */ +struct GenNode +{ + struct GenNode *pNext; /* Pointer to the next node in the list */ + GenDataPtr pData; /* The data for this node, owned by caller */ +}; +typedef struct GenNode GenNode; + +#pragma mark --- List Implementation --- + + /* Initializes the given GenLinkedList to an empty list. This MUST be */ + /* called before any operations are performed on the list, otherwise bad things */ + /* will happen. */ +void InitLinkedList( GenLinkedList *pList, DisposeDataProcPtr disposeProcPtr) +{ + if( pList == NULL ) + return; + + pList->pHead = pList->pTail = NULL; + pList->NumberOfItems = 0; + pList->DisposeProcPtr = disposeProcPtr; +} + + /* returns the current number of items in the given list. */ + /* If pList == NULL, it returns 0 */ +ItemCount GetNumberOfItems( GenLinkedList *pList ) +{ + return (pList) ? pList->NumberOfItems : 0; +} + + /* Creates a new node, containing pData, and adds it to the tail of pList. */ + /* Note: if an error occurs, pList is unchanged. */ +OSErr AddToTail( GenLinkedList *pList, void *pData ) +{ + OSErr err = paramErr; + GenNode *tmpNode = NULL; + + if( pList == NULL || pData == NULL ) + return err; + + /* create memory for new node, if this fails we _must_ bail */ + err = ( ( tmpNode = (GenNode*) NewPtr( sizeof( GenNode ) ) ) != NULL ) ? noErr : MemError(); + if( err == noErr ) + { + tmpNode->pData = pData; /* Setup new node */ + tmpNode->pNext = NULL; + + if( pList->pTail != NULL ) /* more then one item already */ + ((GenNode*) pList->pTail)->pNext = (void*) tmpNode; /* so append to tail */ + else + pList->pHead = (void*) tmpNode; /* no items, so adjust head */ + + pList->pTail = (void*) tmpNode; + pList->NumberOfItems += 1; + } + + return err; +} + + /* Takes pSrcList and inserts it into pDestList at the location pIter points to. */ + /* The lists must have the same DisposeProcPtr, but the Data can be different. If pSrcList */ + /* is empty, it does nothing and just returns */ + /* */ + /* If pIter == NULL, insert pSrcList before the head */ + /* else If pIter == pTail, append pSrcList to the tail */ + /* else insert pSrcList in the middle somewhere */ + /* On return: pSrcList is cleared and is an empty list. */ + /* The data that was owned by pSrcList is now owned by pDestList */ +void InsertList( GenLinkedList *pDestList, GenLinkedList *pSrcList, GenIteratorPtr pIter ) +{ + if( pDestList == NULL || pSrcList == NULL || + pSrcList->pHead == NULL || pSrcList->pTail == NULL || + pDestList->DisposeProcPtr != pSrcList->DisposeProcPtr ) + return; + + if( pDestList->pHead == NULL && pDestList->pTail == NULL ) /* empty list */ + { + pDestList->pHead = pSrcList->pHead; + pDestList->pTail = pSrcList->pTail; + } + else if( pIter == NULL ) /* insert before head */ + { + /* attach the list */ + ((GenNode*)pSrcList->pTail)->pNext = pDestList->pHead; + /* fix up head */ + pDestList->pHead = pSrcList->pHead; + } + else if( pIter == pDestList->pTail ) /* append to tail */ + { + /* attach the list */ + ((GenNode*)pDestList->pTail)->pNext = pSrcList->pHead; + /* fix up tail */ + pDestList->pTail = pSrcList->pTail; + } + else /* insert in middle somewhere */ + { + GenNode *tmpNode = ((GenNode*)pIter)->pNext; + ((GenNode*)pIter)->pNext = pSrcList->pHead; + ((GenNode*)pSrcList->pTail)->pNext = tmpNode; + } + + pDestList->NumberOfItems += pSrcList->NumberOfItems; /* sync up NumberOfItems */ + + InitLinkedList( pSrcList, NULL); /* reset the source list */ +} + + /* Goes through the list and disposes of any memory we allocated. Calls the DisposeProcPtr,*/ + /* if it exists, to give the caller a chance to free up their memory */ +void DestroyList( GenLinkedList *pList ) +{ + GenIteratorPtr pIter = NULL, + pNextIter = NULL; + + if( pList == NULL ) + return; + + for( InitIterator( pList, &pIter ), pNextIter = pIter; pIter != NULL; pIter = pNextIter ) + { + Next( &pNextIter ); /* get the next node before we blow away the link */ + + if( pList->DisposeProcPtr != NULL ) + CallDisposeDataProc( pList->DisposeProcPtr, GetData( pIter ) ); + DisposePtr( (char*) pIter ); + } + + InitLinkedList( pList, NULL); +} + +/*#############################################*/ +/*#############################################*/ +/*#############################################*/ + +#pragma mark - +#pragma mark --- Iterator Implementation --- + + /* Initializes pIter to point at the head of pList */ + /* This must be called before performing any operations with pIter */ +void InitIterator( GenLinkedList *pList, GenIteratorPtr *pIter ) +{ + if( pList != NULL && pIter != NULL ) + *pIter = pList->pHead; +} + + /* On return, pIter points to the next node in the list. NULL if its gone */ + /* past the end of the list */ +void Next( GenIteratorPtr *pIter ) +{ + if( pIter != NULL ) + *pIter = ((GenNode*)*pIter)->pNext; +} + + /* Returns the data of the current node that pIter points to */ +GenDataPtr GetData( GenIteratorPtr pIter ) +{ + return ( pIter != NULL ) ? ((GenNode*)pIter)->pData : NULL; +} diff --git a/wolf3d/code/iphone/GenLinkedList.h b/wolf3d/code/iphone/GenLinkedList.h new file mode 100644 index 0000000..c6dd69a --- /dev/null +++ b/wolf3d/code/iphone/GenLinkedList.h @@ -0,0 +1,93 @@ +/* + File: GenLinkedList.h + + Contains: Linked List utility routines prototypes + + Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. + ("Apple") in consideration of your agreement to the following terms, and your + use, installation, modification or redistribution of this Apple software + constitutes acceptance of these terms. If you do not agree with these terms, + please do not use, install, modify or redistribute this Apple software. + + In consideration of your agreement to abide by the following terms, and subject + to these terms, Apple grants you a personal, non-exclusive license, under AppleÕs + copyrights in this original Apple software (the "Apple Software"), to use, + reproduce, modify and redistribute the Apple Software, with or without + modifications, in source and/or binary forms; provided that if you redistribute + the Apple Software in its entirety and without modifications, you must retain + this notice and the following text and disclaimers in all such redistributions of + the Apple Software. Neither the name, trademarks, service marks or logos of + Apple Computer, Inc. may be used to endorse or promote products derived from the + Apple Software without specific prior written permission from Apple. Except as + expressly stated in this notice, no other rights or licenses, express or implied, + are granted by Apple herein, including but not limited to any patent rights that + may be infringed by your derivative works or by other works in which the Apple + Software may be incorporated. + + The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO + WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED + WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN + COMBINATION WITH YOUR PRODUCTS. + + IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION + OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT + (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN + ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + Copyright © 2003-2004 Apple Computer, Inc., All Rights Reserved +*/ + + +#ifndef __GENLINKEDLIST__ +#define __GENLINKEDLIST__ + +#ifdef __cplusplus +extern "C" { +#endif + +#if TARGET_API_MAC_OSX || defined( __APPLE_CC__ ) +#include +#endif + +/* This is a quick, simple and generic linked list implementation. I tried */ +/* to setup the code so that you could use any linked list implementation you */ +/* want. They just need to support these few functions. */ + +typedef void* GenIteratorPtr; +typedef void* GenDataPtr; + + /* This is a callback that is called from DestroyList for each node in the */ + /* list. It gives the caller the oportunity to free any memory they might */ + /* allocated in each node. */ +typedef CALLBACK_API( void , DisposeDataProcPtr ) ( GenDataPtr pData ); + +#define CallDisposeDataProc( userRoutine, pData ) (*(userRoutine))((pData)) + +struct GenLinkedList +{ + GenDataPtr pHead; /* Pointer to the head of the list */ + GenDataPtr pTail; /* Pointer to the tail of the list */ + ItemCount NumberOfItems; /* Number of items in the list (mostly for debugging) */ + DisposeDataProcPtr DisposeProcPtr; /* rountine called to dispose of caller data, can be NULL */ +}; +typedef struct GenLinkedList GenLinkedList; + +void InitLinkedList ( GenLinkedList *pList, DisposeDataProcPtr disposeProcPtr ); +ItemCount GetNumberOfItems( GenLinkedList *pList ); +OSErr AddToTail ( GenLinkedList *pList, GenDataPtr pData ); +void InsertList ( GenLinkedList *pDestList, GenLinkedList *pSrcList, GenIteratorPtr pIter ); +void DestroyList ( GenLinkedList *pList ); + +void InitIterator ( GenLinkedList *pList, GenIteratorPtr *pIter ); +void Next ( GenIteratorPtr *pIter ); +GenDataPtr GetData ( GenIteratorPtr pIter ); + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/wolf3d/code/iphone/Info.plist b/wolf3d/code/iphone/Info.plist index bc5d079..1835ef8 100644 --- a/wolf3d/code/iphone/Info.plist +++ b/wolf3d/code/iphone/Info.plist @@ -7,13 +7,13 @@ CFBundleDevelopmentRegion English CFBundleDisplayName - ${PRODUCT_NAME} + ${PRODUCT_NAME}+ CFBundleExecutable ${EXECUTABLE_NAME} CFBundleIconFile ${PRODUCT_NAME}_icon.png CFBundleIdentifier - ${PROFILE_PREFIX}.${PRODUCT_NAME:identifier} + com.idsoftware.${PRODUCT_NAME:identifier} CFBundleInfoDictionaryVersion 6.0 CFBundleName @@ -22,26 +22,28 @@ APPL CFBundleSignature ???? + CFBundleURLTypes + + + CFBundleURLName + com.idsoftware.wolf3dp + CFBundleURLSchemes + + wolf3dp + + + CFBundleVersion - 1.1 + 1.2 LSRequiresIPhoneOS NSMainNibFile MainWindow UIInterfaceOrientation UIInterfaceOrientationLandscapeLeft + UIPrerenderedIcon + UIStatusBarHidden - CFBundleURLTypes - - - CFBundleURLName - com.idsoftware.wolf3d - CFBundleURLSchemes - - wolf3d - - - diff --git a/wolf3d/code/iphone/arialGlyphRects.h b/wolf3d/code/iphone/arialGlyphRects.h new file mode 100644 index 0000000..7f65fb2 --- /dev/null +++ b/wolf3d/code/iphone/arialGlyphRects.h @@ -0,0 +1,104 @@ +// generated by fontimg +// + +typedef struct { unsigned short x0, y0, x1, y1; float xoff, yoff, xadvance; } GlyphRect; + +GlyphRect glyphRects[] = { + /* ' ' */ { 2, 2, 2, 2, 0.000000, 0.000000, 7.958042 }, + /* '!' */ { 6, 2, 9, 22, 2.250000, -20.750000, 7.958042 }, + /* '"' */ { 14, 2, 21, 9, 1.250000, -20.750000, 10.167832 }, + /* '#' */ { 26, 2, 41, 23, 0.250000, -21.000000, 15.930070 }, + /* '$' */ { 46, 2, 59, 27, 1.000000, -22.500000, 15.930070 }, + /* '%' */ { 64, 2, 86, 24, 1.500000, -21.000000, 25.468531 }, + /* '&' */ { 92, 2, 109, 23, 1.000000, -21.000000, 19.104895 }, + /* ''' */ { 114, 2, 117, 9, 1.250000, -20.750000, 5.468532 }, + /* '(' */ { 122, 2, 129, 29, 1.500000, -21.000000, 9.538462 }, + /* ')' */ { 134, 2, 141, 29, 1.500000, -21.000000, 9.538462 }, + /* '*' */ { 146, 2, 155, 11, 0.750000, -21.000000, 11.146853 }, + /* '+' */ { 160, 2, 173, 15, 1.500000, -17.000000, 16.727272 }, + /* ',' */ { 178, 2, 181, 9, 2.250000, -3.000000, 7.958042 }, + /* '-' */ { 186, 2, 194, 4, 0.750000, -8.750000, 9.538462 }, + /* '.' */ { 198, 2, 201, 5, 2.500000, -3.000000, 7.958042 }, + /* '/' */ { 206, 2, 214, 23, 0.000000, -21.000000, 7.958042 }, + /* '0' */ { 218, 2, 231, 23, 1.000000, -20.750000, 15.930070 }, + /* '1' */ { 236, 2, 243, 22, 3.000000, -20.750000, 15.930070 }, + /* '2' */ { 2, 34, 15, 54, 0.750000, -20.750000, 15.930070 }, + /* '3' */ { 20, 34, 33, 55, 1.000000, -20.750000, 15.930070 }, + /* '4' */ { 38, 34, 52, 54, 0.250000, -20.750000, 15.930070 }, + /* '5' */ { 58, 34, 72, 54, 1.000000, -20.250000, 15.930070 }, + /* '6' */ { 76, 34, 89, 55, 1.000000, -20.750000, 15.930070 }, + /* '7' */ { 94, 34, 107, 54, 1.250000, -20.250000, 15.930070 }, + /* '8' */ { 112, 34, 125, 55, 1.000000, -20.750000, 15.930070 }, + /* '9' */ { 130, 34, 143, 55, 1.000000, -20.750000, 15.930070 }, + /* ':' */ { 148, 34, 151, 49, 2.500000, -15.000000, 7.958042 }, + /* ';' */ { 156, 34, 159, 53, 2.250000, -15.000000, 7.958042 }, + /* '<' */ { 164, 34, 177, 48, 1.500000, -17.250000, 16.727272 }, + /* '=' */ { 182, 34, 195, 42, 1.500000, -14.500000, 16.727272 }, + /* '>' */ { 200, 34, 213, 48, 1.500000, -17.250000, 16.727272 }, + /* '?' */ { 218, 34, 231, 55, 1.250000, -21.000000, 15.930070 }, + /* '@' */ { 2, 60, 28, 87, 1.500000, -21.000000, 29.076923 }, + /* 'A' */ { 34, 60, 53, 80, -0.250000, -20.750000, 19.104895 }, + /* 'B' */ { 58, 60, 73, 80, 2.000000, -20.750000, 19.104895 }, + /* 'C' */ { 78, 60, 96, 81, 1.250000, -21.000000, 20.685314 }, + /* 'D' */ { 102, 60, 119, 80, 2.000000, -20.750000, 20.685314 }, + /* 'E' */ { 124, 60, 139, 80, 2.250000, -20.750000, 19.104895 }, + /* 'F' */ { 144, 60, 158, 80, 2.250000, -20.750000, 17.496504 }, + /* 'G' */ { 162, 60, 181, 81, 1.500000, -21.000000, 22.279720 }, + /* 'H' */ { 186, 60, 202, 80, 2.250000, -20.750000, 20.685314 }, + /* 'I' */ { 208, 60, 211, 80, 2.500000, -20.750000, 7.958042 }, + /* 'J' */ { 216, 60, 227, 81, 0.750000, -20.750000, 14.321678 }, + /* 'K' */ { 232, 60, 249, 80, 2.000000, -20.750000, 19.104895 }, + /* 'L' */ { 2, 92, 15, 112, 2.000000, -20.750000, 15.930070 }, + /* 'M' */ { 20, 92, 39, 112, 2.000000, -20.750000, 23.860140 }, + /* 'N' */ { 44, 92, 60, 112, 2.000000, -20.750000, 20.685314 }, + /* 'O' */ { 66, 92, 85, 113, 1.250000, -21.000000, 22.279720 }, + /* 'P' */ { 90, 92, 106, 112, 2.000000, -20.750000, 19.104895 }, + /* 'Q' */ { 110, 92, 130, 114, 1.000000, -21.000000, 22.279720 }, + /* 'R' */ { 136, 92, 154, 112, 2.250000, -20.750000, 20.685314 }, + /* 'S' */ { 160, 92, 176, 113, 1.250000, -21.000000, 19.104895 }, + /* 'T' */ { 182, 92, 198, 112, 0.500000, -20.750000, 17.496504 }, + /* 'U' */ { 204, 92, 220, 113, 2.250000, -20.750000, 20.685314 }, + /* 'V' */ { 226, 92, 245, 112, 0.000000, -20.750000, 19.104895 }, + /* 'W' */ { 2, 120, 28, 140, 0.250000, -20.750000, 27.034966 }, + /* 'X' */ { 34, 120, 53, 140, 0.000000, -20.750000, 19.104895 }, + /* 'Y' */ { 58, 120, 77, 140, 0.000000, -20.750000, 19.104895 }, + /* 'Z' */ { 82, 120, 98, 140, 0.500000, -20.750000, 17.496504 }, + /* '[' */ { 104, 120, 109, 146, 1.750000, -20.750000, 7.958042 }, + /* '\' */ { 114, 120, 122, 141, 0.000000, -21.000000, 7.958042 }, + /* ']' */ { 126, 120, 131, 146, 0.500000, -20.750000, 7.958042 }, + /* '^' */ { 136, 120, 148, 131, 0.750000, -21.000000, 13.440559 }, + /* '_' */ { 152, 120, 169, 122, -0.500000, 3.750000, 15.930070 }, + /* '`' */ { 174, 120, 179, 124, 1.000000, -20.750000, 9.538462 }, + /* 'a' */ { 184, 120, 197, 135, 1.000000, -15.250000, 15.930070 }, + /* 'b' */ { 202, 120, 215, 141, 1.750000, -20.750000, 15.930070 }, + /* 'c' */ { 220, 120, 233, 135, 1.000000, -15.250000, 14.321678 }, + /* 'd' */ { 238, 120, 251, 141, 0.750000, -20.750000, 15.930070 }, + /* 'e' */ { 2, 152, 15, 167, 1.000000, -15.250000, 15.930070 }, + /* 'f' */ { 20, 152, 28, 173, 0.250000, -21.000000, 7.958042 }, + /* 'g' */ { 34, 152, 47, 173, 0.750000, -15.250000, 15.930070 }, + /* 'h' */ { 52, 152, 64, 172, 1.750000, -20.750000, 15.930070 }, + /* 'i' */ { 70, 152, 72, 172, 1.750000, -20.750000, 6.363636 }, + /* 'j' */ { 78, 152, 84, 179, -1.500000, -20.750000, 6.363636 }, + /* 'k' */ { 88, 152, 100, 172, 1.750000, -20.750000, 14.321678 }, + /* 'l' */ { 106, 152, 108, 172, 1.750000, -20.750000, 6.363636 }, + /* 'm' */ { 114, 152, 134, 167, 1.750000, -15.250000, 23.860140 }, + /* 'n' */ { 140, 152, 152, 167, 1.750000, -15.250000, 15.930070 }, + /* 'o' */ { 158, 152, 172, 167, 0.750000, -15.250000, 15.930070 }, + /* 'p' */ { 178, 152, 191, 173, 1.750000, -15.250000, 15.930070 }, + /* 'q' */ { 196, 152, 209, 173, 1.000000, -15.250000, 15.930070 }, + /* 'r' */ { 214, 152, 222, 167, 1.750000, -15.250000, 9.538462 }, + /* 's' */ { 228, 152, 240, 167, 0.750000, -15.250000, 14.321678 }, + /* 't' */ { 246, 152, 253, 172, 0.500000, -20.250000, 7.958042 }, + /* 'u' */ { 2, 184, 14, 199, 1.750000, -15.000000, 15.930070 }, + /* 'v' */ { 20, 184, 33, 199, 0.250000, -15.000000, 14.321678 }, + /* 'w' */ { 38, 184, 58, 199, 0.000000, -15.000000, 20.685314 }, + /* 'x' */ { 64, 184, 78, 199, 0.000000, -15.000000, 14.321678 }, + /* 'y' */ { 84, 184, 98, 205, 0.250000, -15.000000, 14.321678 }, + /* 'z' */ { 102, 184, 115, 199, 0.500000, -15.000000, 14.321678 }, + /* '{' */ { 120, 184, 128, 211, 0.750000, -21.000000, 9.566434 }, + /* '|' */ { 134, 184, 136, 211, 2.500000, -21.000000, 7.440559 }, + /* '}' */ { 142, 184, 150, 211, 0.500000, -21.000000, 9.566434 }, + /* '~' */ { 156, 184, 170, 188, 1.000000, -12.500000, 16.727272 }, + /* '' */ { 176, 184, 190, 202, 3.500000, -18.000000, 21.482517 } +}; + diff --git a/wolf3d/code/iphone/default.png b/wolf3d/code/iphone/default.png index 93416659df4ad4043ab89b05973802025dacb255..c3505d4ccc61b0e689e540595107e70d556bb80b 100644 GIT binary patch delta 88252 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

hp!oO5Xmui)QK7ab;`z5RRJ+;k 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�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

AqngrZDB0vpymM4%ta=Yw=4y|czF-w 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 M07*qoM6N<$f(Gjb)c^nh delta 84256 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 120 && x < 360 && y > 80 && y < 240 ) { + dragHud->hudFlags |= HF_DISABLED; + } else { + // magnet pull a matchable axis if it is close enough + if ( dragHud == &huds.forwardStick ) { + SnapSticks( &huds.sideStick, dragHud ); + SnapSticks( &huds.turnStick, dragHud ); + } + if ( dragHud == &huds.sideStick ) { + SnapSticks( &huds.forwardStick, dragHud ); + } + if ( dragHud == &huds.turnStick ) { + SnapSticks( &huds.forwardStick, dragHud ); + } + } + Sound_StartLocalSound( "iphone/baction_01.wav" ); + dragHud = NULL; + } + + if ( numTouches == 1 && numPrevTouches == 0 ) { + // identify the hud being touched for drag + int x = touches[0][0]; + int y = touches[0][1]; + + //convert x, y coordinates from iphone_image to screen coordinates + ConvertFromImageToScreen( &x, &y, boundsRect); +// ConvertFromScreenToImage( &x, &y); + + dragHud = NULL; + for ( hudPic_t *hud = (hudPic_t *)&huds ; hud != (hudPic_t *)(&huds+1) ; hud++ ) { + if ( x >= hud->x && x - hud->x < hud->width && + y >= hud->y && y - hud->y < hud->height ) { + dragHud = hud; + //int tempDragX = dragHud->x; + //int tempDragY = dragHud->y; + //ConvertFromImageToScreen(&, <#int * y#>) + dragX = dragHud->x - x; + dragY = dragHud->y - y; + Sound_StartLocalSound( "iphone/bdown_01.wav" ); + dragHud->hudFlags &= ~HF_DISABLED; + break; + } + } + } + + if ( numTouches == 1 && numPrevTouches == 1 && dragHud ) { + // adjust the position of the dragHud + int x = touches[0][0]; + int y = touches[0][1]; + ConvertFromImageToScreen(&x, &y, boundsRect); + + dragHud->x = x + dragX; + dragHud->y = y + dragY; + if ( dragHud->x < 0 ) { + dragHud->x = 0; + } + if ( dragHud->x > 480 - dragHud->width ) { + dragHud->x = 480 - dragHud->width; + } + if ( dragHud->y < 0 ) { + dragHud->y = 0; + } + if ( dragHud->y > 320 - dragHud->height ) { + dragHud->y = 320 - dragHud->height; + } + } + + // layout the disabled items in the center + w = 0; + for ( hudPic_t *hud = (hudPic_t *)&huds ; hud != (hudPic_t *)(&huds+1) ; hud++ ) { + if ( hud->hudFlags & HF_DISABLED ) { + w += hud->width; + } + } + x = 240 - w / 2; + + for ( hudPic_t *hud = (hudPic_t *)&huds ; hud != (hudPic_t *)(&huds+1) ; hud++ ) { + if ( hud->hudFlags & HF_DISABLED ) { + hud->x = x; + hud->y = 160-hud->height/2; + x += hud->width; + } + } + + // decide where the menu button, map button, and ammo will draw + + // solid background color and some UI elements for context + R_Draw_Fill( 0, 0, 480, 320, gray ); + + iphoneDrawPic(0, 0, 480, 320, "iphone/submenus_background_image.tga"); + //iphoneDrawPic(240-108, 0, 217, 50, "iphone/header_advanced.tga"); + iphoneDrawPic(240-108*9/10, 0, 217*9/10, 50*9/10, "iphone/header_advanced.tga"); + + //gsh + iphoneDrawPic(10, 60-adjustY, 462, 250, "iphone/iphone_image.tga"); +// iphoneDrawPic(0, 320-240, 480, 240, "iphone/iphone_image.tga"); + + +// iphoneDrawFace(); +// iphoneDrawNotifyText(); + + + // draw the active items at their current locations + for ( hudPic_t *hud = (hudPic_t *)&huds ; hud != (hudPic_t *)(&huds+1) ; hud++ ) { //nice foreach loop! + if ( hud->hudFlags & HF_DISABLED ) { + pfglColor3f( 0.5, 0.5, 0.5 ); + } + if ( hud == &huds.ammo ) { + int x = huds.ammo.x + huds.ammo.width / 2; + int y = huds.ammo.y; + int w = 48; + int h = 48; + + ConvertFromScreenToImage(&x, &y, boundsRect); + ScaleFromScreenToImage(&w, &h, boundsRect); + + iphoneDrawNumber( x, y, 99, w, h ); + } else { + int x = hud->x; + int y = hud->y; + int w = hud->width; + int h = hud->height; + + ConvertFromScreenToImage(&x, &y, boundsRect); + ScaleFromScreenToImage(&w, &h, boundsRect); + + iphoneDrawPicNum( x, y, w, h, hud->glTexNum ); + } + pfglColor3f( 1, 1, 1 ); + } + + + + + + // draw the back button + if ( iphoneDrawPicWithTouch( 0, 0, 64, 36, "iphone/button_back.tga" ) ) { + menuState = IPM_CONTROLS; + iphoneSetNotifyText( "" ); + } + + //x = 64; + //if (revLand->value) + x = 480-64; + // draw the menu button + if ( iphoneDrawPicWithTouch( x, 0, 64, 36, "iphone/menu.tga" ) ) { + //if ( iphoneDrawPicWithTouch( 64, 0, 64, 36, "iphone/button_menu_yellow.tga" ) ) { + menuState = IPM_MAIN; + iphoneSetNotifyText( "" ); + } + + /* + //------------------------------------ + //Draw volume up/down settings... gsh + int width = 64*9/10; + int height = 36;//48*9/10; + + int y = 0; + y = 0; + x = 480-width; + if (revLand->value){ + y = 320-height; + x = 0; + } + + //gsh + if ((int)volumeFireUpSetting->value) { + if (iphoneDrawPicWithTouch( x, y, width, height, "iphone/button_pistol.tga" )) + Cvar_Set("volumeFireUpSetting", "0"); + } + else { + if (iphoneDrawPicWithTouch( x, y, width, height, "iphone/button_knife.tga" )) + Cvar_Set("volumeFireUpSetting", "1"); + } + + + x = 480-width-width-5; + if (revLand->value) + x = width+5; + + if ((int)volumeFireDownSetting->value) { + if (iphoneDrawPicWithTouch( x, y, width, height, "iphone/button_pistol.tga" )) + Cvar_Set("volumeFireDownSetting", "0"); + } + else { + if (iphoneDrawPicWithTouch( x, y, width, height, "iphone/button_knife.tga" )) + Cvar_Set("volumeFireDownSetting", "1"); + } + //----------------------------------- + */ + //draw the instructions + //if (revLand->value) + // iphoneDrawText(x+width+20, 300, 16, 16, "Drag the controls to customize"); + //else +// iphoneCenterText(240, 300, "Drag each element of the controls to customize"); + iphoneCenterArialText(245, 315, 0.8f, "Drag each element of the controls to customize"); +} + diff --git a/wolf3d/code/iphone/iphone_alerts.h b/wolf3d/code/iphone/iphone_alerts.h new file mode 100644 index 0000000..45227d0 --- /dev/null +++ b/wolf3d/code/iphone/iphone_alerts.h @@ -0,0 +1,39 @@ +/* + * iphone_alerts.h + * wolf3d + * + * Created by Greg Hodges on 7/14/09. + * Copyright 2009 id software. All rights reserved. + * + * C wrappers for the UIAlertView. + * + */ +/* + + Copyright (C) 2009 Id Software, Inc. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + */ + + +#ifndef __IPHONE_ALERTS__ +#define __IPHONE_ALERTS__ + +void iphoneMessageBox(char *title, char *message); +void iphoneKillMessageBox(); +void iphoneYesNoBox(char *title, char *message); + +#endif \ No newline at end of file diff --git a/wolf3d/code/iphone/iphone_alerts.m b/wolf3d/code/iphone/iphone_alerts.m new file mode 100644 index 0000000..e1a4464 --- /dev/null +++ b/wolf3d/code/iphone/iphone_alerts.m @@ -0,0 +1,110 @@ +/* + * iphone_alerts.c + * wolf3d + * + * Created by Greg Hodges on 7/14/09. + * Copyright 2009 id software. All rights reserved. + * + */ +/* + + Copyright (C) 2009 Id Software, Inc. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + */ + + +#import "iphone_alerts.h" +#import "wolf3dAppDelegate.h" + + +/* + ================================== + MessageBox + Provides a basic pop-up message box + =================================== + */ +UIAlertView *alert; + +void InitAlert() +{ + alert = [[UIAlertView alloc] initWithTitle:@"Title" + message:@"Message" + delegate:nil + cancelButtonTitle:@"OK" + otherButtonTitles: nil]; +} + + +void iphoneMessageBox(char *title, char *message) +{ + //check if alert exists and initialize if it isn't + if (!alert) + { + InitAlert(); + } + + NSString *nsTitle = [[NSString alloc] initWithCString:title]; + NSString *nsMessage = [[NSString alloc] initWithCString:message]; + + alert.title = nsTitle; + alert.message = nsMessage; + + [alert show]; +} + +void iphoneKillMessageBox() +{ + [alert dismissWithClickedButtonIndex:alert.cancelButtonIndex animated:NO]; +} + +#if 1 +/* + ================================== + YesNoBox + Provides a yes/no box. The + appdelegate responds to this + through the UIAlerViewDelegate + ClickedButton call. + =================================== + */ +UIAlertView *alertYesNo; + +void InitAlertYesNo() +{ + alertYesNo = [[UIAlertView alloc] initWithTitle:@"Title" + message:@"Message" + delegate:(wolf3dAppDelegate *)[UIApplication sharedApplication].delegate//nil + cancelButtonTitle:@"No" + otherButtonTitles:@"Yes", nil]; +} + +void iphoneYesNoBox(char *title, char *message) +{ + if (!alertYesNo) + { + InitAlertYesNo(); + } + + NSString *nsTitle = [[NSString alloc] initWithCString:title]; + NSString *nsMessage = [[NSString alloc] initWithCString:message]; + + alertYesNo.title = nsTitle; + alertYesNo.message = nsMessage; + + [alertYesNo show]; +} +#endif diff --git a/wolf3d/code/iphone/iphone_downloadSOD.m b/wolf3d/code/iphone/iphone_downloadSOD.m new file mode 100644 index 0000000..0cf94a3 --- /dev/null +++ b/wolf3d/code/iphone/iphone_downloadSOD.m @@ -0,0 +1,359 @@ +// +// iphone_downloadSOD.m +// wolf3d +// +// Created by Greg Hodges on 6/8/09. +// Copyright 2009 id software. All rights reserved. +// +/* + + Copyright (C) 2009 Id Software, Inc. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + */ + + + +#import "../wolfiphone.h" +#import "wolf3dAppDelegate.h" +#import "iphone_alerts.h" + +#include +#include + +#import //this is for testing network availability + +//Note: in order to link the zlib library into the build from usr/lib/libz.ylib +// the flag -lz was added to the OTHER_LDFLAGS under the build options. +// To get there: right click on the wolf3d project. choose get info. +// Click on build and scroll to "Other Link Flags" under "Linking" + + +extern void Com_Printf(const char* fmt, ... ); +extern void my_snprintf( char *dest, size_t size, const char *format, ... ); + +#ifdef SPEARSTOREKIT +extern void PurchaseSOD(); +extern bool isStorekitAvailable; +#endif +extern char iphoneDocDirectory[1024]; +extern menuState_t menuState; + + +int inf(FILE *source, FILE *dest); +void zerr(int ret); + + +//this is used for determining if we have the correctly sized data +const unsigned int DownloadFileSize = 45583; //size of maps + +//----------------------------- +// TestURLConnection +// Test for internet access +//----------------------------- +int TestURLConnection() +{ + Com_Printf("Testing URL Connection\n"); + + //We wish to be able to test connectability to both apple and id + //because we need to communicate to both in order to complete the Spear of Destiny Transaction + + char *hostid = "www.idsoftware.com"; + char *hostApple = "www.apple.com"; + SCNetworkReachabilityFlags flags[2]; + SCNetworkReachabilityRef reachability; + BOOL gotFlags[2]; + + reachability = SCNetworkReachabilityCreateWithName(NULL, hostid); + gotFlags[0] = SCNetworkReachabilityGetFlags(reachability, &flags[0]); + CFRelease(reachability); + + reachability = SCNetworkReachabilityCreateWithName(NULL, hostApple); + gotFlags[1] = SCNetworkReachabilityGetFlags(reachability, &flags[1]); + CFRelease(reachability); + + + if (gotFlags[0] && gotFlags[1]) + { + // kSCNetworkReachabilityFlagsReachable indicates that the specified nodename or address can + // be reached using the current network configuration. + if ((flags[0] & kSCNetworkReachabilityFlagsReachable) && (flags[1] & kSCNetworkReachabilityFlagsReachable)) + { + Com_Printf("Both Hosts were reached\n"); + return 1; + } + } + + //TODO: alert user they cannot download SOD + iphoneMessageBox("Host Server Unavailable", "Check your internet connection and try again later."); + + return 0; +} + +//----------------------------- +// OpenURLConnection +// This connects to a server and downloads the file located there +//----------------------------- +void OpenURLConnection( const char *url ) +{ + Com_Printf( "ConnectURL char *: %s\n", url ); + + //convert url to nsstring + NSString *nssURL = [NSString stringWithUTF8String: url]; + + // create the request + NSURLRequest *theRequest=[NSURLRequest requestWithURL:[NSURL URLWithString:nssURL] + cachePolicy:NSURLRequestUseProtocolCachePolicy + timeoutInterval:60.0]; + + // create the connection with the request + // and start loading the data + NSURLConnection *theConnection=[[NSURLConnection alloc] initWithRequest:theRequest + delegate:(wolf3dAppDelegate *)[UIApplication sharedApplication].delegate]; + + if (!theConnection) + { + // inform the user that the download could not be made + Com_Printf("Connection failed... no download plausible\n"); + + //inform user + iphoneMessageBox("Connection Failed", "The install was unable to download. Check your internet connection and try again later."); + + //return to main menu + menuState = IPM_MAIN; + } +} + +//============================ +// DisplayWrongVersionedOS +// Display to the user they have the wrong OS version and cannot use storekit +//============================= +void DisplayWrongVersionedOS() +{ + iphoneMessageBox("Feature Unavailable", "Your device is not updated. This feature requires OS 3.0 or higher."); +} + +//============================ +// BeginStoreKit +// This starts the purchasing process of the storekit framework +//============================= +#ifdef SPEARSTOREKIT +void BeginStoreKit() +{ + //only start the storekit API if it exists + if (isStorekitAvailable) + { + if (TestURLConnection()) //check URL connection again + { + menuState = IPM_STOREKIT; + Com_Printf("BeginStoreKit() called\n"); + PurchaseSOD(); + } + } + else + DisplayWrongVersionedOS(); +} +#endif + + +//============================ +// DownloadSOD +// starts the download process of SOD +// this is called after purchase of the SOD levels is confirmed +//============================= +void DownloadSOD() +{ + menuState = IPM_DOWNLOADPROGRESS; //change the menu to the download screen + + NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); + NSString *documentsDirectory = [paths objectAtIndex:0]; + + //check if download.tgz already exists and delete if it does + //create/open file for saving + NSString *fileatpath = [documentsDirectory stringByAppendingString:@"/downloadedfile.tgz"]; + NSFileHandle *file = [NSFileHandle fileHandleForWritingAtPath:fileatpath]; + + if (file) //file exists... delete it, we do not care to append to a pre-exisiting file + { + if( remove( [fileatpath UTF8String] ) != 0 ) + Com_Printf("Error deleting buffer\n"); + else + Com_Printf("Deleted buffer\n"); + } + + //TODO: change this url to what will eventually be the permanent location + OpenURLConnection("http://gregory.hodges.googlepages.com/spearMaps.tgz"); + + //after this the control automatically goes back to the appDelegate where it receives the packets of downloaded information +} + +unsigned int dataAmount = 0; +unsigned int dataTotalAmount = 0; +extern unsigned int totalDownloaded; +//int hasBeenMessaged = 0; + +//============================ +// AppendData +// adds a packet of data directly to file +//============================ +void AppendDataToFile(NSData* data) +{ + NSUInteger length = [data length]; + + dataAmount = (unsigned int)length; + dataTotalAmount += dataAmount; + totalDownloaded = dataTotalAmount; //update the download screen with the total downloaded + + //get documents directory + NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); + NSString *documentsDirectory = [paths objectAtIndex:0]; + + //create/open file for saving + NSString *fileatpath = [documentsDirectory stringByAppendingString:@"/downloadedfile.tgz"]; + NSFileHandle *file = [NSFileHandle fileHandleForWritingAtPath:fileatpath]; + + if (!file) //file does not exist... create and append file + { + NSLog(@"open file failed\ncreating file with path: %@\n", fileatpath); + [[NSFileManager defaultManager] createFileAtPath:fileatpath + contents:data + attributes:nil]; + return; + } + + //seek to end of file and append data + [file seekToEndOfFile]; + [file writeData:data]; + [file closeFile]; +} + + + +//=========================== +// DecompressData +// Calls into untgz.c to start data inflation +// this function should not be getting called +//============================ +void DecompressData() +{ + Com_Printf("Starting DecompressData"); + + char path[1024]; + my_snprintf(path, sizeof(path), "%s/downloadedfile.tgz", iphoneDocDirectory); +} + +//================================ +// IsSpearPurchased +// returns 1 if Spear was purchased +//================================ +int IsSpearPurchased() +{ + //get documents directory + NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); + NSString *documentsDirectory = [paths objectAtIndex:0]; + + //create/open file for saving + NSString *fileatpath = [documentsDirectory stringByAppendingString:@"/SpearPurchased.log"]; + NSFileHandle *file = [NSFileHandle fileHandleForWritingAtPath:fileatpath]; + + if (file) + return 1; + + return 0; +} + +//================================ +// IsSpearInstalled +// returns 1 if Spear is installed +//================================ +int IsSpearInstalled() +{ + //get documents directory + NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); + NSString *documentsDirectory = [paths objectAtIndex:0]; + + //create/open file for saving + NSString *fileatpath = [documentsDirectory stringByAppendingString:@"/SpearInstalled.log"]; + NSFileHandle *file = [NSFileHandle fileHandleForWritingAtPath:fileatpath]; + + if (file) + return 1; + + return 0; +} + +//================================ +// WriteDownloadLog +// records that we've finished installing SOD +// but haven't completed the purchase +//================================ +void WriteInstallLog() +{ + char *buffer = "useless data"; + NSData *data = [[NSData alloc] initWithBytes: (void *)buffer length: strlen(buffer)]; + + //get documents directory + NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); + NSString *documentsDirectory = [paths objectAtIndex:0]; + + //create/open file for saving + NSString *fileatpath = [documentsDirectory stringByAppendingString:@"/SpearInstalled.log"]; + NSFileHandle *file = [NSFileHandle fileHandleForWritingAtPath:fileatpath]; + + if (!file) //file does not exist... create and append file + { + NSLog(@"open file failed\ncreating file with path: %@\n", fileatpath); + [[NSFileManager defaultManager] createFileAtPath:fileatpath + contents:data + attributes:nil]; + return; + } + +} + +//================================ +// FinalizeDownload +// Installs the needed files for SOD and +// removes any unwanted data +//================================ +extern void iphoneWriteConfig(); +void FinalizeDownload() +{ + // get the documents directory + NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); + NSString *documentsDirectory = [paths objectAtIndex:0]; + NSString *fileName = [documentsDirectory stringByAppendingString:@"/downloadedfile.tgz"]; + + //inflate the data and store in its appropriate directory + DecompressData(); + + //TODO: After inflating data... we should delete the downloadedfile.tgz + if( remove( [fileName UTF8String] ) != 0 ) + Com_Printf( "Error deleting downloadedfile.tgz\n" ); + else + Com_Printf( "downloadedfile.tgz successfully deleted\n" ); + + + iphoneKillMessageBox(); + + //write a file that lets wolf3d know that SOD is installed + WriteInstallLog(); + +#ifdef SPEARSTOREKIT + //start the storekit + BeginStoreKit(); +#endif +} diff --git a/wolf3d/code/iphone/iphone_downloadUserMap.m b/wolf3d/code/iphone/iphone_downloadUserMap.m new file mode 100644 index 0000000..1b1172b --- /dev/null +++ b/wolf3d/code/iphone/iphone_downloadUserMap.m @@ -0,0 +1,223 @@ +/* + * iphone_downloadUserMap.c + * wolf3d + * + * Created by Greg Hodges on 7/20/09. + * Copyright 2009 id software. All rights reserved. + * + */ +/* + + Copyright (C) 2009 Id Software, Inc. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + */ + + +#import "../wolfiphone.h" +#import "wolf3dAppDelegate.h" +#import "iphone_alerts.h" + +#include +#include + +extern void Com_Printf(const char* fmt, ... ); +extern void my_snprintf( char *dest, size_t size, const char *format, ... ); + +extern char iphoneDocDirectory[1024]; +extern menuState_t menuState; + + +int inf(FILE *source, FILE *dest); +void zerr(int ret); + + +char mapName[1024];// = "/"; + +//this is used for determining if we have the correctly sized data +const unsigned int DownloadUserFileSize = 4096; //max size of maps? + + +//----------------------------- +// DownloadURLConnection +// This connects to a server and downloads the file located there +//----------------------------- +void DownloadURLConnection( char *url ) +{ + Com_Printf( "ConnectURL char *: %s\n", url ); + + int length = strlen(url); + if (length <= 4) + { + iphoneMessageBox("error", "url is not a valid map name. Maps must end in \".map\""); + return; + } + + length = strlen(url); + //acquire file name of map + int pos = length; + while (pos > 0) + { + --pos; + if (url[pos] == '/') + break; + } + ++pos; + strcpy(mapName, url + pos); + + //------------------------------------------------- + //check if this file already exists... if so, delete the old one + //get documents directory + NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); + NSString *documentsDirectory = [paths objectAtIndex:0]; + + //get maps directory + NSString *mapsDirectory = [documentsDirectory stringByAppendingString:@"/usermaps/"]; + + DIR *dp = opendir ([mapsDirectory UTF8String]); + if (dp != NULL) + { + struct dirent *ep; + while (ep = readdir (dp)) + { + //if you find a .DS_Store file... ignore it! + if ( strcmp(ep->d_name, ".DS_Store") == 0 ) + continue; + if ( strcmp(ep->d_name, ".") == 0 ) + continue; + if ( strcmp(ep->d_name, "..") == 0 ) + continue; + + if (strcmp(ep->d_name, mapName) == 0) + { + printf("found a file with name: %s\n", mapName); + printf("overwiting file\n"); + //TODO: delete this file + char buffer[2048]; + sprintf(buffer, "%s%s", [mapsDirectory UTF8String], mapName); + printf("removing at: %s\n", buffer); + remove(buffer); + } + } + } + //------------------------------------------------- + + + //convert url to nsstring + NSString *nssURL = [NSString stringWithUTF8String: url]; + + // create the request + NSURLRequest *theRequest=[NSURLRequest requestWithURL:[NSURL URLWithString:nssURL] + cachePolicy:NSURLRequestUseProtocolCachePolicy + timeoutInterval:60.0]; + + // create the connection with the request + // and start loading the data + NSURLConnection *theConnection=[[NSURLConnection alloc] initWithRequest:theRequest + delegate:(wolf3dAppDelegate *)[UIApplication sharedApplication].delegate]; + + if (!theConnection) + { + // inform the user that the download could not be made + Com_Printf("Connection failed... no download plausible\n"); + + //inform user + iphoneMessageBox("Connection Failed", "Can not download. Check your internet connection, file url and try again later."); + + //return to main menu + menuState = IPM_MAIN; + } + + menuState = IPM_DOWNLOADPROGRESS; +} + +unsigned int userDataAmount = 0; +unsigned int userDataTotalAmount = 0; +extern unsigned int totalDownloaded; + +//============================ +// AppendData +// adds a packet of data directly to file +//============================ +void AppendUserDataToFile(NSData* data) +{ + NSUInteger length = [data length]; + + userDataAmount = (unsigned int)length; + userDataTotalAmount += userDataAmount; + totalDownloaded = userDataTotalAmount; //update the download screen with the total downloaded + + //get documents directory + NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); + NSString *documentsDirectory = [paths objectAtIndex:0]; + + //get maps directory + NSString *mapsDirectory = [documentsDirectory stringByAppendingString:@"/usermaps/"]; + + //check if maps directory exists, if not create maps directory + mkdir([mapsDirectory UTF8String], 0755); + + //create/open file for saving + NSString *fileatpath = [mapsDirectory stringByAppendingString:[NSString stringWithUTF8String:mapName]]; + NSFileHandle *file = [NSFileHandle fileHandleForWritingAtPath:fileatpath]; + + if (!file) //file does not exist... create and append file + { + NSLog(@"open file failed\ncreating file with path: %@\n", fileatpath); + [[NSFileManager defaultManager] createFileAtPath:fileatpath + contents:data + attributes:nil]; + return; + } + + //seek to end of file and append data + [file seekToEndOfFile]; + [file writeData:data]; + [file closeFile]; +} + +//================================ +// FinalizeUserDownload +//================================ +extern int Level_VerifyMap( const char *levelname ); +extern int iphoneGetUserMapLevelByName(const char *mapName); +void FinalizeUserDownload() +{ + char buffer[1024]; + sprintf(buffer, "usermaps/%s", mapName); + + //we need to verify that this is a true .map file + if (Level_VerifyMap(buffer) == 0) + { + //it wasn't a valid map. Inform user and delete + iphoneMessageBox("invalid map", "downloaded file is an invalid map"); + + NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); + NSString *documentsDirectory = [paths objectAtIndex:0]; + + char buffer2[2048]; + sprintf(buffer2, "%s/usermaps/%s", [documentsDirectory UTF8String], mapName); + remove(buffer2); + menuState = IPM_MAIN; + return; + } + + //start this map immediately! + menuState = IPM_EPISODE; + iphonePreloadBeforePlay(); + int level = iphoneGetUserMapLevelByName(mapName); + iphoneStartUserMap(10, level, currentMap.skill, mapName); +} diff --git a/wolf3d/code/iphone/iphone_loop.c b/wolf3d/code/iphone/iphone_loop.c index fd75703..2667547 100644 --- a/wolf3d/code/iphone/iphone_loop.c +++ b/wolf3d/code/iphone/iphone_loop.c @@ -120,8 +120,9 @@ brown plant cp 136.5551 ~/dev/iphone/wolf3d/base/sprites/013.5551 */ - +#include //gsh #include "../wolfiphone.h" +#include "arialGlyphRects.h" //gsh... cass made a nice fontimage helper. We might as well use it. currentMap_t currentMap; @@ -131,12 +132,16 @@ int iphoneFrameNum; int intermissionTriggerFrame; int slowAIFrame; +//gsh +int returnButtonFrameNum = 0; + // console mode int consoleActive; // the native iPhone code should set the following each frame: int numTouches; int touches[5][2]; // [0] = x, [1] = y in landscape mode, raster order with y = 0 at top +int isTouchMoving = 0; //gsh float tilt; // -1.0 to 1.0 float tiltPitch; @@ -150,6 +155,9 @@ int prevTouches[5][2]; texture_t *numberPics[10]; +//gsh +texture_t *arialFontTexture; + char *mugshotnames[ NUM_MUGSHOTS ] = { "iphone/FACE1APIC.tga", @@ -181,7 +189,11 @@ char *mugshotnames[ NUM_MUGSHOTS ] = "iphone/FACE7CPIC.tga", "iphone/FACE8APIC.tga", -"iphone/GOTGATLINGPIC.tga" +"iphone/GOTGATLINGPIC.tga", + +"iphone/GODMODEFACE0PIC.tga", +"iphone/GODMODEFACE1PIC.tga", +"iphone/GODMODEFACE2PIC.tga", }; int damageflash; @@ -308,6 +320,439 @@ int iphoneCenterText( int x, int y, const char *str ) { return l * step; } +//gsh +void iphoneCenterTextWithColor(int x, int y, const char *str, colour4_t color ) { + + pfglColor4f(color[0], color[1], color[2], color[3]); + + iphoneCenterText(x, y, str); + + pfglColor4f(1, 1, 1, 1); +} +/* + ================== + iphoneDrawArialText + left justified arial text + gsh + Returns the width in pixels + ================== + */ +int iphoneDrawArialText( int x, int y, float scale, const char *str ) { + /* + int l = strlen( str ); + int i; + font_t *myfont = myfonts[0]; + // int scale; + int step = 10; + + // scale = 16; + int left_margin = x; + */ + texture_t *gl; + + gl = TM_FindTexture( "iphone/arialImageLAL_white-alpha.tga", TT_Pic ); + if( ! gl ) { + Com_Printf( "Can't find pic: %s\n", "iphone/arialImageLAL_white-alpha.tga" ); + return 0; + } + + R_Bind( gl->texnum ); + + +// float fx = x; +// float fy = y; + +// int scale = 1;//16; +// float scale = 0.9f; + + pfglBegin( GL_QUADS ); + + while ( *str ) { + int i = *str; + if ( i >= ' ' && i < 128 ) { + GlyphRect *glyph = &glyphRects[i-32]; + + // the glyphRects don't include the shadow outline + float x0 = ( glyph->x0 - 2 ) / 256.0; + float y0 = ( glyph->y0 - 2 ) / 256.0; + float x1 = ( glyph->x1 + 3 ) / 256.0; + float y1 = ( glyph->y1 + 3 ) / 256.0; + + + float width = ( x1 - x0 ) * 256 * scale; + float height = ( y1 - y0 ) * 256 * scale; + +// float xoff = ( glyph->xoff - 1 ) * scale; + float yoff = ( glyph->yoff - 1 ) * scale; + + if (i == 'l' || i == 'I' || i == 'h' || i == 'i') + yoff += 1; + + pfglTexCoord2f( x0, y0 ); +// pfglVertex2f( fx + xoff, fy + yoff ); + pfglVertex2i( x, y + yoff); + + pfglTexCoord2f( x1, y0 ); +// pfglVertex2f( fx + xoff + width, fy + yoff ); + pfglVertex2i( x+width, y + yoff); + + pfglTexCoord2f( x1, y1); +// pfglVertex2f( fx + xoff + width, fy + yoff + height ); + pfglVertex2i( x+width, y+height + yoff ); + + pfglTexCoord2f( x0, y1); +// pfglVertex2f( fx + xoff, fy + yoff + height ); + pfglVertex2i( x, y+height + yoff); + + // with our default texture, the difference is negligable +// fx += glyph->xadvance * scale; + x += glyph->xadvance * scale; + // fx += ceil(glyph->xadvance); // with the outline, ceil is probably the right thing + } + str++; + } + + pfglEnd(); + + return x; +} +/* + ================== + iphoneCenterArialText + center justified arial text + gsh + Returns the width in pixels + ================== + */ +int iphoneCenterArialText( int x, int y, float scale, const char *str ) +{ + const char *strcopy = str; + float width = 0; + while ( *str ) + { + int i = *str; + if ( i >= ' ' && i < 128 ) { + + GlyphRect *glyph = &glyphRects[i-32]; + width += glyph->xadvance * scale; + } + ++str; + } + + return iphoneDrawArialText( x - width/2, y, scale, strcopy ); +} +/* + ================== + iphoneDrawArialTextInBox + center justified arial text + gsh + Returns the width in pixels + ================== + */ +int iphoneDrawArialTextInBox( rect_t paragraph, int lineLength, const char *str, rect_t boxRect ) { + int l = strlen( str ); + int i; + + if (paragraph.x > boxRect.x + boxRect.width) + return 0; + if (paragraph.x + lineLength < boxRect.x) + return 0; + + int x = paragraph.x; + int y = paragraph.y; +// Com_Printf("y value: %i\n", y); +// int width = paragraph.width; //font width +// int height = paragraph.height; //font height + + const int left_margin = x+5; + int step = 10; + + texture_t *gl; + + gl = TM_FindTexture( "iphone/arialImageLAL_white-alpha.tga", TT_Pic ); + if( ! gl ) { + Com_Printf( "Can't find pic: %s\n", "iphone/arialImageLAL_white-alpha.tga" ); + return 0; + } + + R_Bind( gl->texnum ); + pfglBegin( GL_QUADS ); + + int lengthOfNextWord = 0; + float scale = 0.65f;//0.9f; + + for ( i = 0 ; i < l ; i++/*, x += step*/ ) { + int m = str[i]; + GlyphRect *glyph = &glyphRects[m-32]; + + float x0 = ( glyph->x0 - 2 ) / 256.0; + float y0 = ( glyph->y0 - 2 ) / 256.0; + float x1 = ( glyph->x1 + 3 ) / 256.0; + float y1 = ( glyph->y1 + 3 ) / 256.0; + + float width = ( x1 - x0 ) * 256 * scale; + float height = ( y1 - y0 ) * 256 * scale; + + // float xoff = ( glyph->xoff - 1 ) * scale; + float yoff = ( glyph->yoff - 1 ) * scale; + + + //int row, col; + //float frow, fcol; + int num = str[i]; + + //check when to new-line + if ( num == ' ' ) { + float w = 0; + int n = i + 1; + while ( str[n] != ' ' && str[n] != '\0' && str[n] != '\n') { + //++n; + int m = str[n]; + GlyphRect *glyph2 = &glyphRects[m-32]; + w += glyph2->xadvance * scale; + ++n; + } + lengthOfNextWord = n - i - 1; +// Com_Printf("length of word: %i\n", n - i - 1); +// Com_Printf("length of word pixels: %f\n",w); + + if ( w + x > lineLength + left_margin ) { + y += 30*scale; + x = left_margin; + } + else + x += 10*scale; + //whil + //x += 10*scale; + continue; + } + if (num == '\n') { + y += 30*scale; + x = left_margin; + continue; + }/* + if (x + glyph->xadvance * scale > left_margin + lineLength) { + y += 30*scale; + x = left_margin;// + width; + }*/ + + //check rendering boundaries + if (x < boxRect.x+10) { + x += glyph->xadvance * scale + 1; + continue; + } + if (x + glyph->xadvance * scale > boxRect.x + boxRect.width) { + x += glyph->xadvance * scale + 1; + continue; + } + + if (y > boxRect.y + boxRect.height) + break; + + pfglTexCoord2f( x0, y0 ); + pfglVertex2i( x, y + yoff); + + pfglTexCoord2f( x1, y0 ); + pfglVertex2i( x+width, y + yoff); + + pfglTexCoord2f( x1, y1); + pfglVertex2i( x+width, y+height + yoff ); + + pfglTexCoord2f( x0, y1); + pfglVertex2i( x, y+height + yoff); + + x += glyph->xadvance * scale + 1; + + } + + pfglEnd(); + + return l * step; +} + + + +int iphoneDrawText( int x, int y, int width, int height, const char *str ) { + int l = strlen( str ); + int i; + font_t *myfont = myfonts[0]; +// int scale; + int step = 10; + +// scale = 16; + int left_margin = x; + + R_Bind( myfont->texfont->texnum ); + //R_Bind(arialFontTexture->texnum ); + pfglBegin( GL_QUADS ); + + for ( i = 0 ; i < l ; i++, x += step ) { + int row, col; + float frow, fcol; + int num = str[i]; + + if ( num == ' ' ) { + continue; + } + if (num == '\n') { + y += height; + x = left_margin; + } + + row = (num >> 4) - 2; + col = num & 15; + + frow = row * myfont->hFrac; + fcol = col * myfont->wFrac; + + pfglTexCoord2f( fcol, frow ); + pfglVertex2i( x, y ); + + pfglTexCoord2f( fcol+myfont->wFrac, frow ); + pfglVertex2i( x+width, y ); + + pfglTexCoord2f( fcol+myfont->wFrac, frow+myfont->hFrac ); + pfglVertex2i( x+width, y+height ); + + pfglTexCoord2f( fcol, frow+myfont->hFrac ); + pfglVertex2i( x, y+height ); + } + + pfglEnd(); + + return l * step; +} + +/* + ================== + iphoneDrawTextWithColor + gsh + ================== + */ +void iphoneDrawTextWithColor( rect_t rect, const char *str, float colors[4] ) { + + pfglColor4f(colors[0], colors[1], colors[2], colors[3]); + iphoneDrawText(rect.x, rect.y, rect.width, rect.height, str); + pfglColor4f(1, 1, 1, 1); +} + +/* + ================== + iphoneDrawMapName + gsh + ================== + */ +void iphoneDrawMapName( rect_t rect, const char *str ) { + + rect.y += 25; + rect.x += 110;//80; + /* + float colors[4] = { 0, 0, 0, 1 }; + iphoneDrawTextWithColor(RectMake(rect.x+1, rect.y+1, rect.width, rect.height), str, colors); + iphoneDrawTextWithColor(RectMake(rect.x+2, rect.y+2, rect.width, rect.height), str, colors); + + colors[0] = 225.0f/255; + colors[1] = 166.0f/255; + iphoneDrawTextWithColor(rect, str, colors); + */ + + pfglColor4f(0, 0, 0, 1); + iphoneDrawArialText(rect.x + 1, rect.y + 1, 0.9f, str); + iphoneDrawArialText(rect.x + 2, rect.y + 2, 0.9f, str); + pfglColor4f(225.0f/255, 166.0f/255, 0, 1); + pfglColor4f(225.0f/255, 242.0f/255, 0, 1); + iphoneDrawArialText(rect.x, rect.y, 0.9f, str); + pfglColor4f(1, 1, 1, 1); +} + +/* + ================== + iphoneDrawTextInBox + gsh + Returns the width in pixels + ================== + */ +int iphoneDrawTextInBox( rect_t paragraph, int lineLength, const char *str, rect_t boxRect ) { + int l = strlen( str ); + int i; + font_t *myfont = myfonts[0]; + + int x = paragraph.x; + int y = paragraph.y; + int width = paragraph.width; //font width + int height = paragraph.height; //font height + + int left_margin = x; + int step = 10; + + R_Bind( myfont->texfont->texnum ); + pfglBegin( GL_QUADS ); + + int lengthOfNextWord = 0; + + for ( i = 0 ; i < l ; i++, x += step ) { + int row, col; + float frow, fcol; + int num = str[i]; + + //check when to new-line + if ( num == ' ' ) { + int n = i+1; + while (str[n] != ' ' && str[n] != '\0' && str[n] != '\n') { + ++n; + } + lengthOfNextWord = n - i;// - 1; + if (x + lengthOfNextWord*step > left_margin + lineLength) { + y += height; + x = left_margin; + } + continue; + } + if (num == '\n') { + y += height; + x = left_margin; + continue; + } + if (x + width > left_margin + lineLength) { + y += height; + x = left_margin + width; + } + + //check rendering boundaries + if (x < boxRect.x) + continue; + if (x + width > boxRect.x + boxRect.width) + continue; + if (y < boxRect.y) + continue; + if (y + height > boxRect.y + boxRect.height) + continue; + + row = (num >> 4) - 2; + col = num & 15; + + frow = row * myfont->hFrac; + fcol = col * myfont->wFrac; + + pfglTexCoord2f( fcol, frow ); + pfglVertex2i( x, y ); + + pfglTexCoord2f( fcol+myfont->wFrac, frow ); + pfglVertex2i( x+width, y ); + + pfglTexCoord2f( fcol+myfont->wFrac, frow+myfont->hFrac ); + pfglVertex2i( x+width, y+height ); + + pfglTexCoord2f( fcol, frow+myfont->hFrac ); + pfglVertex2i( x, y+height ); + } + + pfglEnd(); + + return l * step; +} + + /* ================== @@ -358,7 +803,7 @@ int TouchReleased( int x, int y, int w, int h ) { } if ( !downPrev ) { - if ( downNow ) { + if ( downNow && !isTouchMoving ) { Sound_StartLocalSound( "iphone/bdown_01.wav" ); } // wasn't down the previous frame @@ -370,14 +815,16 @@ int TouchReleased( int x, int y, int w, int h ) { return 0; } - if ( numTouches == numPrevTouches ) { + if ( numTouches == numPrevTouches && !isTouchMoving ) { // finger dragged off Sound_StartLocalSound( "iphone/baborted_01.wav" ); return 0; } - // released - Sound_StartLocalSound( "iphone/baction_01.wav" ); + if ( !isTouchMoving ) { //gsh, added the if !isTouchMoving check + // released + Sound_StartLocalSound( "iphone/baction_01.wav" ); + } return 1; } @@ -652,6 +1099,28 @@ PRIVATE void CreateIphoneUserCmd() if ( tiltFire->value > 0 && tiltPitch < tiltFire->value ) { cmd->buttons |= BUTTON_ATTACK; } + +#ifdef VOLUMHACK + //gsh... attempting a left/right click attack + if ( volumeFireUp->value ) { + + if ((int)volumeFireUpSetting->value) + cmd->buttons |= BUTTON_ATTACK; + else + cmd->buttons |= BUTTON_ALTERNATE_ATTACK; + + Cvar_Set("volumeFireUp", "0"); + } + else if ( volumeFireDown->value ) { + + if ((int)volumeFireDownSetting->value) + cmd->buttons |= BUTTON_ATTACK; + else + cmd->buttons |= BUTTON_ALTERNATE_ATTACK; + + Cvar_Set("volumeFireDown", "0"); + } +#endif // tapping the weapon issues the nextWeapon impulse if ( TouchReleased( 240 - 40, 320 - 80 - 64, 80, 64 ) ) { @@ -680,11 +1149,14 @@ iphoneHighlightPicWhenTouched Draw transparent except when touched ================= */ +//gsh TODO: change hud alphas +//TODO: make this cvar setable and adjustable in settings menu +//float alphaValueForHudControls = 1;//0.5f; void iphoneHighlightPicNumWhenTouched( int x, int y, int w, int h, int glTexNum ) { if ( TouchDown( x, y, w, h ) ) { pfglColor4f(1,1,1,1); } else { - pfglColor4f(1,1,1,0.5); + pfglColor4f(1,1,1,hudAlpha->value);//0.5); } iphoneDrawPicNum( x, y, w, h, glTexNum ); pfglColor4f(1,1,1,1); @@ -752,6 +1224,7 @@ void iphoneDrawNumber( int x, int y, int number, int charWidth, int charHeight ) for( i = 0 ; i < length ; i++ ) { int digit = string[i] - '0'; tex = numberPics[digit]; + R_Bind( tex->texnum ); pfglBegin( GL_QUADS ); @@ -828,6 +1301,10 @@ void iphoneDrawFace() { h = 0; } pic = mugshotnames[ 3*((100-h)/16)+Player.faceframe ]; + + //gsh + if ((Player.flags & FL_GODMODE)) + pic = mugshotnames[ 23+Player.faceframe ]; } } else @@ -901,11 +1378,79 @@ void iphoneDrawNotifyText() { } } + --notifyFrameNum; //gsh + +// pfglColor4f( 1, 1, 1, f ); + //iphoneCenterText( 240, 5, notifyText ); +// iphoneDrawArialText( 200, 20, 0.7f, notifyText ); //gsh + pfglColor4f( 0, 0, 0, f ); + iphoneCenterArialText( 240+1, 20+1, 0.8f, notifyText); //gsh + iphoneCenterArialText( 240+2, 20+2, 0.8f, notifyText); //gsh + iphoneCenterArialText( 240+3, 20+3, 0.8f, notifyText); //gsh pfglColor4f( 1, 1, 1, f ); - iphoneCenterText( 240, 5, notifyText ); + iphoneCenterArialText( 240, 20, 0.8f, notifyText); //gsh pfglColor4f( 1, 1, 1, 1 ); } +/* + ================== + iphoneDrawReturnButton + + Displays a button that allows the player to return to the map screen. + But it only displays for a few seconds. + ================== + */ +void iphoneDrawReturnButton() { + + if (returnButtonFrameNum <= 0) + return; + + // display for three seconds, then fade over 0.3 + float f = iphoneFrameNum - returnButtonFrameNum - 80; + if ( f < 0 ) { + f = 1.0; + } else { + f = 1.0 - f * 0.1f; + if ( f < 0 ) { + returnButtonFrameNum = 0; + return; + } + } + + //always be semi-transparent + if ( f > 0.5f ) + f = 0.5f; + + pfglColor4f( 1, 1, 1, f ); + if (iphoneDrawPicWithTouch( 240-32, 32, 64, 48, "iphone/button_back.tga")) {//int x, int y, int w, int h, const char *pic )) { + menuState = IPM_MAPS; + returnButtonFrameNum = 0; + + //if it's a spear map, it needs special attention + if (currentMap.episode > 5 && currentMap.episode < 10) + { + //get the level number + int levelNum = currentMap.episode*10 + currentMap.map; + + if (levelNum == 78 || (levelNum >= 60 && levelNum < 65)) { + episode->value = 6; + } + else if (levelNum == 79 || (levelNum >= 65 && levelNum < 70)) { + episode->value = 7; + } + else if (levelNum >= 70 && levelNum < 76) { + episode->value = 8; + } + else if (levelNum == 76 || levelNum == 77 || levelNum == 80) { + episode->value = 9; + } + } + } + pfglColor4f( 1, 1, 1, 1 ); + + --returnButtonFrameNum; +} + void iphoneStartBonusFlash() { bonusFrameNum = iphoneFrameNum; } @@ -936,14 +1481,14 @@ void iphoneSetAttackDirection( int dir ) { attackDirTime[1] = iphoneFrameNum; } } - +//gsh... note to self: this is where the controls are drawn void iphoneDrawHudControl( hudPic_t *hud ) { if ( hud->hudFlags & HF_DISABLED ) { return; } iphoneHighlightPicNumWhenTouched( hud->x, hud->y, hud->width, hud->height, hud->glTexNum ); } - +//gsh... note to self: this is where menu/map buttons are drawn int iphoneDrawHudButton( hudPic_t *hud ) { if ( hud->hudFlags & HF_DISABLED ) { return 0; @@ -1007,7 +1552,7 @@ iphoneFrame ================== */ void iphoneFrame() { - unsigned char blendColor[4]; + unsigned char blendColor[4] = { 0, 0, 0, 0 }; iphoneFrameNum++; loggedTimes[iphoneFrameNum&(MAX_LOGGED_TIMES-1)].enterFrame = Sys_Milliseconds(); @@ -1052,6 +1597,12 @@ void iphoneFrame() { //------------------ // normal gameplay //------------------ + + //this is a hack for "Floor 18, Part II: Death's Door" + //there's no gold key to leave the first room + //so we give it to the player here... gsh + if (currentMap.episode == 8 && !(Player.items & ITEM_KEY_1)) + Player.items |= ITEM_KEY_1; if( Player.playstate != ex_dead ) { @@ -1125,16 +1676,19 @@ void iphoneFrame() { blendColor[0] = 255; blendColor[1] = 0; blendColor[2] = 0; - blendColor[3] = deathFadeIntensity; + blendColor[3] = deathFadeIntensity; deathFadeIntensity += 2; if( deathFadeIntensity >= 240 ) { deathFadeIntensity = 0; PL_NewGame( &Player ); - iphoneStartMap( currentMap.episode, currentMap.map, currentMap.skill ); + if (currentMap.episode >=9) //gsh + iphoneStartUserMap( currentMap.episode, currentMap.map, currentMap.skill, NULL ); + else + iphoneStartMap( currentMap.episode, currentMap.map, currentMap.skill ); } } else { iphoneDrawWeapon(); - if( damageflash ) { + if( damageflash ) { blendColor[0] = 255; blendColor[1] = 0; blendColor[2] = 0; @@ -1160,6 +1714,9 @@ void iphoneFrame() { } iphoneDrawNotifyText(); + + //gsh + iphoneDrawReturnButton(); iphoneDrawMapView(); @@ -1180,7 +1737,7 @@ void iphoneFrame() { if ( iphoneDrawHudButton( &huds.map ) ) { iphoneOpenAutomap(); } - + Client_Screen_DrawConsole(); ShowTilt(); // debug tool @@ -1189,4 +1746,20 @@ void iphoneFrame() { iphoneSavePrevTouches(); SysIPhoneSwapBuffers(); // do the swapbuffers + } + +void iphoneDrawLoading() +{ + Com_Printf("Draw Loading!\n"); +// unsigned char blendColor[4]; + + iphoneSet2D(); + + //draw stuff here + iphoneDrawText(100, 100, 16, 16, "Drawing Loading!");//, <#int y#>, <#int width#>, <#int height#>, <#const char * str#>) + + SysIPhoneSwapBuffers(); // do the swapbuffers +} + + diff --git a/wolf3d/code/iphone/iphone_main.c b/wolf3d/code/iphone/iphone_main.c index e4e7461..b9c6991 100644 --- a/wolf3d/code/iphone/iphone_main.c +++ b/wolf3d/code/iphone/iphone_main.c @@ -29,7 +29,6 @@ #include "../wolfiphone.h" - cvar_t *controlScheme; cvar_t *sensitivity; cvar_t *stickTurnBase; @@ -42,6 +41,13 @@ cvar_t *tiltMove; cvar_t *tiltDeadBand; cvar_t *tiltAverages; cvar_t *tiltFire; +#ifdef VOLUMEHACK +cvar_t *volumeFireUp; //gsh +cvar_t *volumeFireDown; //gsh +cvar_t *volumeFireUpSetting; //gsh +cvar_t *volumeFireDownSetting; //gsh +#endif +cvar_t *hudAlpha; //gsh cvar_t *music; cvar_t *showTilt; cvar_t *showTime; @@ -56,6 +62,10 @@ cvar_t *autoFire; W32 sys_frame_time; +#ifdef STOREKIT +extern void CheckForStorekitExistence(); //gsh +#endif + void Sys_Error( const char *format, ... ) { va_list argptr; @@ -101,7 +111,12 @@ void Reset_f() { void iphoneStartup() { char *s; int start = Sys_Milliseconds(); - + +#ifdef STOREKIT + //check for storekit framework and set appropriate flags... gsh + CheckForStorekitExistence(); +#endif + // temporary const char *systemVersion = SysIPhoneGetOSVersion(); printf( "systemVersion = %s\n", systemVersion ); @@ -119,6 +134,7 @@ void iphoneStartup() { Con_Init(); FS_InitFilesystem(); + // We need to add the early commands twice, because // a basedir or cddir needs to be set before execing // config files, but we want other parms to override @@ -137,9 +153,7 @@ void iphoneStartup() { Cvar_Get( "version", s, CVAR_SERVERINFO | CVAR_NOSET ); Con_Init(); - Sound_Init(); - Game_Init(); // game and player init Cbuf_AddText( "exec config.cfg\n" ); @@ -165,6 +179,13 @@ void iphoneStartup() { tiltTurn = Cvar_Get( "tiltTurn", "0", CVAR_ARCHIVE ); tiltMove = Cvar_Get( "tiltMove", "0", CVAR_ARCHIVE ); tiltFire = Cvar_Get( "tiltFire", "0", CVAR_ARCHIVE ); +#ifdef VOLUMEHACK + volumeFireUp = Cvar_Get( "volumeFireUp", "0", 0 ); //gsh + volumeFireDown = Cvar_Get( "volumeFireDown", "0", 0 ); //gsh + volumeFireUpSetting = Cvar_Get( "volumeFireUpSetting", "1", CVAR_ARCHIVE ); //gsh // 1 = primary + volumeFireDownSetting = Cvar_Get( "volumeFireDownSetting", "0", CVAR_ARCHIVE ); //gsh // 0 = secondary +#endif + hudAlpha = Cvar_Get("hudAlpha", "1.0", CVAR_ARCHIVE); //gsh music = Cvar_Get( "music", "1", CVAR_ARCHIVE ); tiltDeadBand = Cvar_Get( "tiltDeadBand", "0.08", CVAR_ARCHIVE ); tiltAverages = Cvar_Get( "tiltAverages", "3", CVAR_ARCHIVE ); @@ -181,7 +202,8 @@ void iphoneStartup() { // make sure volume changes and incoming calls draw the right orientation SysIPhoneSetUIKitOrientation( revLand->value ); - + + /* //if we don't preload the ogg files then we have faster start ups... gsh // preload all the ogg FM synth sounds Com_Printf( "before ogg preload: %i msec\n", Sys_Milliseconds() - start ); @@ -210,7 +232,7 @@ void iphoneStartup() { Sound_RegisterSound( "lsfx/078.wav" ); Sound_RegisterSound( "lsfx/080.wav" ); Sound_RegisterSound( "lsfx/085.wav" ); - Sound_RegisterSound( "lsfx/086.wav" ); + Sound_RegisterSound( "lsfx/086.wav" );*/ // these should get overwritten by LoadTheGame memset( ¤tMap, 0, sizeof( currentMap ) ); @@ -218,12 +240,13 @@ void iphoneStartup() { currentMap.episode = 0; HudSetForScheme( 0 ); + Com_Printf( "before LoadTheGame: %i msec\n", Sys_Milliseconds() - start ); - + if ( !LoadTheGame() ) { PL_NewGame( &Player ); iphoneStartMap( currentMap.episode, currentMap.map, currentMap.skill ); - } + } // always start at main menu @@ -236,7 +259,7 @@ void iphoneStartup() { =================== iphonePreloadBeforePlay - This couold all be done at startup, but moving a bit of the delay + This could all be done at startup, but moving a bit of the delay to after pressing the resume button works a little better. =================== */ diff --git a/wolf3d/code/iphone/iphone_mapselector.c b/wolf3d/code/iphone/iphone_mapselector.c new file mode 100644 index 0000000..17bd86d --- /dev/null +++ b/wolf3d/code/iphone/iphone_mapselector.c @@ -0,0 +1,478 @@ +/* + * iphone_mapselector.c + * wolf3d + * + * Created by Greg Hodges on 7/2/09. + * Copyright 2009 id software. All rights reserved. + * + */ +#include "../wolfiphone.h" +#include + +const char levelNames[][29] = { +"E1 level 1", +"E1 level 2", +"E1 level 3", +#ifndef LITE +"E1 level 4", +"E1 level 5", +"E1 level 6", +"E1 level 7", +"E1 level 8", +"E1 Boss ", +"E1 Secret ", +"E2 level 1", +"E2 level 2", +"E2 level 3", +"E2 level 4", +"E2 level 5", +"E2 level 6", +"E2 level 7", +"E2 level 8", +"E2 Boss ", +"E2 Secret ", +"E3 level 1", +"E3 level 2", +"E3 level 3", +"E3 level 4", +"E3 level 5", +"E3 level 6", +"E3 level 7", +"E3 level 8", +"E3 Boss ", +"E3 Secret ", +"E4 level 1", +"E4 level 2", +"E4 level 3", +"E4 level 4", +"E4 level 5", +"E4 level 6", +"E4 level 7", +"E4 level 8", +"E4 Boss ", +"E4 Secret ", +"E5 level 1", +"E5 level 2", +"E5 level 3", +"E5 level 4", +"E5 level 5", +"E5 level 6", +"E5 level 7", +"E5 level 8", +"E5 Boss ", +"E5 Secret ", +"E5 level 1", +"E6 level 2", +"E6 level 3", +"E6 level 4", +"E6 level 5", +"E6 level 6", +"E6 level 7", +"E6 level 8", +"E6 Boss ", +"E6 Secret ", +"Floor 1: Tunnels 1 ", +"Floor 2: Tunnels 2 ", +"Floor 3: Tunnels 3 ", +"Floor 4: Tunnels 4 ", +"Floor 5: Tunnels Boss ", +"Floor 6: Dungeons 1 ", +"Floor 7: Dungeons 2 ", +"Floor 8: Dungeons 3 ", +"Floor 9: Dungeons 4 ", +"Floor 10: Dungeons Boss ", +"Floor 11: Castle 1 ", +"Floor 12: Castle 2 ", +"Floor 13: Castle 3 ", +"Floor 14: Castle 4 ", +"Floor 15: Castle 5 ", +"Floor 16: Castle Boss ", +"Floor 17: Ramparts ", +"Floor 18: Death Knight ", +"Floor 19: Secret 1 ", +"Floor 20: Secret 2 ", +"Floor 21: Angel of Death", +#endif +}; + +int dragVelocity = 0; //velocity for the scrolling maps +int dragPosition = 32; //position for the scrolling maps + +extern int BackButton(); +#ifdef SPEARSTOREKIT +extern void GetSpearOfDestiny( int x, int y ); +#endif +extern int iphoneDrawPicWithTouchAndColor( int x, int y, int w, int h, const char *pic, colour4_t color ); +extern void iphoneStartUserMap( int episodeNum, int mapNum, int skillLevel, char *mapName ); + + +#ifdef LITE +extern void GetMoreLevels( int x, int y ); +#endif + +//========================================== +// iphoneUpdateDrags() +// Updates the touches for the scrolling maps +//========================================== +void iphoneUpdateDrags(int numMaps, int skillSides, int height, int spacing, int numUserMaps) +{ + //Update drag/touch info + int x = 64; + int w = 480 - skillSides - 64; + int y = 0; + int h = 320; + + if (TouchDown(x, y, w, h)) + { + if (!numPrevTouches) + prevTouches[0][1] = touches[0][1]; + else if (numTouches == 1) + dragVelocity = prevTouches[0][1] - touches[0][1]; + } + + //boundary check the drags + int num = numMaps+1+6; + if (g_version-> value == SPEAROFDESTINY) + num = numMaps + 7; + + num += numUserMaps + 1; + + if (dragPosition < 320 - (height+spacing)*(num) - 20) + dragPosition < 320 - (height+spacing)*(num) - 20; + else if (dragPosition > 40) + dragPosition = 40; + + if (dragPosition < 320 - (height+spacing)*(num)) + dragVelocity = -1; + else if (dragPosition > 32) + dragVelocity = 1; + + //update the drags + dragPosition -= dragVelocity; + + //retard the velocity + if (dragVelocity > 0) + --dragVelocity; + else if (dragVelocity < 0) + ++dragVelocity; + +} + +//========================================== +// iphoneDrawRewards() +// Draws the rewards on each level +//========================================== +int forceDrawRewards = 0; +void iphoneDrawRewards(int m, int s, int x, int y) +{ + int ch = currentMap.mapFlags[s][m]; + // bit1 = attempted + // bit2 = completed + // bit3 = 100% kills + // bit4 = 100% secrets + // bit5 = 100% treasure + + //ch = 4+8+16+32; //used for testing proper placement + + x += 80; + int startX = x; + + //x += 66;//375; + y -= 27;//10; + + int width = 40;//22; + int height = 40;//22; + int spacing = -12; + // draw award shadows + iphoneDrawPic( x,y+23, width, height, "iphone/kills_shadow.tga" ); x += width + spacing; + iphoneDrawPic( x,y+23, width, height, "iphone/secrets_shadow.tga" ); x += width + spacing; + iphoneDrawPic( x,y+23, width, height, "iphone/treasure_shadow.tga" ); x += width + spacing; + iphoneDrawPic( x,y+23, width, height, "iphone/partime_shadow.tga" ); //x += width + 5; + + x = startX; + + // draw awards + if ( (ch & MF_KILLS) || forceDrawRewards) { + iphoneDrawPic( x,y+23, width, height, "iphone/kills.tga" ); + } + x += width + spacing; + if ( (ch & MF_SECRETS) || forceDrawRewards) { + iphoneDrawPic( x,y+23, width, height, "iphone/secrets.tga" ); + } + x += width + spacing; + if ( (ch & MF_TREASURE) || forceDrawRewards) { + iphoneDrawPic( x,y+23, width, height, "iphone/treasure.tga" ); + } + x += width + spacing; + if ( (ch & MF_TIME) || forceDrawRewards) { + iphoneDrawPic( x,y+23, width, height, "iphone/partime.tga" ); + } +} + +//========================================== +// iphoneDrawUserMaps() +// This function scans through the custom maps +// and displays the ones on screen +//========================================== +int iphoneDrawUserMaps(int Yoffset, int height, int spacing, int skillValue) +{ + int numMaps = 0; + + DIR *dp; + struct dirent *ep; + char mapBuffer[1024]; + + int length = strlen(iphoneDocDirectory); + strcpy(mapBuffer, iphoneDocDirectory); + strcpy(mapBuffer + length, "/usermaps/"); + + dp = opendir (mapBuffer); + + if (dp != NULL) + { + int y = Yoffset; + if (g_version->value != SPEAROFDESTINY) //make space for the purchase SOD button + y += height + spacing + spacing; + + //draw the custom map spacing + y += height + spacing; + iphoneDrawPic( 0, y, 480-80, height, "iphone/button_epSOD.tga"); + + + while (ep = readdir (dp)) + { + ++numMaps; + if (numMaps < 3) //skip the '.' and '..' + continue; + + y += height + spacing; + Com_Printf("value y: %i\n", y); + + //TODO: draw the names! + if ( y >= -height && y < 320 ) + { + + //draw maps and touch + int alpha = 128; + //colour4_t colorSecret = { 80, 0, 0, alpha }; + colour4_t colorNoTry = { 0, 0, 0, alpha }; + + if ( iphoneDrawPicWithTouchAndColor( 0, y, 480-80, height, "iphone/menu_bar.tga", colorNoTry) && dragVelocity == 0 )//&& isSecretAvailable ) + { + //reset the player to new + PL_NewGame( &Player ); + + //set episode and map numbers and load map + int e = 9; //9 will be used to show a custom map + Cvar_SetValue( episode->name, e ); + iphonePreloadBeforePlay(); //This prevents crashes when loading SOD maps + iphoneStartUserMap( e, numMaps - 3, skillValue, ep->d_name ); + } + + iphoneDrawText( 100, y + 16, 16, 16, ep->d_name ); + //iphoneCenterText( 160, y + 16, ep->d_name ); + + //draw the rewards + iphoneDrawRewards(numMaps-2+90, skillValue, 0, y); + } + } + closedir (dp); + + --numMaps; + --numMaps; + } + + Com_Printf("numMaps: %i\n", numMaps); + + return numMaps; +} + +//========================================== +// iphoneSelectMapMenu() +// A doom classic style map selector +//========================================== +void iphoneSelectMapMenu() +{ + int s; //skill + char str[64]; + + // highlight the current skill selection + int skillValue = (int)skill->value; + if (skillValue < 0) + skillValue = 0; + if (skillValue > 3) + skillValue = 3; + + int skillSides = 80; + + int e = 0; //episode + + int m, map; //mission/map + int height = 48; + int spacing = 10; + int y = 0; + + char strMission[80]; + strMission[79] = '\0'; + +#ifdef LITE + int numMaps = 3;//5; +#else + int numMaps = 60; + if (g_version->value == SPEAROFDESTINY) + numMaps = 60 + 21; +#endif + + iphoneDrawPic(0, 0, 480, 320, "iphone/submenus_background_image.tga"); + + //check for BackButton touches + if ( BackButton() ) { + menuState = IPM_MAIN; + return; + } + + //we want to use the menu selector bar + my_snprintf( str, sizeof( str ), "iphone/menu_bar.tga" ); + + //iterate through the maps... drawing only the visible maps + for (m = 0; m < numMaps; m++) + { + + //Draws the spacing between each episode + e = m/10; + if (e < 6) + { + y = dragPosition + ((height + spacing) * e*11);// + ((height + spacing) * (e+1)); + if ( y >= -height && y < 320 ) + { + my_snprintf( str, sizeof( str ), "iphone/button_ep%i.tga", e+1 ); + iphoneDrawPic( 0, y, 480-80, height, str); + } + + y = dragPosition + ((height + spacing) * m) + ((height + spacing) * (e+1)); + } + else + { + y = dragPosition + ((height + spacing) * 6*11); + if ( y >= -height && y < 320 ) + { + my_snprintf( str, sizeof( str ), "iphone/button_epSOD.tga"); + iphoneDrawPic( 0, y, 480-80, height, str); + } + y = dragPosition + ((height + spacing) * m) + ((height + spacing) * 7); + } + + //we want to use the menu selector bar + my_snprintf( str, sizeof( str ), "iphone/menu_bar.tga" ); + + //only draw the maps that are currently viewable + if ( y >= -height && y < 320 ) + { + e = m/10; + map = m%10; + + //color maps + int ch = currentMap.mapFlags[skillValue][m]; + + int alpha = 128; + colour4_t colorSecret = { 80, 0, 0, alpha }; + colour4_t colorNoTry = { 0, 0, 0, alpha }; + colour4_t colorTried = { 80, 80, 0, alpha }; + colour4_t colorCompleted = { 0, 80, 0, alpha }; + colour4_t colorBlink = { 80, 80, 80, alpha }; + + unsigned char *color = colorNoTry; + bool isSecretAvailable = true; + + if ( map == 9 && !( ch & MF_TRIED ) && e < 6) //if it's the 9th and not SOD + { + color = colorSecret; + isSecretAvailable = false; + } + else if ( (m == 79 || m == 78) && !(ch & MF_TRIED) ) //if it's the secret SOD levels + { + color = colorSecret; + isSecretAvailable = false; + } else if ( ch & MF_COMPLETED ) { + color = colorCompleted; + } else if ( ch & MF_TRIED ) { + color = colorTried; + } else { + color = colorNoTry; + } + + //blink the level you're currently on + if ( ( iphoneFrameNum & 8 ) && map == currentMap.map && e == currentMap.episode ) + color = colorBlink; + + //draw maps and touch + if ( iphoneDrawPicWithTouchAndColor( 0, y, 480-80, height, str, color) && dragVelocity == 0 && isSecretAvailable ) + { + //reset the player to new + PL_NewGame( &Player ); + + //set episode and map numbers and load map + Cvar_SetValue( episode->name, e ); + iphonePreloadBeforePlay(); //This prevents crashes when loading SOD maps + iphoneStartMap( e, map, skillValue ); + } + + //draw the rewards + iphoneDrawRewards(m, skillValue, 0, y); + + //draw the episode and map over the selection bar + my_snprintf( strMission, sizeof( strMission ), "%s", levelNames[m] ); + iphoneDrawText( 100, y + 16, 16, 16, strMission ); +// iphoneCenterText( 160, y + 16, strMission ); + } + } + + int numUserMaps = 0; + +#ifdef LITE + //buy more episodes button + GetMoreLevels( 0, dragPosition + (height + spacing) * (m+1) ); +#else +#if 0 + //buy more episodes button + if (g_version->value != SPEAROFDESTINY) + GetSpearOfDestiny( 0, dragPosition + (height + spacing) * (m+6) ); +#endif + //TODO: Draw user maps + numUserMaps = iphoneDrawUserMaps(y, height, spacing, skillValue); +#endif + + //Update the scrolling drags + iphoneUpdateDrags(numMaps, skillSides, height, spacing, numUserMaps); + + + //Draw/choose the skills on the right side of the screen + for ( s = 0 ; s < 4 ; s++ ) + { + my_snprintf( str, sizeof( str ), "iphone/button_skill%i.tga", s+1 ); + + if ( s != (int)skill->value ) { + pfglColor3f( 0.5, 0.5, 0.5 ); + } + if ( iphoneDrawPicWithTouch( 480 - skillSides, skillSides*s, skillSides, skillSides, str ) ) { + Cvar_SetValue( skill->name, s ); + skillValue = s; + break; + } + pfglColor3f( 1, 1, 1 ); + } + + + + //Draw a bar that covers empty space + iphoneDrawPic(0, 0, 480-80, 32, "iphone/menu_bar.tga"); + + //levels header + iphoneDrawPic(240-64, 0, 128, 32, "iphone/header_levels.tga"); + + BackButton(); //this is kinda cheap... + //we want the back button to render on top + //but we also want it to check for touches before the maps + //so we call this function twice + //(at the beginning for touches; the end for rendering) +} diff --git a/wolf3d/code/iphone/iphone_menus.c b/wolf3d/code/iphone/iphone_menus.c index daf99fa..b47f789 100644 --- a/wolf3d/code/iphone/iphone_menus.c +++ b/wolf3d/code/iphone/iphone_menus.c @@ -17,6 +17,8 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +//gsh +//#define NUM_TOUCHFRAME 10 #include "../wolfiphone.h" @@ -27,6 +29,83 @@ menuState_t menuState; colour4_t highlightColor = { 128, 128, 128, 255 }; colour4_t colorPressed = { 128, 128, 0, 255 }; +//------------- +// These are for the download progress menu +extern unsigned int dataAmount; //gsh... provides us information with the amount of data we've downloaded +unsigned int totalDownloaded = 0; +extern const unsigned int DownloadFileSize; +extern unsigned int userDataAmount; //gsh... provides us information with the amount of data we've downloaded +extern const unsigned int DownloadUserFileSize; +//-------------- +//gsh +int wasDLInstructionsCalledFromEpisodeMenu = 0; +int wasCalledFromDownloadInstructionsMenu = 0; +/* + ========================= + iphoneSetLevelNotifyText + gsh + ========================= + */ +void iphoneSetLevelNotifyText() +{ + char str[128]; + + if (currentMap.episode < 6) + sprintf( str, "Entering level E%iM%i", currentMap.episode+1, currentMap.map+1 ); + else if (currentMap.episode < 10) { + int currentLevel = currentMap.episode * 10 + currentMap.map; + switch (currentLevel) { + case 60: case 61: case 62: case 63: case 64: + sprintf( str, "Entering Tunnels %i", currentLevel-60+1); + break; + case 78: + sprintf( str, "Entering Tunnels Secret");//, 6); + break; + case 65: case 66: case 67: case 68: case 69: + sprintf( str, "Entering Dungeons %i", currentLevel-65+1); + break; + case 70: case 71: case 72: case 73: case 74: case 75: + sprintf( str, "Entering Castle %i", currentLevel - 70 + 1); + break; + case 79: + sprintf( str, "Entering Castle Secret");//, 6); + break; + case 76: + sprintf( str, "Entering Ramparts"); + break; + case 77: + sprintf( str, "Entering Death Knight"); + break; + case 80: + sprintf( str, "Entering Dungeon Dimension"); + break; + default: + sprintf( str, " "); + break; + } + } + else + sprintf( str, "Entering level custom %i", /*currentMap.episode+1,*/ currentMap.map+1 ); + + iphoneSetNotifyText( str ); +} + +/* + ================== + RectMake + gsh + ================== + */ +rect_t RectMake(int x, int y, int width, int height) +{ + rect_t rect; + rect.x = x; + rect.y = y; + rect.width = width; + rect.height = height; + return rect; +} + /* ================== iphoneDrawPicNum @@ -71,6 +150,72 @@ void iphoneDrawPic( int x, int y, int w, int h, const char *pic ) { pfglEnd(); } +//gsh +void iphoneDrawPicInBox( rect_t drawRect, const char *pic, rect_t boxRect ) +{ + texture_t *gl; + + gl = TM_FindTexture( pic, TT_Pic ); + if( ! gl ) { + Com_Printf( "Can't find pic: %s\n", pic ); + return; + } + + + int newX = drawRect.x; + int newY = drawRect.y; + int newW = drawRect.width; + int newH = drawRect.height; + + float u0 = 0; + float u1 = gl->maxS; + float v0 = 0; + float v1 = gl->maxT; + + + if (drawRect.x > boxRect.x + boxRect.width) + return; //we're not in box... //newX = boxX + boxW; + if (drawRect.x + drawRect.width < boxRect.x) + return; + if (drawRect.y > boxRect.y + boxRect.height) + return; + if (drawRect.y + drawRect.height < boxRect.y) + return; + + if (drawRect.x < boxRect.x) { + newX = boxRect.x; + u0 = gl->maxS * ((float)(boxRect.x - drawRect.x))/((float)drawRect.width); + newW = drawRect.width - (boxRect.x - drawRect.x); + } + if (drawRect.x + drawRect.width > boxRect.x + boxRect.width) { + newW = boxRect.x + boxRect.width - drawRect.x; + u1 = gl->maxS * ((float)(newW))/((float)drawRect.width); + } + if (drawRect.y < boxRect.y) { + newY = boxRect.y; + v0 = gl->maxT * ((float)(boxRect.y - drawRect.y))/((float)drawRect.height); + newH = drawRect.height - (boxRect.y - drawRect.y); + } + if (drawRect.y + drawRect.height > boxRect.y + boxRect.height) { + newH = boxRect.y + boxRect.height - drawRect.y; + v1 = gl->maxT * ((float)(newH))/((float)drawRect.height); + } + + + R_Bind( gl->texnum ); + pfglBegin( GL_QUADS ); + + //float u, v; + //u = ((float)newX)/((float)x); + //v = ((float)newY)/((float)y); + pfglTexCoord2f( u0, v0 ); pfglVertex2i( newX, newY ); + pfglTexCoord2f( u1, v0 ); pfglVertex2i( newX+newW, newY ); + pfglTexCoord2f( u1, v1 ); pfglVertex2i( newX+newW, newY+newH ); + pfglTexCoord2f( u0, v1 ); pfglVertex2i( newX, newY+newH ); + + pfglEnd(); +} + /* ================== iphoneDrawPicWithTouch @@ -93,6 +238,243 @@ int iphoneDrawPicWithTouch( int x, int y, int w, int h, const char *pic ) { return r; } +//gsh +int iphoneDrawPicWithTouchSpecified( rect_t drawRect, rect_t touchRect, const char *pic) {//int x, int y, int w, int h, int touchX, int touchY, int touchW, int touchH, const char *pic ) { + int r = TouchReleased( touchRect.x, touchRect.y, touchRect.width, touchRect.height ); + + if ( r ) { + // make sure it is full intensity if it is touch-released, even if + // it wasn't active previously + pfglColor3f( 1, 1, 1 ); + } + iphoneDrawPic( drawRect.x, drawRect.y, drawRect.width, drawRect.height, pic ); + if ( TouchDown( touchRect.x, touchRect.y, touchRect.width, touchRect.height ) ) { + colour4_t color = { 255, 255, 255, 64 }; + R_Draw_Blend( drawRect.x, drawRect.y, drawRect.width, drawRect.height, color ); + } + return r; +} +//gsh +int iphoneDrawPicInBoxWithTouchSpecified( rect_t drawRect, rect_t touchRect, const char *pic, rect_t boundingRect ) { + + //make sure touches are in drawing bounds + if (touchRect.x < boundingRect.x) { + touchRect.width -= boundingRect.x - touchRect.x; + touchRect.x = boundingRect.x; + } + if (touchRect.x + touchRect.width > boundingRect.x + boundingRect.width) + touchRect.width = boundingRect.x + boundingRect.width - touchRect.x; + if (touchRect.y < boundingRect.y) { + touchRect.height -= boundingRect.y - touchRect.y; + touchRect.y = boundingRect.y; + } + if (touchRect.y + touchRect.height > boundingRect.y + boundingRect.height) + touchRect.height = boundingRect.y + boundingRect.height - touchRect.y; + + int r = TouchReleased( touchRect.x, touchRect.y, touchRect.width, touchRect.height ); + + if ( r ) { + // make sure it is full intensity if it is touch-released, even if + // it wasn't active previously + pfglColor3f( 1, 1, 1 ); + } +// iphoneDrawPic( x, y, w, h, pic ); +// iphoneDrawPicInBox( x, y, w, h, pic, boxX, boxY, boxW, boxH ); + iphoneDrawPicInBox( drawRect, pic, boundingRect ); +// iphoneDrawPicInBox( int x, int y, int w, int h, const char *pic, int boxX, int boxY, int boxW, int boxH ) + if ( TouchDown( touchRect.x, touchRect.y, touchRect.width, touchRect.height ) ) { + colour4_t color = { 255, 255, 255, 64 }; + R_Draw_Blend( touchRect.x, touchRect.y, touchRect.width, touchRect.height, color ); + } + return r; +} + +//gsh +int iphoneDrawPicInBoxWithTouchAndVelocity( rect_t drawRect, rect_t touchRect, const char *pic, rect_t boundingRect, int dragVelocity ) { + + //make sure touches are in drawing bounds + if (touchRect.x < boundingRect.x) { + touchRect.width -= boundingRect.x - touchRect.x; + touchRect.x = boundingRect.x; + } + if (touchRect.x + touchRect.width > boundingRect.x + boundingRect.width) + touchRect.width = boundingRect.x + boundingRect.width - touchRect.x; + if (touchRect.y < boundingRect.y) { + touchRect.height -= boundingRect.y - touchRect.y; + touchRect.y = boundingRect.y; + } + if (touchRect.y + touchRect.height > boundingRect.y + boundingRect.height) + touchRect.height = boundingRect.y + boundingRect.height - touchRect.y; + + int r = TouchReleased( touchRect.x, touchRect.y, touchRect.width, touchRect.height ); + + //if the object is moving, then it shouldn't be selectable + if ( dragVelocity ) + r = 0; + + if ( r ) { + // make sure it is full intensity if it is touch-released, even if + // it wasn't active previously + pfglColor3f( 1, 1, 1 ); + } + // iphoneDrawPic( x, y, w, h, pic ); + // iphoneDrawPicInBox( x, y, w, h, pic, boxX, boxY, boxW, boxH ); + iphoneDrawPicInBox( drawRect, pic, boundingRect ); + // iphoneDrawPicInBox( int x, int y, int w, int h, const char *pic, int boxX, int boxY, int boxW, int boxH ) + if ( TouchDown( touchRect.x, touchRect.y, touchRect.width, touchRect.height ) && !dragVelocity) { + colour4_t color = { 255, 255, 255, 64 }; + R_Draw_Blend( touchRect.x, touchRect.y, touchRect.width, touchRect.height, color ); + } + return r; +} + + +//gsh +int iphoneDrawPicInBoxWithTouchSpecifiedAndColor( rect_t drawRect, rect_t touchRect, const char *pic, rect_t boundingRect, colour4_t color ) { + + //make sure touches are in drawing bounds + if (touchRect.x < boundingRect.x) { + touchRect.width -= boundingRect.x - touchRect.x; + touchRect.x = boundingRect.x; + } + if (touchRect.x + touchRect.width > boundingRect.x + boundingRect.width) + touchRect.width = boundingRect.x + boundingRect.width - touchRect.x; + if (touchRect.y < boundingRect.y) { + touchRect.height -= boundingRect.y - touchRect.y; + touchRect.y = boundingRect.y; + } + if (touchRect.y + touchRect.height > boundingRect.y + boundingRect.height) + touchRect.height = boundingRect.y + boundingRect.height - touchRect.y; + + int r = TouchReleased( touchRect.x, touchRect.y, touchRect.width, touchRect.height ); + + if ( r ) { + // make sure it is full intensity if it is touch-released, even if + // it wasn't active previously + pfglColor3f( 1, 1, 1 ); + } + // iphoneDrawPic( x, y, w, h, pic ); + // iphoneDrawPicInBox( x, y, w, h, pic, boxX, boxY, boxW, boxH ); + iphoneDrawPicInBox( drawRect, pic, boundingRect ); + R_Draw_Blend( touchRect.x, touchRect.y, touchRect.width, touchRect.height, color ); + // iphoneDrawPicInBox( int x, int y, int w, int h, const char *pic, int boxX, int boxY, int boxW, int boxH ) + if ( TouchDown( touchRect.x, touchRect.y, touchRect.width, touchRect.height ) ) { + colour4_t colorWhite = { 255, 255, 255, 64 }; + R_Draw_Blend( touchRect.x, touchRect.y, touchRect.width, touchRect.height, colorWhite ); + } + return r; +} + +//gsh +int iphoneDrawPicInBoxWithTouchColorAndVelocity( rect_t drawRect, rect_t touchRect, const char *pic, rect_t boundingRect, colour4_t color, int dragVelocity ) { + + //make sure touches are in drawing bounds + if (touchRect.x < boundingRect.x) { + touchRect.width -= boundingRect.x - touchRect.x; + touchRect.x = boundingRect.x; + } + if (touchRect.x + touchRect.width > boundingRect.x + boundingRect.width) + touchRect.width = boundingRect.x + boundingRect.width - touchRect.x; + if (touchRect.y < boundingRect.y) { + touchRect.height -= boundingRect.y - touchRect.y; + touchRect.y = boundingRect.y; + } + if (touchRect.y + touchRect.height > boundingRect.y + boundingRect.height) + touchRect.height = boundingRect.y + boundingRect.height - touchRect.y; + + int r = TouchReleased( touchRect.x, touchRect.y, touchRect.width, touchRect.height ); + + if (dragVelocity) + r = 0; + + if ( r ) { + // make sure it is full intensity if it is touch-released, even if + // it wasn't active previously + pfglColor3f( 1, 1, 1 ); + } + // iphoneDrawPic( x, y, w, h, pic ); + // iphoneDrawPicInBox( x, y, w, h, pic, boxX, boxY, boxW, boxH ); + iphoneDrawPicInBox( drawRect, pic, boundingRect ); + R_Draw_Blend( touchRect.x, touchRect.y, touchRect.width, touchRect.height, color ); + // iphoneDrawPicInBox( int x, int y, int w, int h, const char *pic, int boxX, int boxY, int boxW, int boxH ) + if ( TouchDown( touchRect.x, touchRect.y, touchRect.width, touchRect.height ) && !dragVelocity ) { + colour4_t colorWhite = { 255, 255, 255, 64 }; + R_Draw_Blend( touchRect.x, touchRect.y, touchRect.width, touchRect.height, colorWhite ); + } + return r; +} + + + + +/* + ================== + iphoneDrawPicWithTouchAndColor + gsh + ================== + */ +int iphoneDrawPicWithTouchAndColor( int x, int y, int w, int h, const char *pic, colour4_t color ) { + int r = TouchReleased( x, y, w, h ); + + if ( r ) { + // make sure it is full intensity if it is touch-released, even if + // it wasn't active previously +// pfglColor3f( 1, 1, 1 ); + } +// glColor4f( 0.5f, 0.5f, 1, 0 ); + iphoneDrawPic( x, y, w, h, pic ); + R_Draw_Blend( x, y, w, h, color ); + if ( TouchDown( x, y, w, h ) ) { + colour4_t color = { 255, 255, 255, 64 }; + R_Draw_Blend( x, y, w, h, color ); + } +// glColor4f( 1, 1, 1, 1 ); + return r; +} + +/* + ================== + iphoneDrawPicInBoxWithTouchAndColor + gsh + ================== + */ +int iphoneDrawPicInBoxWithTouchAndColor( rect_t drawRect, const char *pic, colour4_t color, rect_t boxRect ) { + int r = TouchReleased(drawRect.x, drawRect.y, drawRect.width, drawRect.height) + & TouchReleased(boxRect.x, boxRect.y, boxRect.width, boxRect.height); //touch must be inside both boxes! + + if ( r ) { + // make sure it is full intensity if it is touch-released, even if + // it wasn't active previously + // pfglColor3f( 1, 1, 1 ); + } + + iphoneDrawPicInBox( drawRect, pic, boxRect ); + + if (drawRect.x + drawRect.width < boxRect.x) + return 0; + if (drawRect.x > boxRect.x + boxRect.width) + return 0; + if (drawRect.y + drawRect.height < boxRect.y) + return 0; + if (drawRect.y > boxRect.y + boxRect.height) + return 0; + + if (drawRect.x < boxRect.x) { drawRect.x = boxRect.x; } + if (drawRect.x + drawRect.width > boxRect.x + boxRect.width) { drawRect.width = boxRect.x + boxRect.width - drawRect.x; } + if (drawRect.y < boxRect.y) { drawRect.y = boxRect.y; } + if (drawRect.y + drawRect.height > boxRect.y + boxRect.height) { drawRect.height = boxRect.y + boxRect.height - drawRect.y; } + + R_Draw_Blend( drawRect.x, drawRect.y, drawRect.width, drawRect.height, color ); + if ( TouchDown( drawRect.x, drawRect.y, drawRect.width, drawRect.height ) ) { + colour4_t color = { 255, 255, 255, 64 }; + R_Draw_Blend( drawRect.x, drawRect.y, drawRect.width, drawRect.height, color ); + } + // glColor4f( 1, 1, 1, 1 ); + return r; +} + + + /* @@ -115,12 +497,12 @@ void iphoneSlider( int x, int y, int w, int h, const char *title, cvar_t *cvar, } // draw the background - iphoneDrawPic( x, y, w, h, "iphone/stat_bar_2.tga" ); + iphoneDrawPic( x, y, w, h, "iphone/slider_bar_underlay.tga");//stat_bar_2.tga" ); // draw the current range texture_t *gl; - gl = TM_FindTexture( "iphone/stat_bar_1.tga", TT_Pic ); + gl = TM_FindTexture( "iphone/slider_bar.tga", TT_Pic );//stat_bar_1.tga", TT_Pic ); assert( gl ); R_Bind( gl->texnum ); pfglBegin( GL_QUADS ); @@ -132,9 +514,24 @@ void iphoneSlider( int x, int y, int w, int h, const char *title, cvar_t *cvar, pfglEnd(); + //gsh... draw the finger catch + iphoneDrawPic( x+w*f-12, y, 24, h, "iphone/slider_knob.tga" );//finger_catch.tga" ); + + // draw the title and fraction sprintf( str, "%s : %i%%", title, (int)(f*100) ); - iphoneCenterText( x+ w/2, y+h/2-8, str ); + //colour4_t color = {0, 0, 0, 1}; + pfglColor4f(0, 0, 0, 1); //gsh +// y -= 3; + iphoneDrawArialText(x + w/6 + 1, y+h*8/10 + 1, 0.9f, str); + iphoneDrawArialText(x + w/6 + 2, y+h*8/10 + 2, 0.9f, str); + iphoneDrawArialText(x + w/6 + 3, y+h*8/10 + 3, 0.9f, str); + //iphoneCenterTextWithColor(x+ w/2 +1, y+h/2-8 +1, str, color); + //iphoneCenterTextWithColor(x+ w/2 +2, y+h/2-8 +2, str, color); + //iphoneCenterTextWithColor(x+ w/2 +3, y+h/2-8 +3, str, color); + //iphoneCenterText( x+ w/2, y+h/2-8, str ); + pfglColor4f(1, 1, 1, 1); + iphoneDrawArialText(x + w/6, y+h*8/10, 0.9f, str); // check for touches if ( numTouches > 0 && touches[0][0] >= x && touches[0][0] < x + w @@ -171,12 +568,44 @@ void iphoneSlider( int x, int y, int w, int h, const char *title, cvar_t *cvar, ================== */ int BackButton() { - if ( iphoneDrawPicWithTouch( 0, 0, 64, 32, "iphone/button_back.tga" ) ) { + if ( iphoneDrawPicWithTouch( 0, 0, 64, 36, "iphone/button_back.tga" ) ) { return 1; } return 0; } +//gsh... a back button on the other side of the screen +int BackButton2() { + if ( iphoneDrawPicWithTouch( 480-64, 0, 64, 36, "iphone/button_back.tga" ) ) { + return 1; + } + return 0; +} + +//gsh +int BackButton3( int x, int y ) { + if ( iphoneDrawPicWithTouch( x, y, 64, 36, "iphone/button_back.tga" ) ) { + return 1; + } + return 0; +} + +/* + ================== + MenuButton + gsh + ================== + */ +int MenuButton() { + if ( iphoneDrawPicWithTouch( 480-64, 0, 64, 36, "iphone/menu.tga" ) ) { +// if ( iphoneDrawPicWithTouch( 64, 0, 64, 36, "iphone/menu.tga" ) ) { +// if ( iphoneDrawPicWithTouch( 64, 0, 64, 36, "iphone/button_menu_yellow.tga" ) ) { + return 1; + } + return 0; +} + + void GetMoreLevels( int x, int y ) { if ( iphoneDrawPicWithTouch( x, y, 128, 64, "iphone/button_levels.tga" ) ) { // directly to the app store for more levels @@ -184,7 +613,58 @@ void GetMoreLevels( int x, int y ) { } } +//gsh +#if 0 +extern void DownloadSOD(); +extern void BeginStoreKit(); +extern void DisplayWrongVersionedOS(); +extern int TestURLConnection();//const char *url); +extern bool isStorekitAvailable; +//extern int IsSpearInstalled(); +//extern void iphoneActivateConsole(); +#endif +//================================= +// GetSpear +//gsh +//=================================== +#if 0 +void GetSpear(void) +{ + if (isStorekitAvailable) + { + if (TestURLConnection()) //check URL connection + { + /* //it's been decided to pre-package spear of destiny with wolf3d + //however users will need to in-app purchase it to unlock + //only download the data if doesn't exist, otherwise just enter the storekit + if (!IsSpearInstalled()) + DownloadSOD(); + else*/ + BeginStoreKit(); + } + } + else + DisplayWrongVersionedOS(); +} +#endif +//================================= +// GetSpearOfDestiny +//gsh +//=================================== +#if 0 +void GetSpearOfDestiny( int x, int y ) +{ + if ( iphoneDrawPicWithTouch( x, y, 128, 64, "iphone/button_levels.tga" ) ) + { + totalDownloaded = 0; //reset the totalDownload to zero + + Com_Printf("GetSpearOfDestiny called\n"); + + GetSpear(); + } +} +#endif /* ================== SaveTheGame @@ -265,6 +745,9 @@ int LoadTheGame() { return 0; } + + Com_Printf("episode: %i\nmap: %i\n", currentMap.episode, currentMap.map); + // load the huds fread( &huds, 1,sizeof(huds), f); @@ -273,7 +756,11 @@ int LoadTheGame() { PL_NewGame( &Player ); oldCompleted = currentMap.levelCompleted; - iphoneStartMap( currentMap.episode, currentMap.map, currentMap.skill ); + if (currentMap.episode < 9) //gsh... added the episode check + iphoneStartMap( currentMap.episode, currentMap.map, currentMap.skill ); + else + iphoneStartUserMap(currentMap.episode, currentMap.map, currentMap.skill, NULL); + currentMap.levelCompleted = oldCompleted; // load modifiactions on top @@ -303,6 +790,206 @@ int LoadTheGame() { return 1; } +/* + ================== + iphoneGetUserMapName + + This matches the incoming mapNumber with the correct + map name in the maps directory. Returns 1 if successful + and 0 if failed + gsh + ================== + */ +int iphoneGetUserMapName(int mapNumber, char *mapName) +{ + int numMaps = 0; + int returnValue = 0; + + DIR *dp; + struct dirent *ep; + char mapBuffer[1024]; + + int length = strlen(iphoneDocDirectory); + strcpy(mapBuffer, iphoneDocDirectory); + strcpy(mapBuffer + length, "/usermaps/"); + + dp = opendir (mapBuffer); + + if (dp != NULL) + { + while (ep = readdir (dp)) + {/* + //if you find a .DS_Store file... ignore it! + if ( strcmp(ep->d_name, ".DS_Store") == 0 ) + continue; + + if ( strcmp(ep->d_name, ".") == 0 ) + continue; + + if ( strcmp(ep->d_name, "..") == 0 ) + continue; + */ + ++numMaps; + if (numMaps < 3) //skip the '.' and '..' + continue; + + if (mapNumber == (numMaps - 3)) + { + strcpy(mapName, ep->d_name); + returnValue = 1; + break; + } + + } + //(void) closedir (dp); + closedir (dp); + } + + Com_Printf("numMaps: %i\n", numMaps); + + return returnValue; +} + +/* + ================== + iphoneGetUserMapName + + This matches the incoming mapNumber with the correct + map name in the maps directory. Returns 1 if successful + and 0 if failed + gsh + ================== + */ +int iphoneGetUserMapLevelByName(const char *mapName) +{ + int numMaps = 0; + int returnValue = 0; + + DIR *dp; + struct dirent *ep; + char mapBuffer[1024]; + + int length = strlen(iphoneDocDirectory); + strcpy(mapBuffer, iphoneDocDirectory); + strcpy(mapBuffer + length, "/usermaps/"); + + dp = opendir (mapBuffer); + + if (dp != NULL) + { + while (ep = readdir (dp)) + { + if (strcmp(ep->d_name, mapName) == 0) + { + return numMaps-3+1; + } + + /* + //if you find a .DS_Store file... ignore it! + if ( strcmp(ep->d_name, ".DS_Store") == 0 ) + continue; + + if ( strcmp(ep->d_name, ".") == 0 ) + continue; + + if ( strcmp(ep->d_name, "..") == 0 ) + continue; + */ + ++numMaps; + /* + if (numMaps < 3) //skip the '.' and '..' + continue; + + if (mapNumber == (numMaps - 3)) + { + strcpy(mapName, ep->d_name); + returnValue = 1; + break; + }*/ + + } + //(void) closedir (dp); + closedir (dp); + } + + Com_Printf("numMaps: %i\n", numMaps); + + return returnValue; +} + + +/* + ================== + iphoneStartUserMap + gsh + This does not reset the player, so call PL_NewGame( &Player ) if it is a new start. + ================== + */ +void iphoneStartUserMap( int episodeNum, int mapNum, int skillLevel, char *mapName ) { + char command[128]; + int levelNum = episodeNum*10+mapNum; + + Com_Printf( "iphoneStartMap( %i, %i, %i )\n", episodeNum, mapNum, skillLevel ); + + // get the sound playing + Sound_Update( vnull, vnull, vnull, vnull ); + + // clean up game feedback + damageflash = 0; + bonusFrameNum = 0; + attackDirTime[0] = 0; + attackDirTime[1] = 0; + + // note that this has been tried now + currentMap.mapFlags[currentMap.skill][levelNum] |= MF_TRIED; + + // start the game + currentMap.episode = episodeNum; + currentMap.map = mapNum; + currentMap.skill = skillLevel; + currentMap.levelCompleted = 0; + + Cvar_SetValue( skill->name, skillLevel ); + Cvar_SetValue( episode->name, episodeNum ); + + Com_Printf("episode: %i\nmap: %i\n", currentMap.episode, currentMap.map); + + if (episodeNum >= 10 && mapName) + { + sprintf( command, "usermaps/%s", mapName ); + Com_Printf("command: %s\n", command); + } + else if (episodeNum >= 10) //mapName was null... we must find what it's called + { + Com_Printf("episode number check true\n"); + mapName = (char *)malloc(sizeof(char)*1024); //sizeof(char) should just be 1 byte + if (iphoneGetUserMapName(mapNum, mapName)) + { + sprintf(command, "usermaps/%s", mapName); + } + else + { + Com_Printf("iphoneGetUserMapName failed"); + currentMap.episode = 0; + currentMap.map = 0; + sprintf( command, "w%i%i", currentMap.episode, currentMap.map ); //load first map if mapName not found + } + free(mapName); + } + else if (episodeNum >= 6) + { + FS_InitFilesystem(); //is this really needed? + sprintf( command, "maps/s%i%i", currentMap.episode - 6, currentMap.map); + } + else + { + sprintf( command, "w%i%i", currentMap.episode, currentMap.map ); + } + Com_Printf("command: %s\n", command); + Client_PrepRefresh( command ); + + menuState = IPM_GAME; +} /* ================== @@ -337,7 +1024,18 @@ void iphoneStartMap( int episodeNum, int mapNum, int skillLevel ) { Cvar_SetValue( skill->name, skillLevel ); Cvar_SetValue( episode->name, episodeNum ); - sprintf( command, "w%i%i", currentMap.episode, currentMap.map ); + + //added by gsh 6/9/2009... load the SOD maps if they were chosen + if (episodeNum >= 6) + { + // FS_InitFilesystem(); + sprintf( command, "maps/s%i%i", currentMap.episode - 6, currentMap.map); + } + else + { + sprintf( command, "w%i%i", currentMap.episode, currentMap.map ); + } + Com_Printf("command: %s\n", command); Client_PrepRefresh( command ); menuState = IPM_GAME; @@ -349,22 +1047,31 @@ void iphoneStartMap( int episodeNum, int mapNum, int skillLevel ) { ================== */ +extern int notifyFrameNum; //gsh void iphoneMainMenu() { char str[80]; float scale = 40 / 32.0; - iphoneDrawPic( 480-256, 0, 256, 128, "iphone/wolf_logo.tga" ); + //iphoneDrawPic( 480-256, 0, 256, 128, "iphone/wolf_logo.tga" ); + //iphoneDrawPic( 0, 0, 480, 320, "iphone/background_main.tga" ); //gsh + iphoneDrawPic(0, 0, 264, 250, "iphone/wolfplatinum_logo.tga"); + /*gsh #ifdef LITE iphoneDrawPic( -20, 0, 256, 64, "iphone/ep_1.tga" ); GetMoreLevels( 0, 96 ); #else iphoneDrawPic( -20, 0, 256, 64, "iphone/ep_1_6.tga" ); -#endif + if (g_version->value != SPEAROFDESTINY) //gsh + GetSpearOfDestiny( 0, 96 ); + else + iphoneDrawPic( 10, 100, 200, 100, "iphone/spear_logo.tga" ); //gsh +#endif*/ // we don't need the logo here now that we have a splash screen // iphoneDrawPic( 0, 320 - 128, 128, 128, "iphone/id_logo.tga" ); - if ( iphoneDrawPicWithTouch( 300 - 64*scale, 80, 128*scale, 64*scale, "iphone/button_resume.tga" ) ) { +// if ( iphoneDrawPicWithTouch( 300 - 64*scale, 80, 128*scale, 64*scale, "iphone/button_resume.tga" ) ) { + if ( iphoneDrawPicWithTouch( 360 - 64*scale, 20, 128*scale, 32*scale, "iphone/button_resume.tga" ) ) { //gsh iphonePreloadBeforePlay(); // if the game was saved at the intermission screen, immediately @@ -372,32 +1079,58 @@ void iphoneMainMenu() { if ( currentMap.levelCompleted ) { iphoneStartIntermission( 0 ); } else { + //reset the notification so that we can see which level we've loaded.. gsh + iphoneSetLevelNotifyText(); + iphoneFrameNum = 0; + notifyFrameNum = 60; menuState = IPM_GAME; } } - sprintf( str, "E%iM%i", currentMap.episode+1, currentMap.map+1 ); - iphoneCenterText( 300, 80+34*scale, str ); - if ( iphoneDrawPicWithTouch( 300 - 64*scale, 170, 128*scale, 32*scale, "iphone/button_control.tga" ) ) { + //gsh + if (currentMap.episode < 9) + sprintf( str, "E%iM%i", currentMap.episode+1, currentMap.map+1 ); + else + sprintf( str, "custom %i", currentMap.map+1 ); + +// iphoneCenterText( 300, 80+34*scale, str ); +// iphoneCenterText( 360, 34*scale, str ); //gsh + +// if ( iphoneDrawPicWithTouch( 300 - 64*scale, 170, 128*scale, 32*scale, "iphone/button_control.tga" ) ) { + if ( iphoneDrawPicWithTouch( 360 - 64*scale, 120, 128*scale, 32*scale, "iphone/button_settings.tga" ) ) { //gsh menuState = IPM_CONTROLS; } - if ( iphoneDrawPicWithTouch( 300 - 64*scale, 220, 128*scale, 32*scale, "iphone/button_new.tga" ) ) { - menuState = IPM_SKILL; +// if ( iphoneDrawPicWithTouch( 300 - 64*scale, 220, 128*scale, 32*scale, "iphone/button_new.tga" ) ) { + if ( iphoneDrawPicWithTouch( 360 - 64*scale, 70, 128*scale, 32*scale, "iphone/button_new.tga" ) ) { //gsh + menuState = IPM_SKILL;//IPM_MAPSELECTOR;//IPM_SKILL; //gsh } - if ( iphoneDrawPicWithTouch( 300 - 64*scale, 270, 128*scale, 32*scale, "iphone/button_web.tga" ) ) { - SysIPhoneOpenURL( "http://www.idsoftware.com/wolfenstein3dclassic/" ); +// if ( iphoneDrawPicWithTouch( 300 - 64*scale, 270, 128*scale, 32*scale, "iphone/button_web.tga" ) ) { + if ( iphoneDrawPicWithTouch( 100 - 64*scale, 250, 64, 64, "iphone/id_logo.tga" ) ) { //gsh + wasCalledFromDownloadInstructionsMenu = 0; + iphoneYesNoBox("Website", "This will navigate you to idsoftware.com. Continue?"); +// SysIPhoneOpenURL( "http://www.idsoftware.com/wolfenstein3dclassic/" ); + } + //gsh +// if (iphoneDrawPicWithTouch(100 - 64*scale, 270, 128*scale, 32*scale, "iphone/button_control.tga" ) ) { + if (iphoneDrawPicWithTouch(360 - 64*scale, 220, 128*scale, 32*scale, "iphone/button_more.tga" ) ) { //gsh + menuState = IPM_MORE;//IPM_TRIVIA; } + int x = 360; + int y = 170; if ( SysIPhoneOtherAudioIsPlaying() ) { - iphoneDrawPic( 480 - 64, 220, 64, 64, "iphone/music_off.tga" ); +// iphoneDrawPic( 480 - 64, 220, 64, 64, "iphone/music_off.tga" ); + iphoneDrawPic( x - 64*scale, y, 128*scale, 32*scale, "iphone/music_off.tga" ); //gsh } else { if ( music->value ) { - if ( iphoneDrawPicWithTouch( 480 - 64, 220, 64, 64, "iphone/music_on.tga" ) ) { + //if ( iphoneDrawPicWithTouch( 480 - 64, 220, 64, 64, "iphone/music_on.tga" ) ) { + if ( iphoneDrawPicWithTouch( x - 64*scale, y, 128*scale, 32*scale, "iphone/music_on.tga" ) ) { //gsh Cvar_SetValue( music->name, 0 ); Sound_StopBGTrack(); } } else { - if ( iphoneDrawPicWithTouch( 480 - 64, 220, 64, 64, "iphone/music_off.tga" ) ) { + //if ( iphoneDrawPicWithTouch( 480 - 64, 220, 64, 64, "iphone/music_off.tga" ) ) { + if ( iphoneDrawPicWithTouch( x - 64*scale, y, 128*scale, 32*scale, "iphone/music_off.tga" ) ) { //gsh Cvar_SetValue( music->name, 1 ); Sound_StartBGTrack( levelData.musicName, levelData.musicName ); } @@ -408,7 +1141,47 @@ void iphoneMainMenu() { iphoneCenterText( 460, 300, str ); } - +//========================================== +// iphoneUpdateControlDrags() +// Updates the touches for the scrolling maps +//========================================== +void iphoneUpdateControlDrags(int numPresets, int x, int y, int w, int h, int spacing, int *dragPosition, int *dragVelocity) +{ + //Update drag/touch info +// int x = 0; +// int w = 480;// - skillSides - 64; + /* + if (TouchDown(x, y, w, h)) + { + if (!numPrevTouches) + prevTouches[0][0] = touches[0][0]; + else if (numTouches == 1) + *dragVelocity = touches[0][0] - prevTouches[0][0]; + }*/ + + //update the drags + *dragPosition += *dragVelocity; + /* + //int rightBoundary = 240 - 2*width;//480/2 - (numPresets*width/2 + spacing); + if (*dragPosition < -270-10)//(numPresets*(width+spacing))/2 ) + *dragPosition = -270-10;//(numPresets*(width+spacing))/2; + else if (*dragPosition > 70+10)//rightBoundary) + *dragPosition = 70+10;//rightBoundary; + + if (*dragPosition < -270) + *dragVelocity = 2; + else if (*dragPosition > 70) + *dragVelocity = -2; + */ + + //retard the velocity + if (*dragVelocity > 0) + --*dragVelocity; + else if (*dragVelocity < 0) + ++*dragVelocity; + +} +#if 0 /* ================== iphoneControlMenu @@ -418,50 +1191,432 @@ void iphoneMainMenu() { void iphoneControlMenu() { int i; + int x = 0; + int y = 0; + + iphoneDrawPic(0, 0, 480, 320, "iphone/submenus_background_image.tga"); + if ( BackButton() ) { menuState = IPM_MAIN; } - if ( iphoneDrawPicWithTouch( 480-128, 0, 128, 32, "iphone/advanced_button.tga" ) ) { + + + x = 480-128*9/10; + if ( iphoneDrawPicWithTouch( x, y, 128*9/10, 36/*32*9/10*/, "iphone/advanced_button.tga" ) ) { Cvar_SetValue( controlScheme->name, -1 ); iphonePreloadBeforePlay(); // make sure all the hud textures are loaded menuState = IPM_HUDEDIT; } + x = 240-108*9/10; + iphoneDrawPic(x, y, 217*9/10, 50*9/10, "iphone/header_settings.tga"); + + //update the drags + int startPosition = 70; + static int dragPosition = 70; + static int dragVelocity = 0; + + y = 20; + x = 0; + + if (iphoneDrawPicWithTouch(9, y+60, 64, 64, "iphone/button_left.tga") ) + dragVelocity = -20;//-18; + if (iphoneDrawPicWithTouch(411, y+60, 64, 64, "iphone/button_right.tga") ) + dragVelocity = 20;//18; + + int size = 160;//180;//110; + //iphoneUpdateControlDrags(4, 70, y+25, size*2, size - 53, 10, &dragPosition, &dragVelocity ); + + //update the drags + dragPosition += dragVelocity; + + //retard the drags + if (dragVelocity > 0) + --dragVelocity; + else if (dragVelocity < 0) + ++dragVelocity; + + //stop dragging when we're slow and close to a node + if ((dragVelocity < 10 && dragVelocity > 0) || (dragVelocity > -10 && dragVelocity < 0)) + { + if (abs((dragPosition-startPosition)%(size+10)) < 7) + { + dragVelocity = 0; + int tx = dragPosition - startPosition; + int n = tx/(size+10);//480; + dragPosition = n*(size+10) + startPosition; + + } + } + if (dragVelocity == 0) + { + int spacing = 10; + int height = size; + int n = (dragPosition - startPosition) / (height+spacing); + + int nodePosition = n*(height+spacing) + startPosition; + if (nodePosition != dragPosition) + { + int nodePositionPlus = nodePosition + (height+spacing); + int nodePositionMinus = nodePosition - (height+spacing); + int distMinus = abs(nodePositionMinus - dragPosition); + int distPlus = abs(nodePositionPlus - dragPosition); + int dist = abs(nodePosition - dragPosition); + + + int diff = nodePosition - dragPosition; + + if (distMinus < distPlus && distMinus < dist) + diff = nodePositionMinus - dragPosition; + else if (distPlus < dist && distPlus < distMinus) + diff = nodePositionPlus - dragPosition; + + if (diff < 0) { + dragVelocity -= 5; + } + else { + dragVelocity += 5; + } + } + } + + + //draw the preset layouts for ( i = 0 ; i < 4 ; i++ ) { char str[128]; int remap[4] = { 3,4,1,2}; // artist named them differently than intended... - sprintf( str, "iphone/layout_%i.tga", remap[i] ); + sprintf( str, "iphone/iphone_preset_%i.tga", remap[i] ); if ( i != controlScheme->value ) { pfglColor3f( 0.5, 0.5, 0.5 ); } - if ( iphoneDrawPicWithTouch( 5 + 120 * i, 40, 110, 110, str ) ) { + int thisX = 5 + (size+10) * i + dragPosition; + + //wrap presets around in a circle + if (thisX > 70 + 480 -140) + thisX -= (size+10) * 4; + + if (thisX < 70 - 480 + 140) + thisX += (size+10) * 4; + + if (dragPosition > 70 + (size+10) * 4 ) + dragPosition = 70; + + if (dragPosition < 70 - (size+10) * 4 ) + dragPosition = 70; + + if ( iphoneDrawPicInBoxWithTouchSpecified( RectMake(thisX, y+50, size, size/2), + RectMake(thisX, y + 25, size, size - 53), + str, + RectMake(70, 0, 480-140, 320) ) + && !dragVelocity) + { Cvar_SetValue( controlScheme->name, i ); HudSetForScheme( i ); } pfglColor3f( 1, 1, 1 ); + + char buffer[32]; + sprintf(buffer, "Preset %i", i+1); + + pfglColor3f( 0, 0, 0 ); + iphoneDrawTextInBox( RectMake(thisX+35+1, y+125+1, 16, 16), 480, buffer, RectMake(70, 0, 480-140+10, 320) ); + iphoneDrawTextInBox( RectMake(thisX+35+2, y+125+2, 16, 16), 480, buffer, RectMake(70, 0, 480-140+10, 320) ); + iphoneDrawTextInBox( RectMake(thisX+35+3, y+125+3, 16, 16), 480, buffer, RectMake(70, 0, 480-140+10, 320) ); + pfglColor3f( 1, 1, 1 ); + iphoneDrawTextInBox( RectMake(thisX+35, y+125, 16, 16), 480, buffer, RectMake(70, 0, 480-140+10, 320) ); } + - iphoneSlider( 20, 170, 440, 40, "sensitivity", sensitivity, 0, 1 ); + int sliderHeight = 25;//35; + int sliderSpacing = 10; + int sliderY = y + size;// - 13;//190;//170; + int sliderX = 80;//20; + int sliderWidth = 320;//440; - iphoneSlider( 20, 220, 440, 40, "tilt move speed", tiltMove, 5000, 30000 ); + //sensitivity slider + iphoneSlider( sliderX, sliderY, sliderWidth, sliderHeight, "sensitivity", sensitivity, 0, 1 ); + sliderY += sliderHeight + sliderSpacing; + + //tilt move speed slider + iphoneSlider( sliderX, sliderY, sliderWidth, sliderHeight, "tilt move speed", tiltMove, 5000, 30000 ); + sliderY += sliderHeight + sliderSpacing; if ( tiltMove->value == 5000 ) { Cvar_SetValue( tiltMove->name, 0 ); } if ( tiltMove->value ) { Cvar_SetValue( tiltTurn->name, 0 ); } - iphoneSlider( 20, 270, 440, 40, "tilt turn speed", tiltTurn, 500, 3000 ); + + //tilt turn speed slider + iphoneSlider( sliderX, sliderY, sliderWidth, sliderHeight, "tilt turn speed", tiltTurn, 500, 3000 ); + sliderY += sliderHeight + sliderSpacing; if ( tiltTurn->value == 500 ) { Cvar_SetValue( tiltTurn->name, 0 ); } if ( tiltTurn->value ) { Cvar_SetValue( tiltMove->name, 0 ); } - - //iphoneSlider( 20, 280, 440, 40, "tilt fire", tiltFire, 0, 1 ); + //hud alpha slider + iphoneSlider( sliderX, sliderY, sliderWidth, sliderHeight, "hud alpha", hudAlpha, 0, 1 ); + sliderY += sliderHeight + sliderSpacing; + + y = 0; + x = 480-74; + if (revLand->value) + { + y = 320-32; + x = 74-64; + } + +#ifdef VOLUMEHACK + //Draw volume up/down settings... gsh + + if ( iphoneDrawPicWithTouch( x, y, 64, 32, "iphone/button_pistol.tga" ) ) { + if ((int)volumeFireUpSetting->value) + Cvar_Set("volumeFireUpSetting", "0"); + else + Cvar_Set("volumeFireUpSetting", "1"); + } + + if ((int)volumeFireUpSetting->value) + iphoneDrawPic(x, y, 64, 32, "iphone/button_pistol.tga"); + else + iphoneDrawPic(x, y, 64, 32, "iphone/button_knife.tga"); + + x = 480-145; + if (revLand->value) + x = 145-64; + + if ( iphoneDrawPicWithTouch( x, y, 64, 32, "iphone/button_pistol.tga" ) ) { + if ((int)volumeFireDownSetting->value) + Cvar_Set("volumeFireDownSetting", "0"); + else + Cvar_Set("volumeFireDownSetting", "1"); + } + + if ((int)volumeFireDownSetting->value) + iphoneDrawPic(x, y, 64, 32, "iphone/button_pistol.tga"); + else + iphoneDrawPic(x, y, 64, 32, "iphone/button_knife.tga"); +#endif } +#endif +/* + ================== + iphoneControlMenu + gsh... different from above + ================== + */ +void iphoneControlMenu() { + int i; + + int x = 0; + int y = 0; + + iphoneDrawPic(x, y, 480, 320, "iphone/submenus_background_image.tga"); + + if ( BackButton() ) { + menuState = IPM_MAIN; + } + + x = 480-128*9/10; + if ( iphoneDrawPicWithTouch( x, y, 128*9/10, 36, "iphone/advanced_button.tga" ) ) { + Cvar_SetValue( controlScheme->name, -1 ); + iphonePreloadBeforePlay(); // make sure all the hud textures are loaded + menuState = IPM_HUDEDIT; + } + + x = 240-108*9/10; + iphoneDrawPic(x, y, 217*9/10, 50*9/10, "iphone/header_settings.tga"); + + //update the position of the control settings + const int startPosition = 70; + static int currentPreset = 0; + int numPresets = 4; + + //update the drags + static int Position = 70; + + y = 20; + x = 0; + + static int moveRight = 0; + + //move right or left, depending on button pushed + if (iphoneDrawPicWithTouch(9, y+60, 64, 64, "iphone/button_left.tga") ) { + moveRight = 0; + ++currentPreset; + if (currentPreset > numPresets-1) { + currentPreset = 0;//1; + + } + } + if (iphoneDrawPicWithTouch(411, y+60, 64, 64, "iphone/button_right.tga") ) { + moveRight = 1; + --currentPreset; + if (currentPreset < 0) { + currentPreset = numPresets-1;//2; + } + } + + + int width = 160;//305; + int spacing = 10;//215;//175; + + int size = width;// + spacing; + + int destinationPosition = startPosition - currentPreset*(width+spacing); + + //if we are not where we are supposed to be... get us there + if (Position != destinationPosition) + { + int oldPosition = Position; + int deltaPos = destinationPosition - Position; + + //move entities + int moveDistance = 8; + if ( moveRight ) + { + Position += moveDistance; + if (destinationPosition < Position && destinationPosition > oldPosition) + Position = destinationPosition; + } + else //move left + { + Position -= moveDistance; + if (destinationPosition > Position && destinationPosition < oldPosition) + Position = destinationPosition; + } + + //check if the current position has crossed over the destination position + if ( abs(deltaPos) <= 16) + Position = destinationPosition; + } + + + //draw the preset layouts + for ( i = 0 ; i < 4 ; i++ ) { + char str[128]; + int remap[4] = { 3,4,1,2}; // artist named them differently than intended... + sprintf( str, "iphone/iphone_preset_%i.tga", remap[i] ); + if ( i != controlScheme->value ) { + pfglColor3f( 0.5, 0.5, 0.5 ); + } + int thisX = 5 + (size+10) * i + Position; + + //wrap presets around in a circle + if (thisX > 70 + 480 -140) + thisX -= (size+10) * 4; + + if (thisX < 70 - 480 + 140) + thisX += (size+10) * 4; + + if (Position > 70)//70 + (size+10) * 4 ) + Position = 70 - (size+10)*4; + + if (Position < 70 - (size+10) * 4 ) + Position = 70; + + if ( iphoneDrawPicInBoxWithTouchSpecified( RectMake(thisX, y+50, size, size/2), + RectMake(thisX, y + 25, size, size - 53), + str, + RectMake(70, 0, 480-140, 320) ) ) + { + Cvar_SetValue( controlScheme->name, i ); + HudSetForScheme( i ); + } + pfglColor3f( 1, 1, 1 ); + + char buffer[32]; + sprintf(buffer, "Preset %i", i+1); + + pfglColor3f( 0, 0, 0 ); + iphoneDrawTextInBox( RectMake(thisX+35+1, y+125+1, 16, 16), 480, buffer, RectMake(70, 0, 480-140+10, 320) ); + iphoneDrawTextInBox( RectMake(thisX+35+2, y+125+2, 16, 16), 480, buffer, RectMake(70, 0, 480-140+10, 320) ); + iphoneDrawTextInBox( RectMake(thisX+35+3, y+125+3, 16, 16), 480, buffer, RectMake(70, 0, 480-140+10, 320) ); + pfglColor3f( 1, 1, 1 ); + iphoneDrawTextInBox( RectMake(thisX+35, y+125, 16, 16), 480, buffer, RectMake(70, 0, 480-140+10, 320) ); + } + + + int sliderHeight = 25;//35; + int sliderSpacing = 10; + int sliderY = y + size;// - 13;//190;//170; + int sliderX = 80;//20; + int sliderWidth = 320;//440; + + //sensitivity slider + iphoneSlider( sliderX, sliderY, sliderWidth, sliderHeight, "sensitivity", sensitivity, 0, 1 ); + sliderY += sliderHeight + sliderSpacing; + + //tilt move speed slider + iphoneSlider( sliderX, sliderY, sliderWidth, sliderHeight, "tilt move speed", tiltMove, 5000, 30000 ); + sliderY += sliderHeight + sliderSpacing; + if ( tiltMove->value == 5000 ) { + Cvar_SetValue( tiltMove->name, 0 ); + } + if ( tiltMove->value ) { + Cvar_SetValue( tiltTurn->name, 0 ); + } + + //tilt turn speed slider + iphoneSlider( sliderX, sliderY, sliderWidth, sliderHeight, "tilt turn speed", tiltTurn, 500, 3000 ); + sliderY += sliderHeight + sliderSpacing; + if ( tiltTurn->value == 500 ) { + Cvar_SetValue( tiltTurn->name, 0 ); + } + if ( tiltTurn->value ) { + Cvar_SetValue( tiltMove->name, 0 ); + } + + //hud alpha slider + iphoneSlider( sliderX, sliderY, sliderWidth, sliderHeight, "hud alpha", hudAlpha, 0, 1 ); + sliderY += sliderHeight + sliderSpacing; + + y = 0; + x = 480-74; + if (revLand->value) + { + y = 320-32; + x = 74-64; + } + +#ifdef VOLUMEHACK + //Draw volume up/down settings... gsh + + if ( iphoneDrawPicWithTouch( x, y, 64, 32, "iphone/button_pistol.tga" ) ) { + if ((int)volumeFireUpSetting->value) + Cvar_Set("volumeFireUpSetting", "0"); + else + Cvar_Set("volumeFireUpSetting", "1"); + } + + if ((int)volumeFireUpSetting->value) + iphoneDrawPic(x, y, 64, 32, "iphone/button_pistol.tga"); + else + iphoneDrawPic(x, y, 64, 32, "iphone/button_knife.tga"); + + + x = 480-145; + if (revLand->value) + x = 145-64; + + if ( iphoneDrawPicWithTouch( x, y, 64, 32, "iphone/button_pistol.tga" ) ) { + if ((int)volumeFireDownSetting->value) + Cvar_Set("volumeFireDownSetting", "0"); + else + Cvar_Set("volumeFireDownSetting", "1"); + } + + if ((int)volumeFireDownSetting->value) + iphoneDrawPic(x, y, 64, 32, "iphone/button_pistol.tga"); + else + iphoneDrawPic(x, y, 64, 32, "iphone/button_knife.tga"); +#endif +} + /* ================== @@ -473,6 +1628,12 @@ void iphoneSkillMenu() { int s; char str[64]; + //gsh + iphoneDrawPic(0, 0, 480, 320, "iphone/submenus_background_image.tga"); + + //gsh + iphoneDrawPic(0, 0, 480, 48, "iphone/header_how_tough_are_you.tga"); + if ( BackButton() ) { menuState = IPM_MAIN; } @@ -487,7 +1648,8 @@ void iphoneSkillMenu() { if ( s != (int)skill->value ) { pfglColor3f( 0.5, 0.5, 0.5 ); } - if ( iphoneDrawPicWithTouch( 112, 40+64*s, 256, 64, str ) ) { + //if ( iphoneDrawPicWithTouch( 112, 40+64*s, 256, 64, str ) ) { + if ( iphoneDrawPicWithTouch( 112, 60+64*s, 256, 64, str ) ) { Cvar_SetValue( skill->name, s ); menuState = IPM_EPISODE; } @@ -495,6 +1657,7 @@ void iphoneSkillMenu() { } } + /* ================== iphoneEpisodeMenu @@ -508,6 +1671,9 @@ void iphoneEpisodeMenu() { int numE = 1; #else int numE = 6; + //gsh + if (g_version->value == SPEAROFDESTINY) + numE = 9; #endif if ( BackButton() ) { @@ -516,24 +1682,925 @@ void iphoneEpisodeMenu() { // 96 x 48 images for ( e = 0 ; e < numE ; e++ ) { - my_snprintf( str, sizeof( str ), "iphone/button_ep%i.tga", e+1 ); + + //gsh... added the if and button_epSOD + if (e < 6) + my_snprintf( str, sizeof( str ), "iphone/button_ep%i.tga", e+1 ); + else + my_snprintf(str, sizeof( str), "iphone/button_epSOD.tga"); if ( e != (int)episode->value ) { pfglColor3f( 0.5, 0.5, 0.5 ); } + int height = 32;//48;//gsh... 96x32 + if ( iphoneDrawPicWithTouch( 48, 32+height*e, 384, height, str ) ) { + Cvar_SetValue( episode->name, e ); + menuState = IPM_MAPS; + }/*gsh if ( iphoneDrawPicWithTouch( 48, 32+48*e, 384, 48, str ) ) { Cvar_SetValue( episode->name, e ); menuState = IPM_MAPS; - } + }*/ pfglColor3f( 1, 1, 1 ); } #ifdef LITE // buy more episodes button GetMoreLevels( 240 - 64, 200 ); +#else + #if 0 + if (g_version->value != SPEAROFDESTINY) //gsh + GetSpearOfDestiny( 240 - 64, 232 ); + #endif #endif } +/* + ================== + iphoneUpdateScrollingDrags + gsh + ================== + */ +//int scrollingFlags = 0; +void iphoneUpdateScrollingDrags(int *dragPosition, int *dragVelocity, int upperLimit, int lowerLimit, int padding, rect_t touchRect) +{ + static int oldVelocity = 0; + + + //if Touch is in touchable area +// if (TouchDown(touchRect.x, touchRect.y, touchRect.width, touchRect.height)) + if (TouchDown( 0, 0, 480, 320) )//|| isTouchMoving) + { + if (!numPrevTouches) + prevTouches[0][1] = touches[0][1]; + else if (numTouches == 1) + *dragVelocity = touches[0][1] - prevTouches[0][1]; + + //if (*dragVelocity < 3) + // isTouchMoving = 0; + + /* + Com_Printf("update drags: Touch is down!\n"); + Com_Printf("dragVelocity: %i\n", *dragVelocity); + Com_Printf("prevTouch: %i\n", prevTouches[0][1]); + Com_Printf("touch: %i\n", touches[0][1]); + */ + } +// else if (*dragVelocity == 0) //this returns the position to a node position + else if (abs(*dragVelocity) < 5) //this returns the position to a node position + { + int startPosition = 70 - 10; + int height = 48; + int spacing = 20; + int nodePosition = upperLimit + padding; + + + int n = (*dragPosition - startPosition) / (height+spacing); + + nodePosition = n*(height+spacing) + startPosition;// - 5; +/* if (nodePosition != *dragPosition) + {/* + int nodePositionPlus = nodePosition + (height+spacing); + int nodePositionMinus = nodePosition - (height+spacing); + int distMinus = abs(nodePositionMinus - *dragPosition); + int distPlus = abs(nodePositionPlus - *dragPosition); + int dist = abs(nodePosition - *dragPosition); + + + int diff = nodePosition - *dragPosition; + + if (distMinus < distPlus && distMinus < dist) + diff = nodePositionMinus - *dragPosition; + else if (distPlus < dist && distPlus < distMinus) + diff = nodePositionPlus - *dragPosition; + + if (diff < 0) + --*dragVelocity; + else + ++*dragVelocity; + * + if (oldVelocity > 0) + ++*dragVelocity; + else if (oldVelocity < 0) + --*dragVelocity; + }*/ + + //let's try a different way to continue our path + if (nodePosition != *dragPosition && oldVelocity > 0) + { + int minSpeed = 5; + if (oldVelocity > 0) { + *dragVelocity = minSpeed; + int newPosition = *dragPosition + *dragVelocity; + + Com_Printf("newPosition: %i\n", newPosition); + Com_Printf("nodePosition: %i\n", nodePosition); + Com_Printf("dragPosition: %i\n\n\n", *dragPosition); + + if ( newPosition > nodePosition ) { + *dragPosition = nodePosition; + *dragVelocity = 0; + } + }/* + else if (oldVelocity < 0) { + *dragVelocity = -minSpeed; + int newPosition = *dragPosition + *dragVelocity; + + Com_Printf("newPosition: %i\n", newPosition); + Com_Printf("nodePosition: %i\n", nodePosition); + Com_Printf("dragPosition: %i\n\n\n", *dragPosition); + + if ( newPosition < nodePosition - height ){//(height + spacing)) { + *dragPosition = nodePosition; + *dragVelocity = 0; + } + }*/ + } + + nodePosition = (n-1)*(height+spacing) + startPosition;// - 10;//5; +// if (nodePosition - (height + spacing - 5) != *dragPosition && oldVelocity < 0) + if (nodePosition != *dragPosition && oldVelocity < 0) + { + int minSpeed = 5; + + //else if (oldVelocity < 0) { + *dragVelocity = -minSpeed; + int newPosition = *dragPosition + *dragVelocity; +// nodePosition -= height + spacing - 5; + + Com_Printf("newPosition: %i\n", newPosition); + Com_Printf("nodePosition: %i\n", nodePosition); + Com_Printf("dragPosition: %i\n\n\n", *dragPosition); + + if ( newPosition < nodePosition /*- height*/ ){//(height + spacing)) { + *dragPosition = nodePosition; + *dragVelocity = 0; + } + //} + + } + } + + + oldVelocity = *dragVelocity; + + + //update the drags + *dragPosition += *dragVelocity; + + //boundary check our drags + if (*dragPosition < lowerLimit - padding) + *dragPosition = lowerLimit - padding; + if (*dragPosition > upperLimit + padding) + *dragPosition = upperLimit + padding; + + if (*dragPosition < lowerLimit) + *dragVelocity = 2; + if (*dragPosition > upperLimit) + *dragVelocity = -2; + + //retard the velocity + if (*dragVelocity > 0) + --*dragVelocity; + else if (*dragVelocity < 0) + ++*dragVelocity; + + +// if (abs(*dragVelocity) < 5) +// *dragVelocity = 0; + +} + +/* + ================== + iphoneScrollingEpisodeMenu + gsh + ================== + */ +void iphoneScrollingEpisodeMenu() { + int e; + char str[64]; + int doesUserMapsExist = 0; + + //used for preventing accidental touches + //user must hold touch down for 10 frames in order to load episode + static int touchedFrameNum = 0; + static int lastTouchFrame = 0; + +#ifdef LITE + int numE = 1; +#else + int numE = 11;//10; + + //check for user maps + DIR *dp; + char mapBuffer[1024]; + + int length = strlen(iphoneDocDirectory); + strcpy(mapBuffer, iphoneDocDirectory); + strcpy(mapBuffer + length, "/usermaps/"); + + dp = opendir (mapBuffer); + + + if (dp) { + doesUserMapsExist = 1; + //numE = 11; + closedir(dp); + } + + //Com_Printf("number of episodes: %i\n", numE); + //gsh + //if (g_version->value == SPEAROFDESTINY) + // numE = 9; +#endif + + + + iphoneDrawPic(0, 0, 480, 320, "iphone/submenus_background_image.tga"); + + iphoneDrawPic( 0, 0, 480, 48, "iphone/header_episodes.tga"); + + if ( BackButton() ) { + menuState = IPM_SKILL; + } + + static int dragPosition = 70; + static int dragVelocity = 0; + +// Com_Printf("dragPosition: %i dragVelocity: %i\n", dragPosition, dragVelocity ); + +/* int height=(320-48)/2; + //iphoneDrawPic( 480-64, 60, 64, 64, "iphone/button_back.tga"); + if (iphoneDrawPicWithTouch( 480-64, 48, 64, height, "iphone/button_back.tga"))//TouchDown(480-64, 60, 64, 64)) + dragVelocity = -25; + //iphoneDrawPic( 480-64, 60+64, 64, 64, "iphone/button_back.tga"); + if ( iphoneDrawPicWithTouch( 480-64, height+48, 64, height, "iphone/button_back.tga"))//TouchDown(480-64, 60+64, 64, 64)) + dragVelocity = 25; +*/ + int lowerLimit = -320; + lowerLimit = 70-(11-4)*(48+20); + iphoneUpdateScrollingDrags(&dragPosition, &dragVelocity, 55, lowerLimit, 0, RectMake(0, 48, 480-48, 320-64)); + + // 96 x 48 images + for ( e = 0 ; e < numE ; e++ ) { + + my_snprintf( str, sizeof( str ), "iphone/button_ep%i.tga", e+1 ); + + //light the episode we are currently on + if (currentMap.episode > 5 && currentMap.episode < 10) + { + //int tempEpisode = 6; + int levelNum = currentMap.episode*10 + currentMap.map; + + //check for spear of destiny episodes + if (levelNum == 78 || (levelNum >= 60 && levelNum < 65)) + { + if ( e != 6 ) + pfglColor3f( 0.5, 0.5, 0.5 ); + } + else if (levelNum >= 65 && levelNum < 70) + { + if ( e != 7 ) + pfglColor3f( 0.5, 0.5, 0.5 ); + } + else if ((levelNum >= 70 && levelNum < 76) || levelNum == 79) + { + if ( e != 8 ) + pfglColor3f( 0.5, 0.5, 0.5 ); + } + else if (levelNum == 76 || levelNum == 77 || levelNum == 80) + { + if ( e != 9 ) + pfglColor3f( 0.5, 0.5, 0.5 ); + } + + } + else if ( e != (int)episode->value ) { + pfglColor3f( 0.5, 0.5, 0.5 ); + } + + //Com_Printf("isTouchMoving value: %i\n", isTouchMoving); + + + int height = 48; + int spacing = 20;//10; + //if ( iphoneDrawPicInBoxWithTouch( 48, dragPosition+(height+spacing)*e, 384, height, str ) && !dragVelocity ) { +/* if (iphoneDrawPicInBoxWithTouchSpecified(RectMake(0, dragPosition+(height+spacing)*e, 480, height), + RectMake(0, dragPosition+(height+spacing)*e, 480, height), + str, + RectMake(0, 48, 480, 320-48) )//RectMake(0, 42, 480, 238) ) + && !dragVelocity + && !isTouchMoving )//touchedFrameNum >= NUM_TOUCHFRAME ) //must hold finger down for 10 frames +*/// iphoneDrawPicInBoxWithTouchAndVelocity + if (iphoneDrawPicInBoxWithTouchAndVelocity(RectMake(0, dragPosition+(height+spacing)*e, 480, height), + RectMake(0, dragPosition+(height+spacing)*e, 480, height), + str, + RectMake(0, 48, 480, 320-48), dragVelocity | isTouchMoving ) )//RectMake(0, 42, 480, 238) ) + //&& !dragVelocity + //&& !isTouchMoving ) + { + //if they haven't downloaded any custom maps, but choose custom maps, goto the more menu + if (e == 10 && !doesUserMapsExist) + { + wasDLInstructionsCalledFromEpisodeMenu = 1; + menuState = IPM_DOWNLOADINSTRUCTIONS; + pfglColor3f( 1, 1, 1 ); + break; + } + Cvar_SetValue( episode->name, e ); + menuState = IPM_MAPS; + } + pfglColor3f( 1, 1, 1 ); + } + + //update the number of frames the touch is down + if ( TouchDown(0, 0, 480, 320) ) { + int dframe = iphoneFrameNum - lastTouchFrame; + touchedFrameNum += dframe; + } + else { + touchedFrameNum = 0; + } + lastTouchFrame = iphoneFrameNum; + +#ifdef LITE + // buy more episodes button + GetMoreLevels( 240 - 64, 200 ); +#else +#ifdef SPEARSTOREKIT + if (g_version->value != SPEAROFDESTINY) //gsh + GetSpearOfDestiny( 240 - 64, 232 ); +#endif +#endif + + if ( MenuButton() ) { + menuState = IPM_MAIN; + } +} +//gsh +extern void iphoneDrawRewards(int m, int s, int x, int y); +//gsh +extern int returnButtonFrameNum; +/* +//gsh... this is for the map selections +colour4_t colorSecret = { 80, 0, 0, 100 }; +//colour4_t colorNoTry = { 0, 0, 0, 100 }; +colour4_t colorNoTry = { 0, 0, 0, 0 }; +colour4_t colorTried = { 120, 120, 0, 100 }; +colour4_t colorCompleted = { 0, 0, 120, 80 }; +*/ +colour4_t colorSecret = { 120, 0, 60, 100 }; +//colour4_t colorNoTry = { 0, 0, 0, 100 }; +colour4_t colorNoTry = { 0, 0, 0, 150 }; +colour4_t colorTried = { 80, 80, 80, 40 }; +colour4_t colorCompleted = { 0, 120, 0, 80 }; + +/* + ======================== + iphoneDrawWolf3dMaps + gsh + ======================== + */ +void iphoneDrawWolf3dMaps(int dragPosition, int dragVelocity, int e, int s, int height, int spacing, int touchedFrameNum) +{ + char str[64]; + /* + colour4_t colorSecret = { 64, 0, 0, 100 }; + colour4_t colorNoTry = { 0, 0, 0, 100 }; + colour4_t colorTried = { 80, 80, 0, 100 }; + colour4_t colorCompleted = { 0, 80, 0, 100 }; + */ + + + + // draw the individual maps + for ( int m = 0 ; m < 10; m++ ) { + int y; + + switch(m) { + case 9: sprintf( str, "SECRET" ); break; + case 8: sprintf( str, "BOSS" ); break; + default: sprintf( str, "LEVEL %i", m+1 ); break; + } + + y = m*(height + spacing) + dragPosition; + + unsigned char *color = colorNoTry; + // decide on the background color + int levelNum = e*10+m; + int ch = currentMap.mapFlags[s][levelNum]; + + // bit1 = attempted + // bit2 = completed + // bit3 = 100% kills + // bit4 = 100% secrets + // bit5 = 100% treasure + if ( m == 9 && !( ch & MF_TRIED ) ) { + color = colorSecret; + } else if ( ch & MF_COMPLETED ) { + color = colorCompleted; + } else if ( ch & MF_TRIED ) { + color = colorTried; + } else { + color = colorNoTry; + } + + // blink the level you are currently on + if ( ( iphoneFrameNum & 8 ) && m == currentMap.map && e == currentMap.episode ) { + color = colorNoTry; + } + /* + //TODO: delete this switch statement + switch (m%4){ + case 0: color = colorSecret; break; + case 1: color = colorTried; break; + case 2: color = colorCompleted; break; + case 3: color = colorNoTry; break; + }*/ + + rect_t box = RectMake(0, 48, 480, 320-48); + int borderWidth = 40; +/* if (iphoneDrawPicInBoxWithTouchSpecifiedAndColor(RectMake(borderWidth, y - 9, 480-2*borderWidth, height), + RectMake(borderWidth, y - 9, 480-2*borderWidth, height), + "iphone/menu_bar.tga", + box, + color) && !dragVelocity + && !isTouchMoving)//touchedFrameNum >= 10) +*/ if (iphoneDrawPicInBoxWithTouchColorAndVelocity(RectMake(borderWidth, y - 9, 480-2*borderWidth, height), + RectMake(borderWidth, y - 9, 480-2*borderWidth, height), + "iphone/menu_bar.tga", + box, + color, dragVelocity | isTouchMoving) )// && !dragVelocity + //&& !isTouchMoving) + { + // don't let them go to the secret level unless they earned it + if ( m == 9 && !( ch & MF_TRIED ) ) + continue; + + PL_NewGame( &Player ); + iphoneStartMap( e, m, s ); + iphoneFrameNum = 0; + returnButtonFrameNum = 100; + iphonePreloadBeforePlay(); + } + + //draw the rewards + iphoneDrawRewards(e*10+m, s, 0, y); + + //iphoneCenterText( x, y, str ); +// iphoneDrawTextInBox(RectMake(100, y, 16, 16), 500, str, box); +// iphoneDrawText(100, y, 16, 16, str); +// pfglColor4ub(0xE1, 0xA6, 0x0, 0xFF); +// iphoneDrawText( 100, y , 16, 16, str ); +// pfglColor4ub(0xFF, 0xFF, 0xFF, 0xFF); + iphoneDrawMapName( RectMake(100, y, 16, 16), str); +// iphoneDrawMapName( RectMake(100, y, 32, 32), str); +// iphoneFrameNum = 0; +// returnButtonFrameNum = 100;//60; + } +} + +/* + ================== + iphoneDrawSpearMaps + gsh + ================== + */ + +void iphoneDrawSpearMaps(int dragPosition, int dragVelocity, int fakeEpisode, int s, int height, int spacing, int touchedFrameNum) +{ + char str[64]; + /* + colour4_t colorSecret = { 64, 0, 0, 100 }; + colour4_t colorNoTry = { 0, 0, 0, 100 }; + colour4_t colorTried = { 80, 80, 0, 100 }; + colour4_t colorCompleted = { 0, 80, 0, 100 }; + * + //TODO: change these colors!!!! + colour4_t colorSecret = { 80, 0, 0, 100 }; + colour4_t colorNoTry = { 0, 0, 0, 100 }; + colour4_t colorTried = { 120, 120, 0, 100 }; + colour4_t colorCompleted = { 0, 0, 120, 80 }; + */ + int numMaps = 0; + switch (fakeEpisode) { + case 6: numMaps = 6; break; //tunnels maps 1-5, 19 + //case 7: numMaps = 6; break; //dungeon maps 6-10, 20 + //case 8: numMaps = 6; break; //castle maps 11-16 + case 7: numMaps = 5; break; //dungeon maps 6-10 + case 8: numMaps = 7; break; //castle maps 11-16, 20 + case 9: numMaps = 3; break; //final maps 17, 18 and 21 + default: break; + } + + //the real episode & map values + int e = 6; + int m = 0; + + // draw the individual maps + for ( int fakeMap = 0; fakeMap < numMaps; fakeMap++ ) { + int y; + + if (fakeEpisode == 9) + { + switch(fakeMap) { + case 0: sprintf( str, "Ramparts" ); break; + case 1: sprintf( str, "Death Knight" ); break; + case 2: sprintf( str, "Dungeon Dimension" ); break; + default: sprintf( str, "LEVEL %i", fakeMap+1 ); break; + } + } + else + { + switch(fakeMap) { + case 6: if (fakeEpisode == 8) sprintf(str, "SECRET"); break; + case 5: if (fakeEpisode == 8) sprintf( str, "BOSS" ); else sprintf( str, "SECRET" ); break; + case 4: if (fakeEpisode != 8){ sprintf( str, "BOSS" ); break; } + default: sprintf( str, "LEVEL %i", fakeMap+1 ); break; + } + } + + //get the real episode and map and store in e, m + e = fakeEpisode; + m = fakeMap; + switch (fakeEpisode) + { + case 6: + if (fakeMap == 5) { + e = 7; + m = 8; + } + break; + case 7: + e = 6; + m += 5; + break; + case 8: + e = 7; + if (fakeMap == 6) + m = 9; + break; + case 9: + if ( fakeMap == 0) { e = 7; m = 6; } + if ( fakeMap == 1) { e = 7; m = 7; } + if ( fakeMap == 2) { e = 8; m = 0; } + break; + default: + break; + } + + Com_Printf("fakeEpisode: %i fakeMap: %i\n", fakeEpisode, fakeMap); + Com_Printf("e: %i m: %i\n", e, m); + + y = fakeMap*(height + spacing) + dragPosition; + + // decide on the background color + unsigned char *color = colorNoTry; + int levelNum = e*10+m; + int ch = currentMap.mapFlags[s][levelNum]; + + // bit1 = attempted + // bit2 = completed + // bit3 = 100% kills + // bit4 = 100% secrets + // bit5 = 100% treasure + if ( (levelNum == 78 || levelNum == 79) && !( ch & MF_TRIED ) ) { + color = colorSecret; + } else if ( ch & MF_COMPLETED ) { + color = colorCompleted; + } else if ( ch & MF_TRIED ) { + color = colorTried; + } else { + color = colorNoTry; + } + + //blink the level you are currently on + if ( ( iphoneFrameNum & 8 ) && m == currentMap.map && e == currentMap.episode ) { + color = colorNoTry; + } + + rect_t box = RectMake(0, 48, 480, 320-48); + int borderWidth = 40; +/* if (iphoneDrawPicInBoxWithTouchSpecifiedAndColor(RectMake(borderWidth, y - 9, 480-2*borderWidth, height), + RectMake(borderWidth, y - 9, 480-2*borderWidth, height), + "iphone/menu_bar.tga", + box, + color) && !dragVelocity + && !isTouchMoving)//touchedFrameNum >= NUM_TOUCHFRAME) +*/ if (iphoneDrawPicInBoxWithTouchColorAndVelocity(RectMake(borderWidth, y - 9, 480-2*borderWidth, height), + RectMake(borderWidth, y - 9, 480-2*borderWidth, height), + "iphone/menu_bar.tga", + box, + color, dragVelocity | isTouchMoving ) ) + //&& !isTouchMoving) + { + //diagnostic info + Com_Printf("fakeEpisode: %i fakeMap: %i\n", fakeEpisode, fakeMap); + Com_Printf("e: %i m: %i\n", e, m); + + // don't let them go to the secret level unless they earned it + if ( (levelNum == 78 || levelNum == 79) && !( ch & MF_TRIED ) ) + continue; + + PL_NewGame( &Player ); + iphonePreloadBeforePlay(); + iphoneStartMap(e, m, s); + iphoneFrameNum = 0; + returnButtonFrameNum = 100; + break; //break the loop... no need to keep looping + } + + //draw the rewards + iphoneDrawRewards(levelNum, s, 0, y); + + //draw map name + iphoneDrawMapName( RectMake(100, y, 16, 16), str); + } +} + +/* + ================== + iphoneDrawUserCreatedMaps + gsh + ================== + */ +int iphoneDrawUserCreatedMaps(int dragPosition, int dragVelocity, /*int fakeEpisode,*/ int skillValue, int height, int spacing, int touchedFrameNum) +{ + int numMaps = 0; + + DIR *dp; + struct dirent *ep; + char mapBuffer[1024]; + + int length = strlen(iphoneDocDirectory); + strcpy(mapBuffer, iphoneDocDirectory); + strcpy(mapBuffer + length, "/usermaps/"); + + dp = opendir (mapBuffer); + + if (dp != NULL) + { + /* + //colour4_t colorSecret = { 64, 0, 0, 100 }; + colour4_t colorNoTry = { 0, 0, 0, 100 }; + colour4_t colorTried = { 80, 80, 0, 100 }; + colour4_t colorCompleted = { 0, 80, 0, 100 }; + */ + unsigned char *color = colorNoTry; + + int y = dragPosition;//Yoffset; + + //draw the custom map spacing + //y += height + spacing; + iphoneDrawPic( 0, 0, 480, 48, "iphone/header_ep10.tga"); + + Com_Printf("y: %i\n", y); + + while (ep = readdir (dp)) + { + //if you find a .DS_Store file... ignore it! + if ( strcmp(ep->d_name, ".DS_Store") == 0 ) + continue; + + ++numMaps; + if (numMaps < 3) //skip the '.' and '..' + continue; + + //Com_Printf("y: %i\n", y); + + //draw the names! + if ( y >= 0 && y < 320 ) + { + //grab map info + int levelNum = numMaps-3 + 100; + int ch = currentMap.mapFlags[skillValue][levelNum]; + + // bit1 = attempted + // bit2 = completed + // bit3 = 100% kills + // bit4 = 100% secrets + // bit5 = 100% treasure + if ( ch & MF_COMPLETED ) { + color = colorCompleted; + } else if ( ch & MF_TRIED ) { + color = colorTried; + } else { + color = colorNoTry; + } + + //comment to display completed user maps + color = colorNoTry; + + //blink the level you are currently on + if ( ( iphoneFrameNum & 8 ) && (currentMap.map+10*currentMap.episode)==levelNum ) { + //color = colorNoTry; + color = colorTried; + } + + int borderWidth = 40; + rect_t box = RectMake(0, 48, 480, 320-48); + if (iphoneDrawPicInBoxWithTouchColorAndVelocity(RectMake(borderWidth, y - 9, 480-2*borderWidth, height), + RectMake(borderWidth, y - 9, 480-2*borderWidth, height), + "iphone/menu_bar.tga", + box, + color, dragVelocity | isTouchMoving) ) + { + //reset the player to new + PL_NewGame( &Player ); + + //set episode and map numbers and load map + int e = 10; //10 will be used to show a custom map + Cvar_SetValue( episode->name, e ); + iphonePreloadBeforePlay(); //make sure all necessary textures are loaded + iphoneStartUserMap( e, numMaps - 3, skillValue, ep->d_name ); + iphoneFrameNum = 0; + returnButtonFrameNum = 100; + break; //no point in continuing the loop + } + + + pfglColor4f(0, 0, 0, 1); + iphoneCenterArialText( 240+1, y +25+1, 0.9f, ep->d_name ); + iphoneCenterArialText( 240+2, y +25+2, 0.9f, ep->d_name ); + pfglColor4f(225.0f/255, 166.0f/255, 0, 1); + pfglColor4f(225.0f/255, 242.0f/255, 0, 1); + iphoneCenterArialText( 240, y +25, 0.9f, ep->d_name ); + pfglColor4f(1, 1, 1, 1); + + //draw the rewards + //uncomment to display rewards + //iphoneDrawRewards(numMaps-2+90, skillValue, 0, y); + } + + y += height + spacing; + } + closedir (dp); + + --numMaps; + --numMaps; + } + + return numMaps; +} + +/* + ================== + iphoneScrollingMapMenu + gsh + ================== + */ +extern int forceDrawRewards; +void iphoneScrollingMapMenu() +{ +#ifdef LITE +# define NUM_MAPS 3 +#else +# define NUM_MAPS 10 +#endif + + + //used for preventing accidental touches + //user must hold touch down for 10 frames in order to load map + static int touchedFrameNum = 0; + static int lastTouchFrame = 0; + + int e, s; + char str[64]; + + int height = 48; + int spacing = 20;//10; + + static int dragPosition = 70; + static int dragVelocity = 0; + + iphoneDrawPic(0, 0, 480, 320, "iphone/submenus_background_image.tga"); +/* + //static int forceDrawRewards = 0; + //This is only for debugging/testing out the look of the medals + //TODO: delete or comment out + if (iphoneDrawPicWithTouch(0, 320-40, 40, 40, "iphone/button_back.tga")) + { + if (forceDrawRewards == 0) + forceDrawRewards = 1; + else + forceDrawRewards = 0; + } + +*/ + // draw the level selection + e = episode->value; + if ( e < 0 ) { + e = 0; + } + + if ( e > 10 ) //gsh + e = 10; + + s = skill->value; + if ( s < 0 ) { + s = 0; + } + if ( s > 3 ) { + s = 3; + } + + //gsh + int numlevels = 10; + if ( e == 8 ) + numlevels = 1; + + + + //---------------------------- + //Update Drags +/* height=(320-48)/2; + if (iphoneDrawPicWithTouch( 480-64, 48, 64, height, "iphone/button_back.tga")) + dragVelocity = -25; + if ( iphoneDrawPicWithTouch( 480-64, 48+height, 64, height, "iphone/button_back.tga")) + dragVelocity = 25; +*/ height = 48; + + static int lowerLimit = 65; + /* + if ( e < 6) + lowerLimit = -250; + else if ( e < 9) + lowerLimit = -20; + else if ( e < 10) + lowerLimit = 65; + */ + if ( e < 6) { + //lowerLimit = -250; + lowerLimit = 70 - (10-4)*(height+spacing); //startPosition - (numberMaps - numberOnScreen)*(height + spacing) + } + else { + switch (e) { + case 6: //tunnels + lowerLimit = -20; + lowerLimit = 70 - (6-4)*(height+spacing); + break; + case 7: //dungeons + lowerLimit = 20; + lowerLimit = 70 - (5-4)*(height+spacing); + break; + case 8: //castle + lowerLimit = -85; + lowerLimit = 70 - (7-4)*(height+spacing); + break; + case 9: //ramparts + lowerLimit = 65; + break; + } + } + /* + if ( e == 7 ) + lowerLimit = 20; + + if ( e == 8 ) + lowerLimit = -85; + */ + iphoneUpdateScrollingDrags(&dragPosition, &dragVelocity, 65, lowerLimit, 0, RectMake(0, 48, 480-48, 320-64)); + //---------------------------- + + if ( e < 6 ) + iphoneDrawWolf3dMaps(dragPosition, dragVelocity, e, s, height, spacing, touchedFrameNum); + else if ( e < 10 ) + iphoneDrawSpearMaps(dragPosition, dragVelocity, e, s, height, spacing, touchedFrameNum); + else { + int numMaps = iphoneDrawUserCreatedMaps(dragPosition, dragVelocity, s, height, spacing, touchedFrameNum); + + lowerLimit = 65; + if (numMaps > 4) { + //lowerLimit = 269 - (numMaps-1)*(spacing + height); + //lowerLimit = 65 - (numMaps - 4)*(spacing + height) + 30; + lowerLimit = 70 - (numMaps-4)*(height+spacing); + } + } + + + //update the number of frames the touch is down + if ( TouchDown(0, 0, 480, 320) ) { + int dframe = iphoneFrameNum - lastTouchFrame; + touchedFrameNum += dframe; + } + else { + touchedFrameNum = 0; + } + lastTouchFrame = iphoneFrameNum; + + + // draw the header for the episode + my_snprintf( str, sizeof( str ), "iphone/header_ep%i.tga", e+1 ); + iphoneDrawPic( 0, 0, 480, 48, str ); + + if ( BackButton() ) { + menuState = IPM_EPISODE; + } + if ( MenuButton() ) { + menuState = IPM_MAIN; + } +} /* ================== iphoneMapMenu @@ -557,10 +2624,18 @@ void iphoneMapMenu() { e = episode->value; if ( e < 0 ) { e = 0; - } + }/*gsh if ( e > 5 ) { e = 5; - } + }*/ + if ( e > 8 ) //gsh + e = 8; + //if player chose SOD maps... draw all 21 SOD maps.. gsh + /* if ( e >= 6 ){ + iphoneDrawSODMaps(); //this function didn't seem to be working out + return; + } + */ s = skill->value; if ( s < 0 ) { s = 0; @@ -569,6 +2644,11 @@ void iphoneMapMenu() { s = 3; } + //gsh + int numlevels = 10; + if ( e == 8 ) + numlevels = 1; + // draw the episode selection my_snprintf( str, sizeof( str ), "iphone/button_ep%i.tga", e+1 ); iphoneDrawPicWithTouch( 96, 0, 384, 48, str ); @@ -600,6 +2680,8 @@ void iphoneMapMenu() { // decide on the background color int levelNum = e*10+m; int ch = currentMap.mapFlags[s][levelNum]; + + Com_Printf("Map: %i ch: %i\n", levelNum, ch); // bit1 = attempted // bit2 = completed // bit3 = 100% kills @@ -763,26 +2845,108 @@ void DrawRatio( int y, int got, int total, const char *bonusPic ) { void DrawIntermissionStats() { char str[128]; - iphoneDrawPic( 0, 0, 480, 320, "iphone/intermission_256.tga" ); + //gsh +// iphoneDrawPic( 0, 0, 480, 320, "iphone/intermission_256.tga" ); + iphoneDrawPic( 0, 0, 480, 320, "iphone/intermission.tga" ); // episode - my_snprintf( str, sizeof( str ), "iphone/button_ep%i.tga", currentMap.episode+1 ); - iphoneDrawPic( 0, 0, 384, 48, str ); + //if (currentMap.episode < 10)//6) //gsh added the check for SOD +// my_snprintf( str, sizeof( str ), "iphone/button_ep%i.tga", currentMap.episode+1 ); + + //display the correct episode header + if (currentMap.episode < 6) + my_snprintf( str, sizeof( str ), "iphone/header_ep%i.tga", currentMap.episode+1 ); + else if (currentMap.episode < 10) { + int currentLevel = currentMap.episode * 10 + currentMap.map; + switch (currentLevel) { + case 60: case 61: case 62: case 63: case 64: case 78: + my_snprintf( str, sizeof( str ), "iphone/header_ep7.tga" ); + break; + case 65: case 66: case 67: case 68: case 69: case 79: + my_snprintf( str, sizeof( str ), "iphone/header_ep8.tga" ); + break; + case 70: case 71: case 72: case 73: case 74: case 75: + my_snprintf( str, sizeof( str ), "iphone/header_ep9.tga" ); + break; + case 76: case 77: case 80: + my_snprintf( str, sizeof( str ), "iphone/header_ep10.tga" ); + break; +/* case 77: + my_snprintf( str, sizeof( str ), "iphone/header_ep10.tga" ); + break; + case 80: + my_snprintf( str, sizeof( str ), "iphone/header_ep10.tga" ); + break; +*/ default: + sprintf( str, " "); + break; + } + } + else + my_snprintf( str, sizeof( str ), "iphone/header_ep11.tga" ); + //else + // my_snprintf( str, sizeof( str ), "iphone/button_epSOD.tga"); + +// iphoneDrawPic( 0, 0, 384, 48, str ); +// iphoneDrawPic( -104, 0, 480, 48, str ); //gsh + iphoneDrawPic( 0, 0, 480, 48, str ); //gsh // level - iphoneDrawNumber( 430, 0, currentMap.map + 1, 48, 48 ); +// iphoneDrawNumber( 430, 0, currentMap.map + 1, 48, 48 ); +// iphoneDrawNumber( 430-48, 50, currentMap.map + 1, 48, 48 ); //gsh + + //display the appropriate mission number + if (currentMap.episode < 6) + iphoneDrawNumber( 430-48, 50, currentMap.map + 1, 48, 48 ); + else if (currentMap.episode < 10) { + int currentLevel = currentMap.episode * 10 + currentMap.map; + switch (currentLevel) { + case 60: case 61: case 62: case 63: case 64: + iphoneDrawNumber( 430-48, 50, currentLevel - 60 + 1, 48, 48 ); + break; + case 78: + iphoneDrawNumber( 430-48, 50, 6, 48, 48 ); + break; + case 65: case 66: case 67: case 68: case 69: + iphoneDrawNumber( 430-48, 50, currentLevel - 65 + 1, 48, 48 ); + break; + case 79: + iphoneDrawNumber( 430-48, 50, 6, 48, 48 ); + break; + case 70: case 71: case 72: case 73: case 74: case 75: + iphoneDrawNumber( 430-48, 50, currentLevel - 70 + 1, 48, 48 ); + break; + case 76: + iphoneDrawNumber( 430-48, 50, 1, 48, 48 ); + break; + case 77: + iphoneDrawNumber( 430-48, 50, 2, 48, 48 ); + break; + case 80: + iphoneDrawNumber( 430-48, 50, 3, 48, 48 ); + break; + default: + sprintf( str, " "); + break; + } + } + else + iphoneDrawNumber( 430-48, 50, currentMap.map + 1, 48, 48 ); + // par / time - DrawTime( 51, 63, levelstate.fpartime * 60 ); // fpartime is in minutes - DrawTime( 285, 63, levelstate.time / 60 ); // levelstate.time is in tics + int offset = 50; + DrawTime( 51, 63+offset, levelstate.fpartime * 60 ); // fpartime is in minutes + DrawTime( 285, 63+offset, levelstate.time / 60 ); // levelstate.time is in tics if ( levelstate.time/60 <= levelstate.fpartime * 60 ) { - iphoneDrawPic( 480 - 40, 63, 32, 32, "iphone/partime.tga" ); + iphoneDrawPic( 480 - 40, 63+offset, 32, 32, "iphone/partime.tga" ); } // ratios - DrawRatio( 124, levelstate.killed_monsters, levelstate.total_monsters, "iphone/kills.tga" ); - DrawRatio( 189, levelstate.found_secrets, levelstate.total_secrets, "iphone/secrets.tga" ); - DrawRatio( 255, levelstate.found_treasure, levelstate.total_treasure, "iphone/treasure.tga" ); + int correction = 8; + DrawRatio( 124+offset-1*correction, levelstate.killed_monsters, levelstate.total_monsters, "iphone/kills.tga" ); + DrawRatio( 189+offset-2*correction, levelstate.found_secrets, levelstate.total_secrets, "iphone/secrets.tga" ); + DrawRatio( 255+offset-3*correction, levelstate.found_treasure, levelstate.total_treasure, "iphone/treasure.tga" ); } /* @@ -816,9 +2980,34 @@ void iphoneIntermission() { PL_NextLevel( &Player ); - if( g_version->value == SPEAROFDESTINY ) { + if( g_version->value == SPEAROFDESTINY && currentMap.episode >= 6 && currentMap.episode < 9)//added the episode check... gsh) + { + //added all this leveling stuff... gsh + int currentLevel = currentMap.episode * 10 + currentMap.map; + nextLevel = currentLevel + 1; + switch (currentLevel) { + case 78: nextLevel = 64; break; //if tunnels secret was defeated goto tunnels boss +// case 79: nextLevel = 69; break; //if dungeons secret was defeated goto dungeons boss + case 79: nextLevel = 72; break; //if castle secret was defeated goto next castle map + default: break; + } + if (Player.playstate == ex_secretlevel) { + switch (currentLevel) { + case 63: nextLevel = 78; break; //if tunnels secret was earned goto tunnels secret + //case 68: nextLevel = 79; break; //if dungeons secret was earned goto dungeons secret + case 71: nextLevel = 79; break; //if castle secret was earned goto castle secret + default: break; + } + } + if (nextLevel >= 60 + 21) + { + // show ultimate victory + menuState = IPM_VICTORY; + Sound_StartBGTrack( "music/URAHERO.ogg", "music/URAHERO.ogg" ); + return; + } } - else + else if (currentMap.episode < 6) //gsh added the check if it's the original wolf3d episodes { int currentLevel = currentMap.episode * 10 + currentMap.map; @@ -864,6 +3053,14 @@ void iphoneIntermission() { } #endif } + else //they just defeated a custom map... gsh + { + // go back to the episode select screen + //menuState = IPM_VICTORY; + menuState = IPM_MAPS; + Sound_StartBGTrack( "music/URAHERO.ogg", "music/URAHERO.ogg" ); + return; + } iphoneStartMap( (nextLevel/10), (nextLevel%10), skill->value ); } @@ -880,6 +3077,7 @@ void iphoneVictory() { return; } menuState = IPM_EPISODE; +//gsh menuState = IPM_MAPSELECTOR; } /* @@ -1027,6 +3225,11 @@ void iphoneOpenAutomap() { } void iphoneAutomap() { + + //draw the new background... gsh + iphoneDrawPic(0, 0, 480, 320, "iphone/map_background.tga"); + + mapTile_t *mt; float px, py; float angle, c, s; @@ -1072,7 +3275,122 @@ void iphoneAutomap() { } - // todo -- double tap for center on player? + // gsh - double tap for center on player + static unsigned int timeTouchDown = 0; + static int numTaps = 0; + static unsigned int lastTapTime = 0; + static int tapX = 0; + static int tapY = 0; + static int zoom = 0; + static float TargetX = 0; + static float TargetY = 0; + static float TargetZoom = 0; + static int prevTapX = 0; + static int prevTapY = 0; + + //touch down + if ( numTouches == 1 && numPrevTouches == 0) { + timeTouchDown = Sys_Milliseconds(); + prevTapX = tapX; + prevTapY = tapY; + tapX = touches[0][0]; + tapY = touches[0][1]; + } + //touch up + if ( numTouches == 0 && numPrevTouches == 1 ) { + unsigned int currentTime = Sys_Milliseconds(); + + //check if time between last tap and current time is too long + if (Sys_Milliseconds() - lastTapTime > 500 || zoom) + numTaps = 0; + + //detect tap + if ( currentTime - timeTouchDown < 200 ) { + + //record tap time if first tap + if (numTaps < 1) + lastTapTime = Sys_Milliseconds(); + + ++numTaps; + /* + //center map on player + if (numTaps >= 2) { + numTaps = 0; + zoom = 1; + TargetX = Player.position.origin[0] / (float)TILEGLOBAL; + TargetY = Player.position.origin[1] / (float)TILEGLOBAL; + TargetZoom = scale; + }*/ + + //the taps need to be near each other to qualify as a double tap event + int dx = abs(tapX - prevTapX); + int dy = abs(tapY - prevTapY); + int maxTapSeparation = 60; + + //safari style double tap zoom on location! + if (numTaps >= 2 && dx < maxTapSeparation && dy < maxTapSeparation) { + numTaps = 0; + zoom = 1; + //need to translate tap/screen coordinate to map coordinate + + int Xrelative2screenorigin = tapX - 240; + int Yrelative2screenorigin = 160 - tapY; + float scaledX = Xrelative2screenorigin / scale; + float scaledY = Yrelative2screenorigin / scale; + float camSpaceX = scaledX + mapOrigin[0]; + float camSpaceY = scaledY + mapOrigin[1]; + + //mapOrigin[0] = camSpaceX; + //mapOrigin[1] = camSpaceY; + + TargetX = camSpaceX; + TargetY = camSpaceY; + + if (scale < 32) { //zoom half way in + //scale = 32; + TargetZoom = 32; + } + else { + //scale = 4; //zoom completely out + TargetZoom = 4; + } + + Cvar_SetValue( mapScale->name, scale ); + } + } + else { + //no tap... reset to 0 + numTaps = 0; + } + } + + if (zoom) + { + int invSpeed = 2;//3; + float dist = TargetX - mapOrigin[0]; + mapOrigin[0] += dist/invSpeed; + dist = TargetY - mapOrigin[1]; + mapOrigin[1] += dist/invSpeed; + + dist = TargetZoom - scale; + scale += dist/(2*invSpeed); //let's zoom a litle slower than we pan + + + Com_Printf("current: %f\n", mapOrigin[0]); + Com_Printf("target: %f\n", TargetX); + //Com_Printf("dist: %f\n\n", dist); + + float tolerance = 0.5f;//3; + if ( abs(mapOrigin[0] - TargetX) < tolerance && abs(mapOrigin[1] - TargetY) < tolerance && abs(scale - TargetZoom) < tolerance/2) + { + mapOrigin[0] = TargetX; + mapOrigin[1] = TargetY; + scale = TargetZoom; + zoom = 0; + } + + Cvar_SetValue( mapScale->name, scale ); + } // set up matrix for drawing in tile units @@ -1146,44 +3464,638 @@ void iphoneAutomap() { } // stats button for stats display - if ( iphoneDrawPicWithTouch( 480-64, 0, 64, 32, "iphone/stats.tga" ) ) { + //if ( iphoneDrawPicWithTouch( 64, 0, 64, 36, "iphone/stats.tga" ) ) { + if ( iphoneDrawPicWithTouch( 64, 0, 120, 36, "iphone/stats_large.tga" ) ) { menuState = IPM_STATS; } + //gsh + if ( MenuButton() ) { + menuState = IPM_MAIN; + } } void iphoneStats() { DrawIntermissionStats(); - - // require all touches off before the intermission can exit - if ( numTouches == 0 && hasReleased == 0 ) { - hasReleased = 1; - return; // don't let the TouchReleased immediately fire + + //gsh + if ( BackButton() ) { + menuState = IPM_AUTOMAP; } - if ( !hasReleased ) { - return; + if ( MenuButton() ) { + menuState = IPM_MAIN; } - - if ( !TouchReleased( 0, 0, 480, 320 ) ) { - return; - } - - menuState = IPM_AUTOMAP; } +#if SPEARSTOREKIT +//========================== +// iphoneDownloadSpearProgressBar +//gsh +//========================== +void iphoneDownloadSpearProgressBar() +{ + char str[80]; + float scale = 40 / 32.0; + int barHeight = 30; + + //calculate the percentage of data received + unsigned int percentDownloaded = totalDownloaded * 100 / DownloadFileSize; + + int y = barHeight + 150 + 10; + my_snprintf( str, sizeof(str), "Downloading: %i", dataAmount); + iphoneDrawText( 100, y, 16, 16, str ); + y += 17*scale; + my_snprintf( str, sizeof(str), "Percent Download: %i\%", percentDownloaded ); + iphoneDrawText( 100, y, 16, 16, str ); + y += 17*scale; + my_snprintf( str, sizeof(str), "Total Downloaded: %i", totalDownloaded ); + iphoneDrawText( 100, y, 16, 16, str ); + y += 17*scale; + my_snprintf(str, sizeof(str), "SOD will download and install itself."); + iphoneDrawText( 50, y, 16, 16, str ); + y += 17*scale; + my_snprintf(str, sizeof(str), "You have not been charged at this time."); + iphoneDrawText( 50, y, 16, 16, str ); + y += 17*scale; + + //draw "Spear of Destiny" at the top + iphoneDrawPic(90, 20, 256, 128, "iphone/spear_logo.tga"); + + //draw the progress bar + iphoneDrawPic( 40, 150, 400, barHeight, "iphone/menu_bar.tga" ); + + //draw the progress + colour4_t color = { 255, 0, 0, 100 }; + R_Draw_Blend( 40, 150, 4*percentDownloaded, barHeight, color ); +} +#endif + +//========================== +// iphoneDownloadMenu +//gsh +//========================== +char urlbuffer[1024]; +void iphoneDownloadMenu() +{ + char str[80]; + float scale = 40 / 32.0; + int barHeight = 30; + + //draw the background + iphoneDrawPic(0, 0, 480, 320, "iphone/download_background.tga"); + + //draw the url + iphoneCenterText(240, 120, urlbuffer); + //iphoneDrawTextInBox(RectMake(0, 120, 16, 16), 480, urlbuffer, RectMake(0, 120, 480, 32)); + + int y = barHeight + 150 + 10; + my_snprintf( str, sizeof(str), "Packet Size: %i", userDataAmount); + iphoneDrawText( 100, y, 16, 16, str ); + y += 17*scale; + + my_snprintf( str, sizeof(str), "Total Downloaded: %i", totalDownloaded ); + iphoneDrawText( 100, y, 16, 16, str ); + y += 17*scale; + + static int iterator = 5; + static int pos = 230; + + //int width = 60; + pos += iterator; + + if (pos > 480) + pos = -60; + + //draw the barrel + iphoneDrawPic(0, 150, 480, 34, "iphone/download_progress_bar.tga"); + + //draw the bullet! + iphoneDrawPic(pos, 150+34/2-22/2, 60, 22, "iphone/download_bullet.tga"); +} + +/* +//========================================== +// iphoneUpdateTriviaDrags() +// Updates the touches for the scrolling maps + // gsh +//========================================== + //TODO: delete this +void iphoneUpdateTriviaAndInstructionsDrags(int numEnteries, int x, int y, int w, int h, int spacing, int *dragPosition, int *dragVelocity) +{ + //Update drag/touch info + // int x = 0; + // int w = 480;// - skillSides - 64; + /* + if (TouchDown(x, y, w, h)) + { + if (!numPrevTouches) + prevTouches[0][0] = touches[0][0]; + else if (numTouches == 1) + *dragVelocity = touches[0][0] - prevTouches[0][0]; + }* + + + + + //update the drags + *dragPosition += *dragVelocity; + /* + //int rightBoundary = 240 - 2*width;//480/2 - (numPresets*width/2 + spacing); + if (*dragPosition < -(numTrivia-1)*480-10)//270-10)//(numPresets*(width+spacing))/2 ) + *dragPosition = -(numTrivia-1)*480-10;//270-10;//(numPresets*(width+spacing))/2; + else if (*dragPosition > x+10)//rightBoundary) + *dragPosition = x+10;//rightBoundary; + + if (*dragPosition < -(numTrivia-1)*480 + x)//270) + *dragVelocity = 2; + else if (*dragPosition > x) + *dragVelocity = -2; + * + + //retard the velocity + if (*dragVelocity > 0) + --*dragVelocity; + else if (*dragVelocity < 0) + ++*dragVelocity; + +}*/ + +//---------------------------------- +// DrawBlendInBox +// gsh +//----------------------------------- +void DrawBlendInBox(int x, int y, int w, int h, colour4_t color, int boxX, int boxW) +{ + if ( x + w < boxX ) + return; + else if ( x > boxX + boxW) + return; + + if ( x < boxX ) { + w = x + w - boxX; + x = boxX; + } + if ( x + w > boxX + boxW ) + w = boxX + boxW - x; + + R_Draw_Blend( x, y, w, h, color ); +} + +//---------------------------------- +// iphoneTriviaMenu +// gsh +//----------------------------------- +#if 0 +// TODO: delete this implementation of iphoneTriviaMenu +void iphoneTriviaMenu() +{ + //int numTrivia = 15; + + static char triviaText[][1024] = { + " ORIGINAL WOLF:\n\nThe original release of Wolfenstein 3D only contained 3 Episodes. Episodes 4, 5, and 6 were sold separately as \"The Nocturnal Missions\", and were intended to act as a prequel to the original trilogy of Episodes. Subsequent releases of the game included all 6 Episodes together.", + " BJ, THE MAN:\n\nWilliam Joseph Blazkowicz was born August 15, 1911, to Polish immigrants. Blazkowicz was a top spy for the Allied Forces, eventually receiving the Congressional Medal of Honor.", + " BOX ART:\n\nKen Rieger was the talented illustrator who created the cover art for Spear of Destiny, Wolfenstein3D and Commander Keen.", + " THE REAL SPEAR:\n\nThe Spear of Destiny, also known as The Spear or The Holy Lance, is believed to have been the weapon used to pierce the side of Jesus Christ at the Crucifixion. The Spear soon became a symbol of God's favor and those who possessed it were believed to be invincible.", + " THE SPEAR AND HITLER:\n\nHitler is believed to have acquired the Spear at the beginning of WWII. Near the end of that war, it is rumored that General Patton discovered the Spear and that the downfall of Germany began that day. It is fabled that the U.S. was destined to succeed Germany as the New World Power once ownership of the Spear changed hands.", + " DEATH DIMENSION:\n\nIt is said that Hitler made a pact with the Angel of Death, promising the souls of his Nazi soldiers, to protect the Spear of Destiny.", + " GOD MODE:\n\nIn the game, if you can access god mode, BJ's eyes will glow with eerie golden light.", + " A PRINCE:\n\nRobert Prince, wrote the music for Commander Keen, Wolfenstein 3-D, and Doom.", + " DELAYED DEVELOPMENT:\n\nIt is rumored that work on Spear of Destiny was hindered by the development team's time spent playing Fatal Fury (which Jay Wilbur called 'Fatal Productivity'), and Street Fighter II.", + " A MATTER OF DEGREES:\n\nJohn Carmack is known for his innovations in 3D graphics and rocketry but what is less known is that this self-taught engineer has no degrees - he dropped out of school after one semester.", + " THE CARMACK:\n\nJohn Carmack, one of the founders of id Software, preferred to work at a pizza joint and make games rather than go to college. His favorite programming food is pizza and diet coke.", + " DREAMING IN DIGITAL:\n\nDoom was reported to cause gamers to dream in pixels and lower productivity.", + " KEEPING IT IN THE FAMILY:\n\nBJ is Commander Keen's grandfather.", + " WHAT'S IN A NAME:\n\nThe original name for Doom was \"It's Green and Pissed\"", + " GIBS:\n\nGibs, pronounced with a hard 'g', is sometimes confused as 'jibs' due to its pronunciation. The term came from the imagery of flying chicken giblets, or gibs for short.", + " WOLFENSTEIN 3D:\n\nThe game was originally designed to have more complex gameplay. Though dragging bodies, stealth kills and uniforms were already programmed, it was decided that they slowed down the pace of the game and were removed.", + " ORIGINAL WOLF:\n\nThe original release of Wolfenstein 3D only contained 3 Episodes. Episodes 4, 5, and 6 were sold separately as \"The Nocturnal Missions\", and were intended to act as a prequel to the original trilogy of Episodes. Subsequent releases of the game included all 6 Episodes together.", + }; + + //get number of trivia + int numTrivia = sizeof(triviaText)/(sizeof(char) * 1024); + + iphoneDrawPic(0, 0, 480, 320, "iphone/submenus_background_image.tga"); + iphoneDrawPic(240-108*9/10, 0, 217*9/10, 50*9/10, "iphone/header_trivia.tga"); + + if (BackButton()) { + menuState = IPM_MORE; + return; + } + + if (MenuButton()) { + menuState = IPM_MAIN; + return; + } + + int startPosition = 85; + + //update the drags + static int dragPosition = 85; + static int dragVelocity = 0; + //static int currentLocation = 0; + // int y = 30; + + int y = 20; + int x = 0; + //if (revLand->value) + // y = -20; + + if (iphoneDrawPicWithTouch(9, y+60+16, 64, 64, "iphone/button_left.tga") ){//&& !dragVelocity) { + dragVelocity += -25;//-18; + + if (dragVelocity < -50) + dragVelocity = -50; +/* ++currentLocation; + if (currentLocation >= numTrivia) + currentLocation = 0;//numTrivia-1; +*/ } + if (iphoneDrawPicWithTouch(411, y+60+16, 64, 64, "iphone/button_right.tga") ){//&& !dragVelocity) { + dragVelocity += 25;//18; + if (dragVelocity > 50) + dragVelocity = 50; +/* --currentLocation; + if (currentLocation < 0) + currentLocation = numTrivia-1;//0; +*/ } + + int width = 305; + int spacing = 100;//175; + int size = 160;//180;//110; + + iphoneUpdateTriviaAndInstructionsDrags(numTrivia, startPosition, y+25, size*2, size - 53, 10, &dragPosition, &dragVelocity ); //might as well use the same drag functionality in controlmenu + + int minSpeed = 20; + if ((dragVelocity < minSpeed && dragVelocity > 0) || (dragVelocity > -minSpeed && dragVelocity < 0)) + { + //Com_Printf("(dragPosition-startPosition)\%480 = %i\n", (dragPosition-startPosition)%480); + + if (abs((dragPosition-startPosition)%(width+spacing)) < 25) + { + dragVelocity = 0; + int tx = dragPosition - startPosition; + int n = tx/(width+spacing); + dragPosition = n*(width+spacing) + startPosition; + + } + else + { + if (dragVelocity < 0) + dragVelocity = -minSpeed; + else + dragVelocity = minSpeed; + } + //if ((dragPosition-startPosition)%480 == 0) + // dragVelocity = 0; + } + /* + if (dragVelocity == 0 ) + { + int diff = dragPosition - startPosition; + int n = diff/(width+spacing); + int nodePosition = n*(width+spacing); + + if ( diff != nodePosition ) + dragVelocity++; + }*/ + + //int upperLimit = startPosition + (numTrivia-1)*480; + int lowerLimit = startPosition - (numTrivia-1)*(width+spacing); + + + + + +// int x = 0; + y = 50; + int w = 305; + int h = 250; + colour4_t color = { 0, 0, 0, 255 }; + for (int i = 0; i < numTrivia; ++i) + {/* + if (i < currentLocation - 1) + continue; + if (i > currentLocation + 1) + continue;*/ +// x = dragPosition + 480*i; + /* + if (i == 0 && currentLocation == 0 && dragPosition < -480) + x = dragPosition + numTrivia*480; + else if (i == (numTrivia - 1) && currentLocation == (numTrivia - 1) && dragPosition > -480) + x = dragPosition - 480; + */ + /* + if ( x > upperLimit )//startPosition + (numTrivia-1)*480 ) + x -= numTrivia*480; + + if ( x < lowerLimit )//startPosition - (numTrivia-1)*480 ) + x += numTrivia*480; + */ + if (dragPosition > startPosition )//+480)//upperLimit )//startPosition + numTrivia*480) + dragPosition = lowerLimit;//startPosition; + + if (dragPosition < lowerLimit )//startPosition - numTrivia*480) + dragPosition = startPosition; + + x = dragPosition + (width+spacing)*i; + + if (x > 480 || x + width < 0) + continue; + + DrawBlendInBox( x, y, w, h, color, 71, 410-70); + //iphoneDrawTextInBox(RectMake(x, y+16, 16, 16), 410-70-32-16, triviaText[i], RectMake(71, 0, 320, 410-70)); + iphoneDrawArialTextInBox(RectMake(x, y+16, 16, 16), 410-70-32-16, triviaText[i], RectMake(71, 0, 320, 410-70)); + } +} +#endif + +//This is a different implementation of the one above +//gsh +void iphoneTriviaMenu() +{ + static int currentTrivia = 0; //this represents which trivia item we are currently supposed to be on + + static char triviaText[][1024] = { + " ORIGINAL WOLF:\n\nThe original release of Wolfenstein 3D only contained 3 Episodes. Episodes 4, 5, and 6 were sold separately as \"The Nocturnal Missions\", and were intended to act as a prequel to the original trilogy of Episodes. Subsequent releases of the game included all 6 Episodes together.", + " BJ, THE MAN:\n\nWilliam Joseph Blazkowicz was born August 15, 1911, to Polish immigrants. Blazkowicz was a top spy for the Allied Forces, eventually receiving the Congressional Medal of Honor.", + " BOX ART:\n\nKen Rieger was the talented illustrator who created the cover art for Spear of Destiny, Wolfenstein3D and Commander Keen.", + " THE REAL SPEAR:\n\nThe Spear of Destiny, also known as The Spear or The Holy Lance, is believed to have been the weapon used to pierce the side of Jesus Christ at the Crucifixion. The Spear soon became a symbol of God's favor and those who possessed it were believed to be invincible.", + " THE SPEAR AND HITLER:\n\nHitler is believed to have acquired the Spear at the beginning of WWII. Near the end of that war, it is rumored that General Patton discovered the Spear and that the downfall of Germany began that day. It is fabled that the U.S. was destined to succeed Germany as the New World Power once ownership of the Spear changed hands.", + " DEATH DIMENSION:\n\nIt is said that Hitler made a pact with the Angel of Death, promising the souls of his Nazi soldiers, to protect the Spear of Destiny.", + " GOD MODE:\n\nIn the game, if you can access god mode, BJ's eyes will glow with eerie golden light.", + " A PRINCE:\n\nRobert Prince, wrote the music for Commander Keen, Wolfenstein 3-D, and Doom.", + " DELAYED DEVELOPMENT:\n\nIt is rumored that work on Spear of Destiny was hindered by the development team's time spent playing Fatal Fury (which Jay Wilbur called 'Fatal Productivity'), and Street Fighter II.", + " A MATTER OF DEGREES:\n\nJohn Carmack is known for his innovations in 3D graphics and rocketry but what is less known is that this self-taught engineer has no degrees - he dropped out of school after one semester.", + " THE CARMACK:\n\nJohn Carmack, one of the founders of id Software, preferred to work at a pizza joint and make games rather than go to college. His favorite programming food is pizza and diet coke.", + " DREAMING IN DIGITAL:\n\nDoom was reported to cause gamers to dream in pixels and lower productivity.", + " KEEPING IT IN THE FAMILY:\n\nBJ is Commander Keen's grandfather.", + " WHAT'S IN A NAME:\n\nThe original name for Doom was \"It's Green and Pissed\"", + " GIBS:\n\nGibs, pronounced with a hard 'g', is sometimes confused as 'jibs' due to its pronunciation. The term came from the imagery of flying chicken giblets, or gibs for short.", + " WOLFENSTEIN 3D:\n\nThe game was originally designed to have more complex gameplay. Though dragging bodies, stealth kills and uniforms were already programmed, it was decided that they slowed down the pace of the game and were removed.", + " ORIGINAL WOLF:\n\nThe original release of Wolfenstein 3D only contained 3 Episodes. Episodes 4, 5, and 6 were sold separately as \"The Nocturnal Missions\", and were intended to act as a prequel to the original trilogy of Episodes. Subsequent releases of the game included all 6 Episodes together.", + }; + //get number of trivia + int numTrivia = sizeof(triviaText)/(sizeof(char) * 1024); + + iphoneDrawPic(0, 0, 480, 320, "iphone/submenus_background_image.tga"); + iphoneDrawPic(240-108*9/10, 0, 217*9/10, 50*9/10, "iphone/header_trivia.tga"); + + if (BackButton()) { + menuState = IPM_MORE; + return; + } + + if (MenuButton()) { + menuState = IPM_MAIN; + return; + } + + int startPosition = 85; + + //update the drags + static int Position = 85; + //static int Velocity = 0; + //static int currentLocation = 0; + // int y = 30; + + int y = 20; + int x = 0; + + static int moveRight = 0; + + //move right or left, depending on button pushed + if (iphoneDrawPicWithTouch(9, y+60+16, 64, 64, "iphone/button_left.tga") ) { + moveRight = 0; + ++currentTrivia; + if (currentTrivia > numTrivia-1) { + currentTrivia = 1; + + } + } + if (iphoneDrawPicWithTouch(411, y+60+16, 64, 64, "iphone/button_right.tga") ) { + moveRight = 1; + --currentTrivia; + if (currentTrivia < 0) { + currentTrivia = numTrivia-2; + } + } + + + int width = 305; + int spacing = 215;//175; + + int destinationPosition = startPosition - currentTrivia*(width+spacing); + int nearestTrivia = -(Position - startPosition)/(width+spacing); + + //if we are not where we are supposed to be... get us there + if (Position != destinationPosition) + { + Com_Printf("\n\nPosition: %i\n", Position); + Com_Printf("nearestTrivia: %i\n", nearestTrivia); + Com_Printf("currentTrivia: %i\n", currentTrivia); + Com_Printf("destinationPosition: %i\n", destinationPosition); + + + //now we must check for looping situations (where it would be faster to go backwards + + //this works great for traveling in a straight line without looping + + int oldPosition = Position; + int deltaPos = destinationPosition - Position; + /* + if (deltaPos > 0) + Position += 50; + else + Position -= 50; + */ + int moveDistance = 50; + + if ( moveRight )//!crossedOver )//currentTrivia - nearestTrivia >= 0)//abs(currentTrivia - nearestTrivia) > numTrivia/2) + { + //if (deltaPos < 0) + // moveDistance *= -1; + + Position += moveDistance; + + if (destinationPosition < Position && destinationPosition > oldPosition) + Position = destinationPosition; + } + else //if (currentTrivia - nearestTrivia < 0) + { + + //if (deltaPos > 0) + // moveDistance *= -1; + + Position -= moveDistance; + + if (destinationPosition > Position && destinationPosition < oldPosition) + Position = destinationPosition; + + } + + /* + //check if the current position has crossed over the destination position + if (destinationPosition < Position && destinationPosition > oldPosition) + Position = destinationPosition; + */ + + if ( abs(deltaPos) <= 100) + Position = destinationPosition; + } + + //int upperLimit = startPosition + (numTrivia-1)*480; + int lowerLimit = startPosition - (numTrivia-1)*(width+spacing); + + + + + + // int x = 0; + y = 50; + int w = 305; + int h = 250; + colour4_t color = { 0, 0, 0, 255 }; + colour4_t colorWhite = { 255, 255, 255, 255 }; + colour4_t colorGrey = { 100, 100, 100, 255 }; + for (int i = 0; i < numTrivia; ++i) + { + if (Position > startPosition )//+480)//upperLimit )//startPosition + numTrivia*480) + Position = lowerLimit;//startPosition; + + if (Position < lowerLimit )//startPosition - numTrivia*480) + Position = startPosition;// - (width+spacing); + + x = Position + (width+spacing)*i; + + if (x > 480 || x + width < 0) + continue; + + DrawBlendInBox( x-2, y-2, w+4, h+4, colorWhite, 71, 410-70); + DrawBlendInBox( x-1, y-1, w+2, h+2, colorGrey, 71, 410-70); + DrawBlendInBox( x, y, w, h, color, 71, 410-70); + //iphoneDrawTextInBox(RectMake(x, y+16, 16, 16), 410-70-32-16, triviaText[i], RectMake(71, 0, 320, 410-70)); + iphoneDrawArialTextInBox(RectMake(x, y+16, 16, 16), 410-70-32-16, triviaText[i], RectMake(71, 0, 320, 410-70)); + } +} + +//---------------------------------- +// iphoneMoreMenu +// gsh +//----------------------------------- +void iphoneMoreMenu() +{ + wasDLInstructionsCalledFromEpisodeMenu = 0; + + iphoneDrawPic(0, 0, 480, 320, "iphone/submenus_background_image.tga"); + iphoneDrawPic(240-108*9/10, 0, 217*9/10, 50*9/10, "iphone/header_more.tga"); + + float scale = 40 / 32.0; + + if (BackButton()) { + menuState = IPM_MAIN; + } + + if (iphoneDrawPicWithTouch(240-64*scale, 120, 128*scale, 32*scale, "iphone/button_trivia.tga")) { + menuState = IPM_TRIVIA; + } + + if (iphoneDrawPicWithTouch(240-64*scale, 170, 128*scale, 32*scale, "iphone/button_downloads.tga")) { + menuState = IPM_DOWNLOADINSTRUCTIONS; + } +} + +//---------------------------------- +// iphoneDownloadInstructionsMenu +// gsh +//----------------------------------- +void iphoneDownloadInstructionsMenu() +{ + iphoneDrawPic(0, 0, 480, 320, "iphone/submenus_background_image.tga"); + iphoneDrawPic(240-108*9/10, 0, 217*9/10, 50*9/10, "iphone/header_downloads.tga"); + + if (BackButton()) + { + menuState = IPM_MORE; + if (wasDLInstructionsCalledFromEpisodeMenu) + menuState = IPM_EPISODE; + + return; + } + + if (MenuButton()) { + menuState = IPM_MAIN; + return; + } + /* + if ( iphoneDrawPicWithTouch( 64, 0, 64, 36, "iphone/button_web.tga" ) ) { + wasCalledFromDownloadInstructionsMenu = 1; + iphoneYesNoBox("Website", "This will navigate you to idsoftware.com/iphone/wolf3dInstructions.html. Continue?"); + }*/ + + char instructions[] = "\nWolfenstein Platinum allows you to download additional game levels from the Internet." + //" For details, go to:\n\nhttp://www.idsoftware.com/iphone/\nwolf3dInstructions.html"; + " For details, go to:\n\nhttp://www.idsoftware.com/wolfenstein\n" + "-3d-classic-platinum/mapinstructions/"; + + int x = 20; + int y = 100; + int w = 480-40; + int h = 150; + colour4_t color = { 0, 0, 0, 255 }; + colour4_t colorGrey = { 100, 100, 100, 255 }; + colour4_t colorWhite = { 255, 255, 255, 255 }; + + + //draw a background, behind the text, with a white border + R_Draw_Blend( x-2, y-2, w+4, h+4, colorWhite); + R_Draw_Blend( x-1, y-1, w+2, h+2, colorGrey); + R_Draw_Blend( x, y, w, h, color); + + //draw arial text + iphoneDrawArialTextInBox(RectMake(x, y+16, 16, 16), 400, instructions, RectMake(10, 0, 480-40, 320)); + + //check for touch events on the text itself + int r = TouchReleased( x, y, w, h ); + if ( r ) { + wasCalledFromDownloadInstructionsMenu = 1; + iphoneYesNoBox("Website", "This will navigate you to idsoftware.com/wolfenstein-3d-classic-platinum/mapinstructions/ Continue?"); + } + if ( TouchDown( x, y, w, h ) ) { + colour4_t colorLight = { 255, 255, 255, 64 }; + R_Draw_Blend( x, y, w, h, colorLight ); + } +} + + + +//gsh +extern void iphoneSelectMapMenu(); +#if SPEARSTOREKIT +extern void iphoneStoreKit(); +#endif + void iphoneDrawMenus() { - iphoneDrawPic( 0, 0, 480, 320, "iphone/background_1.tga" ); +// iphoneDrawPic( 0, 0, 480, 320, "iphone/background_main_sepia.tga" ); + iphoneDrawPic( 0, 0, 480, 320, "iphone/background_main_hued.tga" ); //gsh switch ( menuState ) { case IPM_MAIN: iphoneMainMenu(); break; case IPM_SKILL: iphoneSkillMenu(); break; - case IPM_EPISODE: iphoneEpisodeMenu(); break; - case IPM_MAPS: iphoneMapMenu(); break; + case IPM_EPISODE: iphoneScrollingEpisodeMenu(); break;//gsh //iphoneEpisodeMenu(); break; + case IPM_MAPS: iphoneScrollingMapMenu(); break; //gsh iphoneMapMenu(); break; + // case IPM_MAPSELECTOR: iphoneSelectMapMenu(); break; //gsh case IPM_CONTROLS: iphoneControlMenu(); break; case IPM_INTERMISSION: iphoneIntermission(); break; case IPM_VICTORY: iphoneVictory(); break; case IPM_AUTOMAP: iphoneAutomap(); break; case IPM_STATS: iphoneStats(); break; - case IPM_HUDEDIT: HudEditFrame(); break; + case IPM_HUDEDIT: iphoneHudEditFrame(); break;//gsh HudEditFrame(); break; + case IPM_DOWNLOADPROGRESS: iphoneDownloadMenu(); break; //gsh +#if SPEARSTOREKIT + case IPM_STOREKIT: iphoneStoreKit(); break; //gsh +#endif +// case IPM_LOADING: iphoneLoadingMenu(); //gsh + case IPM_TRIVIA: iphoneTriviaMenu(); break;//gsh + case IPM_MORE: iphoneMoreMenu(); break;//gsh + case IPM_DOWNLOADINSTRUCTIONS: iphoneDownloadInstructionsMenu(); break; //gsh } } diff --git a/wolf3d/code/iphone/iphone_wolf.h b/wolf3d/code/iphone/iphone_wolf.h index ef1d43e..06f0290 100644 --- a/wolf3d/code/iphone/iphone_wolf.h +++ b/wolf3d/code/iphone/iphone_wolf.h @@ -23,7 +23,7 @@ //#define EPISODE_ONE_ONLY // this is the version number displayed on the menu screen -#define WOLF_IPHONE_VERSION 1.1 +#define WOLF_IPHONE_VERSION 1.2//1.1 extern viddef_t viddef; @@ -33,13 +33,19 @@ typedef enum menuState { IPM_SKILL, IPM_EPISODE, IPM_MAPS, + IPM_MAPSELECTOR, //gsh IPM_CONTROLS, IPM_INTERMISSION, IPM_VICTORY, IPM_AUTOMAP, IPM_STATS, - IPM_HUDEDIT - + IPM_HUDEDIT, + IPM_DOWNLOADPROGRESS, //gsh + IPM_STOREKIT, //gsh +// IPM_LOADING, //gsh + IPM_TRIVIA, //gsh + IPM_MORE, //gsh + IPM_DOWNLOADINSTRUCTIONS, //gsh } menuState_t; extern menuState_t menuState; @@ -51,7 +57,7 @@ void iphoneDrawMenus(); #define SAVEGAME_VERSION 108 #define MAX_SKILLS 4 -#define MAX_MAPS 60 +#define MAX_MAPS 81 //60 changed by gsh to allow for SOD levels #define MF_TRIED 1 #define MF_COMPLETED 2 @@ -69,15 +75,25 @@ typedef struct { int mapFlags[MAX_SKILLS][MAX_MAPS]; } currentMap_t; +//gsh +typedef struct { + int x; + int y; + int width; + int height; +} rect_t; +rect_t RectMake(int x, int y, int width, int height); + extern currentMap_t currentMap; void iphoneStartMap( int episodeNum, int mapNum, int skillLevel ); +void iphoneStartUserMap( int episodeNum, int mapNum, int skillLevel, char *mapName ); //gsh extern char iphoneDocDirectory[1024]; extern char iphoneAppDirectory[1024]; extern texture_t *numberPics[10]; -#define NUM_MUGSHOTS 23 +#define NUM_MUGSHOTS 26//23 extern char *mugshotnames[ NUM_MUGSHOTS ]; extern vec3_t vnull; @@ -100,6 +116,13 @@ extern cvar_t *tiltMove; extern cvar_t *tiltDeadBand; extern cvar_t *tiltAverages; extern cvar_t *tiltFire; +#ifdef VOLUMEHACK +extern cvar_t *volumeFireUp; //gsh +extern cvar_t *volumeFireDown; //gsh +extern cvar_t *volumeFireUpSetting; //gsh +extern cvar_t *volumeFireDownSetting; //gsh +#endif +extern cvar_t *hudAlpha; //gsh extern cvar_t *music; extern cvar_t *showTilt; extern cvar_t *showTime; @@ -115,6 +138,7 @@ extern cvar_t *autoFire; // the native iPhone code should set the following each frame: extern int numTouches; extern int touches[5][2]; // [0] = x, [1] = y in landscape mode, raster order with y = 0 at top +extern int isTouchMoving; //gsh extern float tilt; // -1.0 to 1.0 extern float tiltPitch; @@ -135,6 +159,14 @@ void LoadWallTexture( int wallPicNum ); int TouchDown( int x, int y, int w, int h ); int TouchReleased( int x, int y, int w, int h ); int iphoneCenterText( int x, int y, const char *str ); +void iphoneCenterTextWithColor( int x, int y, const char *str, colour4_t color ); //gsh +int iphoneDrawText( int x, int y, int width, int height, const char *str ); //gsh +int iphoneDrawArialText( int x, int y, float scale, const char *str ); //gsh +int iphoneCenterArialText( int x, int y, float scale, const char *str ); //gsh +int iphoneDrawArialTextInBox( rect_t paragraph, int lineLength, const char *str, rect_t boxRect ); //gsh +void iphoneDrawTextWithColor( rect_t rect, const char *str, float colors[4] ); //gsh +void iphoneDrawMapName( rect_t rect, const char *str ); //gsh +int iphoneDrawTextInBox( rect_t paragraph, int lineLength, const char *str, rect_t boxRect ); //gsh void iphoneDrawNumber( int x, int y, int number, int charWidth, int charHeight ); void iphoneDrawPic( int x, int y, int w, int h, const char *pic ); int iphoneDrawPicWithTouch( int x, int y, int w, int h, const char *pic ); @@ -166,6 +198,12 @@ typedef struct { int width, height; int glTexNum; int hudFlags; + int drawWidth, drawHeight; //from here down (in the struct) is from doom iphone... gsh + int touchWidth, touchHeight; + texture_t *texture; + bool drawAsLimit; + float touchState; + //struct touch_s *touch; } hudPic_t; //#define ALLOW_MAP_VIEW_HUD @@ -188,6 +226,7 @@ extern hud_t huds; void HudSetForScheme( int schemeNum ); void HudSetTexnums(); void HudEditFrame(); +void iphoneHudEditFrame(); @@ -199,6 +238,7 @@ void iphoneStartDamageFlash( int points ); void iphoneSetAttackDirection( int dir ); void iphoneStartIntermission( int framesFromNow ); void iphoneSetNotifyText( const char *str, ... ); +void iphoneSetLevelNotifyText(); //gsh //--------------------------------------- // interfaces to Objective-C land diff --git a/wolf3d/code/iphone/wolf3d.xcodeproj/greghodges.mode1v3 b/wolf3d/code/iphone/wolf3d.xcodeproj/greghodges.mode1v3 new file mode 100644 index 0000000..6f7c124 --- /dev/null +++ b/wolf3d/code/iphone/wolf3d.xcodeproj/greghodges.mode1v3 @@ -0,0 +1,1409 @@ + + + + + ActivePerspectiveName + Project + AllowedModules + + + BundleLoadPath + + MaxInstances + n + Module + PBXSmartGroupTreeModule + Name + Groups and Files Outline View + + + BundleLoadPath + + MaxInstances + n + Module + PBXNavigatorGroup + Name + Editor + + + BundleLoadPath + + MaxInstances + n + Module + XCTaskListModule + Name + Task List + + + BundleLoadPath + + MaxInstances + n + Module + XCDetailModule + Name + File and Smart Group Detail Viewer + + + BundleLoadPath + + MaxInstances + 1 + Module + PBXBuildResultsModule + Name + Detailed Build Results Viewer + + + BundleLoadPath + + MaxInstances + 1 + Module + PBXProjectFindModule + Name + Project Batch Find Tool + + + BundleLoadPath + + MaxInstances + n + Module + XCProjectFormatConflictsModule + Name + Project Format Conflicts List + + + BundleLoadPath + + MaxInstances + n + Module + PBXBookmarksModule + Name + Bookmarks Tool + + + BundleLoadPath + + MaxInstances + n + Module + PBXClassBrowserModule + Name + Class Browser + + + BundleLoadPath + + MaxInstances + n + Module + PBXCVSModule + Name + Source Code Control Tool + + + BundleLoadPath + + MaxInstances + n + Module + PBXDebugBreakpointsModule + Name + Debug Breakpoints Tool + + + BundleLoadPath + + MaxInstances + n + Module + XCDockableInspector + Name + Inspector + + + BundleLoadPath + + MaxInstances + n + Module + PBXOpenQuicklyModule + Name + Open Quickly Tool + + + BundleLoadPath + + MaxInstances + 1 + Module + PBXDebugSessionModule + Name + Debugger + + + BundleLoadPath + + MaxInstances + 1 + Module + PBXDebugCLIModule + Name + Debug Console + + + BundleLoadPath + + MaxInstances + n + Module + XCSnapshotModule + Name + Snapshots Tool + + + BundlePath + /Developer/Library/PrivateFrameworks/DevToolsInterface.framework/Resources + Description + DefaultDescriptionKey + DockingSystemVisible + + Extension + mode1v3 + FavBarConfig + + PBXProjectModuleGUID + ED0A8D011069AA54001E0547 + XCBarModuleItemNames + + XCBarModuleItems + + + FirstTimeWindowDisplayed + + Identifier + com.apple.perspectives.project.mode1v3 + MajorVersion + 33 + MinorVersion + 0 + Name + Default + Notifications + + OpenEditors + + PerspectiveWidths + + -1 + -1 + + Perspectives + + + ChosenToolbarItems + + active-combo-popup + action + NSToolbarFlexibleSpaceItem + build-and-go + com.apple.ide.PBXToolbarStopButton + get-info + NSToolbarFlexibleSpaceItem + com.apple.pbx.toolbar.searchfield + + ControllerClassBaseName + + IconName + WindowOfProjectWithEditor + Identifier + perspective.project + IsVertical + + Layout + + + ContentConfiguration + + PBXBottomSmartGroupGIDs + + 1C37FBAC04509CD000000102 + 1C37FAAC04509CD000000102 + 1C08E77C0454961000C914BD + 1C37FABC05509CD000000102 + 1C37FABC05539CD112110102 + E2644B35053B69B200211256 + 1C37FABC04509CD000100104 + 1CC0EA4004350EF90044410B + 1CC0EA4004350EF90041110B + + PBXProjectModuleGUID + 1CE0B1FE06471DED0097A5F4 + PBXProjectModuleLabel + Files + PBXProjectStructureProvided + yes + PBXSmartGroupTreeModuleColumnData + + PBXSmartGroupTreeModuleColumnWidthsKey + + 186 + + PBXSmartGroupTreeModuleColumnsKey_v4 + + MainColumn + + + PBXSmartGroupTreeModuleOutlineStateKey_v7 + + PBXSmartGroupTreeModuleOutlineStateExpansionKey + + 29B97314FDCFA39411CA2CEA + 29B97315FDCFA39411CA2CEA + 29B97317FDCFA39411CA2CEA + 29B97323FDCFA39411CA2CEA + 19C28FACFE9D520D11CA2CBB + 1C37FBAC04509CD000000102 + 1C37FABC05509CD000000102 + + PBXSmartGroupTreeModuleOutlineStateSelectionKey + + + 32 + 27 + 0 + + + PBXSmartGroupTreeModuleOutlineStateVisibleRectKey + {{0, 0}, {186, 720}} + + PBXTopSmartGroupGIDs + + XCIncludePerspectivesSwitch + + XCSharingToken + com.apple.Xcode.GFSharingToken + + GeometryConfiguration + + Frame + {{0, 0}, {203, 738}} + GroupTreeTableConfiguration + + MainColumn + 186 + + RubberWindowFrame + 391 174 1010 779 0 0 1680 1028 + + Module + PBXSmartGroupTreeModule + Proportion + 203pt + + + Dock + + + ContentConfiguration + + PBXProjectModuleGUID + 1CE0B20306471E060097A5F4 + PBXProjectModuleLabel + Info.plist + PBXSplitModuleInNavigatorKey + + Split0 + + PBXProjectModuleGUID + 1CE0B20406471E060097A5F4 + PBXProjectModuleLabel + Info.plist + _historyCapacity + 0 + bookmark + ED45402710B21F0900DCFA32 + history + + ED2E5C05106C886700F57B9D + ED2E5C06106C886700F57B9D + ED4AC98010742365000706D8 + EDDA5A7E107BA97F003F55D3 + EDDA5A80107BA97F003F55D3 + ED2754531085141B009C891E + ED0653B61087719F00E5450B + EDAFC5A2109A14EE002C3487 + ED845AB1109F5B3500F673AC + ED45402110B21F0900DCFA32 + ED45402210B21F0900DCFA32 + ED45402310B21F0900DCFA32 + ED845AB2109F5B3500F673AC + + prevStack + + ED3703CD106C20620059C5F8 + ED2E5C0A106C886700F57B9D + ED2E5C0B106C886700F57B9D + ED4AC98210742365000706D8 + ED4AC9AB10743318000706D8 + ED4AC9AC10743318000706D8 + EDDA5A82107BA97F003F55D3 + EDDA5A83107BA97F003F55D3 + EDDA5A84107BA97F003F55D3 + ED2754551085141B009C891E + ED2754561085141B009C891E + ED0653B81087719F00E5450B + ED45402410B21F0900DCFA32 + ED45402510B21F0900DCFA32 + ED45402610B21F0900DCFA32 + + + SplitCount + 1 + + StatusBarVisibility + + + GeometryConfiguration + + Frame + {{0, 0}, {802, 686}} + RubberWindowFrame + 391 174 1010 779 0 0 1680 1028 + + Module + PBXNavigatorGroup + Proportion + 686pt + + + ContentConfiguration + + PBXProjectModuleGUID + 1CE0B20506471E060097A5F4 + PBXProjectModuleLabel + Detail + + GeometryConfiguration + + Frame + {{0, 691}, {802, 47}} + RubberWindowFrame + 391 174 1010 779 0 0 1680 1028 + + Module + XCDetailModule + Proportion + 47pt + + + Proportion + 802pt + + + Name + Project + ServiceClasses + + XCModuleDock + PBXSmartGroupTreeModule + XCModuleDock + PBXNavigatorGroup + XCDetailModule + + TableOfContents + + ED45402810B21F0900DCFA32 + 1CE0B1FE06471DED0097A5F4 + ED45402910B21F0900DCFA32 + 1CE0B20306471E060097A5F4 + 1CE0B20506471E060097A5F4 + + ToolbarConfiguration + xcode.toolbar.config.defaultV3 + + + ControllerClassBaseName + + IconName + WindowOfProject + Identifier + perspective.morph + IsVertical + 0 + Layout + + + BecomeActive + 1 + ContentConfiguration + + PBXBottomSmartGroupGIDs + + 1C37FBAC04509CD000000102 + 1C37FAAC04509CD000000102 + 1C08E77C0454961000C914BD + 1C37FABC05509CD000000102 + 1C37FABC05539CD112110102 + E2644B35053B69B200211256 + 1C37FABC04509CD000100104 + 1CC0EA4004350EF90044410B + 1CC0EA4004350EF90041110B + + PBXProjectModuleGUID + 11E0B1FE06471DED0097A5F4 + PBXProjectModuleLabel + Files + PBXProjectStructureProvided + yes + PBXSmartGroupTreeModuleColumnData + + PBXSmartGroupTreeModuleColumnWidthsKey + + 186 + + PBXSmartGroupTreeModuleColumnsKey_v4 + + MainColumn + + + PBXSmartGroupTreeModuleOutlineStateKey_v7 + + PBXSmartGroupTreeModuleOutlineStateExpansionKey + + 29B97314FDCFA39411CA2CEA + 1C37FABC05509CD000000102 + + PBXSmartGroupTreeModuleOutlineStateSelectionKey + + + 0 + + + PBXSmartGroupTreeModuleOutlineStateVisibleRectKey + {{0, 0}, {186, 337}} + + PBXTopSmartGroupGIDs + + XCIncludePerspectivesSwitch + 1 + XCSharingToken + com.apple.Xcode.GFSharingToken + + GeometryConfiguration + + Frame + {{0, 0}, {203, 355}} + GroupTreeTableConfiguration + + MainColumn + 186 + + RubberWindowFrame + 373 269 690 397 0 0 1440 878 + + Module + PBXSmartGroupTreeModule + Proportion + 100% + + + Name + Morph + PreferredWidth + 300 + ServiceClasses + + XCModuleDock + PBXSmartGroupTreeModule + + TableOfContents + + 11E0B1FE06471DED0097A5F4 + + ToolbarConfiguration + xcode.toolbar.config.default.shortV3 + + + PerspectivesBarVisible + + ShelfIsVisible + + SourceDescription + file at '/Developer/Library/PrivateFrameworks/DevToolsInterface.framework/Resources/XCPerspectivesSpecificationMode1.xcperspec' + StatusbarIsVisible + + TimeStamp + 0.0 + ToolbarDisplayMode + 1 + ToolbarIsVisible + + ToolbarSizeMode + 1 + Type + Perspectives + UpdateMessage + The Default Workspace in this version of Xcode now includes support to hide and show the detail view (what has been referred to as the "Metro-Morph" feature). You must discard your current Default Workspace settings and update to the latest Default Workspace in order to gain this feature. Do you wish to update to the latest Workspace defaults for project '%@'? + WindowJustification + 5 + WindowOrderList + + /Users/greghodges/wolf3dplatinum/wolf3d/code/iphone/wolf3d.xcodeproj + + WindowString + 391 174 1010 779 0 0 1680 1028 + WindowToolsV3 + + + FirstTimeWindowDisplayed + + Identifier + windowTool.build + IsVertical + + Layout + + + Dock + + + ContentConfiguration + + PBXProjectModuleGUID + 1CD0528F0623707200166675 + PBXProjectModuleLabel + + StatusBarVisibility + + + GeometryConfiguration + + Frame + {{0, 0}, {500, 218}} + RubberWindowFrame + 56 488 500 500 0 0 1680 1028 + + Module + PBXNavigatorGroup + Proportion + 218pt + + + ContentConfiguration + + PBXProjectModuleGUID + XCMainBuildResultsModuleGUID + PBXProjectModuleLabel + Build + XCBuildResultsTrigger_Collapse + 1021 + XCBuildResultsTrigger_Open + 1011 + + GeometryConfiguration + + Frame + {{0, 223}, {500, 236}} + RubberWindowFrame + 56 488 500 500 0 0 1680 1028 + + Module + PBXBuildResultsModule + Proportion + 236pt + + + Proportion + 459pt + + + Name + Build Results + ServiceClasses + + PBXBuildResultsModule + + StatusbarIsVisible + + TableOfContents + + ED0A8CF61069A9E0001E0547 + EDAFC5AA109A14EE002C3487 + 1CD0528F0623707200166675 + XCMainBuildResultsModuleGUID + + ToolbarConfiguration + xcode.toolbar.config.buildV3 + WindowString + 56 488 500 500 0 0 1680 1028 + WindowToolGUID + ED0A8CF61069A9E0001E0547 + WindowToolIsVisible + + + + FirstTimeWindowDisplayed + + Identifier + windowTool.debugger + IsVertical + + Layout + + + Dock + + + ContentConfiguration + + Debugger + + HorizontalSplitView + + _collapsingFrameDimension + 0.0 + _indexOfCollapsedView + 0 + _percentageOfCollapsedView + 0.0 + isCollapsed + yes + sizes + + {{0, 0}, {316, 203}} + {{316, 0}, {378, 203}} + + + VerticalSplitView + + _collapsingFrameDimension + 0.0 + _indexOfCollapsedView + 0 + _percentageOfCollapsedView + 0.0 + isCollapsed + yes + sizes + + {{0, 0}, {694, 203}} + {{0, 203}, {694, 178}} + + + + LauncherConfigVersion + 8 + PBXProjectModuleGUID + 1C162984064C10D400B95A72 + PBXProjectModuleLabel + Debug - GLUTExamples (Underwater) + + GeometryConfiguration + + DebugConsoleVisible + None + DebugConsoleWindowFrame + {{200, 200}, {500, 300}} + DebugSTDIOWindowFrame + {{200, 200}, {500, 300}} + Frame + {{0, 0}, {694, 381}} + PBXDebugSessionStackFrameViewKey + + DebugVariablesTableConfiguration + + Name + 120 + Value + 85 + Summary + 148 + + Frame + {{316, 0}, {378, 203}} + RubberWindowFrame + 150 519 694 422 0 0 1680 1028 + + RubberWindowFrame + 150 519 694 422 0 0 1680 1028 + + Module + PBXDebugSessionModule + Proportion + 381pt + + + Proportion + 381pt + + + Name + Debugger + ServiceClasses + + PBXDebugSessionModule + + StatusbarIsVisible + + TableOfContents + + 1CD10A99069EF8BA00B06720 + EDAFC5AB109A14EE002C3487 + 1C162984064C10D400B95A72 + EDAFC5AC109A14EE002C3487 + EDAFC5AD109A14EE002C3487 + EDAFC5AE109A14EE002C3487 + EDAFC5AF109A14EE002C3487 + EDAFC5B0109A14EE002C3487 + + ToolbarConfiguration + xcode.toolbar.config.debugV3 + WindowString + 150 519 694 422 0 0 1680 1028 + WindowToolGUID + 1CD10A99069EF8BA00B06720 + WindowToolIsVisible + + + + FirstTimeWindowDisplayed + + Identifier + windowTool.find + IsVertical + + Layout + + + Dock + + + Dock + + + BecomeActive + + ContentConfiguration + + PBXProjectModuleGUID + 1CDD528C0622207200134675 + PBXProjectModuleLabel + iphone_menus.c + StatusBarVisibility + + + GeometryConfiguration + + Frame + {{0, 0}, {1122, 588}} + RubberWindowFrame + 450 88 1122 866 0 0 1680 1028 + + Module + PBXNavigatorGroup + Proportion + 1122pt + + + Proportion + 588pt + + + ContentConfiguration + + PBXProjectModuleGUID + 1CD0528E0623707200166675 + PBXProjectModuleLabel + Project Find + + GeometryConfiguration + + Frame + {{0, 593}, {1122, 232}} + RubberWindowFrame + 450 88 1122 866 0 0 1680 1028 + + Module + PBXProjectFindModule + Proportion + 232pt + + + Proportion + 825pt + + + Name + Project Find + ServiceClasses + + PBXProjectFindModule + + StatusbarIsVisible + + TableOfContents + + 1C530D57069F1CE1000CFCEE + EDAFC5B1109A14EE002C3487 + EDAFC5B2109A14EE002C3487 + 1CDD528C0622207200134675 + 1CD0528E0623707200166675 + + WindowString + 450 88 1122 866 0 0 1680 1028 + WindowToolGUID + 1C530D57069F1CE1000CFCEE + WindowToolIsVisible + + + + Identifier + MENUSEPARATOR + + + FirstTimeWindowDisplayed + + Identifier + windowTool.debuggerConsole + IsVertical + + Layout + + + Dock + + + ContentConfiguration + + PBXProjectModuleGUID + 1C78EAAC065D492600B07095 + PBXProjectModuleLabel + Debugger Console + + GeometryConfiguration + + Frame + {{0, 0}, {650, 209}} + RubberWindowFrame + 450 704 650 250 0 0 1680 1028 + + Module + PBXDebugCLIModule + Proportion + 209pt + + + Proportion + 209pt + + + Name + Debugger Console + ServiceClasses + + PBXDebugCLIModule + + StatusbarIsVisible + + TableOfContents + + 1C78EAAD065D492600B07095 + EDAFC5B3109A14EE002C3487 + 1C78EAAC065D492600B07095 + + ToolbarConfiguration + xcode.toolbar.config.consoleV3 + WindowString + 450 704 650 250 0 0 1680 1028 + WindowToolGUID + 1C78EAAD065D492600B07095 + WindowToolIsVisible + + + + Identifier + windowTool.snapshots + Layout + + + Dock + + + Module + XCSnapshotModule + Proportion + 100% + + + Proportion + 100% + + + Name + Snapshots + ServiceClasses + + XCSnapshotModule + + StatusbarIsVisible + Yes + ToolbarConfiguration + xcode.toolbar.config.snapshots + WindowString + 315 824 300 550 0 0 1440 878 + WindowToolIsVisible + Yes + + + Identifier + windowTool.scm + Layout + + + Dock + + + ContentConfiguration + + PBXProjectModuleGUID + 1C78EAB2065D492600B07095 + PBXProjectModuleLabel + <No Editor> + PBXSplitModuleInNavigatorKey + + Split0 + + PBXProjectModuleGUID + 1C78EAB3065D492600B07095 + + SplitCount + 1 + + StatusBarVisibility + 1 + + GeometryConfiguration + + Frame + {{0, 0}, {452, 0}} + RubberWindowFrame + 743 379 452 308 0 0 1280 1002 + + Module + PBXNavigatorGroup + Proportion + 0pt + + + BecomeActive + 1 + ContentConfiguration + + PBXProjectModuleGUID + 1CD052920623707200166675 + PBXProjectModuleLabel + SCM + + GeometryConfiguration + + ConsoleFrame + {{0, 259}, {452, 0}} + Frame + {{0, 7}, {452, 259}} + RubberWindowFrame + 743 379 452 308 0 0 1280 1002 + TableConfiguration + + Status + 30 + FileName + 199 + Path + 197.0950012207031 + + TableFrame + {{0, 0}, {452, 250}} + + Module + PBXCVSModule + Proportion + 262pt + + + Proportion + 266pt + + + Name + SCM + ServiceClasses + + PBXCVSModule + + StatusbarIsVisible + 1 + TableOfContents + + 1C78EAB4065D492600B07095 + 1C78EAB5065D492600B07095 + 1C78EAB2065D492600B07095 + 1CD052920623707200166675 + + ToolbarConfiguration + xcode.toolbar.config.scm + WindowString + 743 379 452 308 0 0 1280 1002 + + + Identifier + windowTool.breakpoints + IsVertical + 0 + Layout + + + Dock + + + BecomeActive + 1 + ContentConfiguration + + PBXBottomSmartGroupGIDs + + 1C77FABC04509CD000000102 + + PBXProjectModuleGUID + 1CE0B1FE06471DED0097A5F4 + PBXProjectModuleLabel + Files + PBXProjectStructureProvided + no + PBXSmartGroupTreeModuleColumnData + + PBXSmartGroupTreeModuleColumnWidthsKey + + 168 + + PBXSmartGroupTreeModuleColumnsKey_v4 + + MainColumn + + + PBXSmartGroupTreeModuleOutlineStateKey_v7 + + PBXSmartGroupTreeModuleOutlineStateExpansionKey + + 1C77FABC04509CD000000102 + + PBXSmartGroupTreeModuleOutlineStateSelectionKey + + + 0 + + + PBXSmartGroupTreeModuleOutlineStateVisibleRectKey + {{0, 0}, {168, 350}} + + PBXTopSmartGroupGIDs + + XCIncludePerspectivesSwitch + 0 + + GeometryConfiguration + + Frame + {{0, 0}, {185, 368}} + GroupTreeTableConfiguration + + MainColumn + 168 + + RubberWindowFrame + 315 424 744 409 0 0 1440 878 + + Module + PBXSmartGroupTreeModule + Proportion + 185pt + + + ContentConfiguration + + PBXProjectModuleGUID + 1CA1AED706398EBD00589147 + PBXProjectModuleLabel + Detail + + GeometryConfiguration + + Frame + {{190, 0}, {554, 368}} + RubberWindowFrame + 315 424 744 409 0 0 1440 878 + + Module + XCDetailModule + Proportion + 554pt + + + Proportion + 368pt + + + MajorVersion + 3 + MinorVersion + 0 + Name + Breakpoints + ServiceClasses + + PBXSmartGroupTreeModule + XCDetailModule + + StatusbarIsVisible + 1 + TableOfContents + + 1CDDB66807F98D9800BB5817 + 1CDDB66907F98D9800BB5817 + 1CE0B1FE06471DED0097A5F4 + 1CA1AED706398EBD00589147 + + ToolbarConfiguration + xcode.toolbar.config.breakpointsV3 + WindowString + 315 424 744 409 0 0 1440 878 + WindowToolGUID + 1CDDB66807F98D9800BB5817 + WindowToolIsVisible + 1 + + + Identifier + windowTool.debugAnimator + Layout + + + Dock + + + Module + PBXNavigatorGroup + Proportion + 100% + + + Proportion + 100% + + + Name + Debug Visualizer + ServiceClasses + + PBXNavigatorGroup + + StatusbarIsVisible + 1 + ToolbarConfiguration + xcode.toolbar.config.debugAnimatorV3 + WindowString + 100 100 700 500 0 0 1280 1002 + + + Identifier + windowTool.bookmarks + Layout + + + Dock + + + Module + PBXBookmarksModule + Proportion + 100% + + + Proportion + 100% + + + Name + Bookmarks + ServiceClasses + + PBXBookmarksModule + + StatusbarIsVisible + 0 + WindowString + 538 42 401 187 0 0 1280 1002 + + + Identifier + windowTool.projectFormatConflicts + Layout + + + Dock + + + Module + XCProjectFormatConflictsModule + Proportion + 100% + + + Proportion + 100% + + + Name + Project Format Conflicts + ServiceClasses + + XCProjectFormatConflictsModule + + StatusbarIsVisible + 0 + WindowContentMinSize + 450 300 + WindowString + 50 850 472 307 0 0 1440 877 + + + Identifier + windowTool.classBrowser + Layout + + + Dock + + + BecomeActive + 1 + ContentConfiguration + + OptionsSetName + Hierarchy, all classes + PBXProjectModuleGUID + 1CA6456E063B45B4001379D8 + PBXProjectModuleLabel + Class Browser - NSObject + + GeometryConfiguration + + ClassesFrame + {{0, 0}, {374, 96}} + ClassesTreeTableConfiguration + + PBXClassNameColumnIdentifier + 208 + PBXClassBookColumnIdentifier + 22 + + Frame + {{0, 0}, {630, 331}} + MembersFrame + {{0, 105}, {374, 395}} + MembersTreeTableConfiguration + + PBXMemberTypeIconColumnIdentifier + 22 + PBXMemberNameColumnIdentifier + 216 + PBXMemberTypeColumnIdentifier + 97 + PBXMemberBookColumnIdentifier + 22 + + PBXModuleWindowStatusBarHidden2 + 1 + RubberWindowFrame + 385 179 630 352 0 0 1440 878 + + Module + PBXClassBrowserModule + Proportion + 332pt + + + Proportion + 332pt + + + Name + Class Browser + ServiceClasses + + PBXClassBrowserModule + + StatusbarIsVisible + 0 + TableOfContents + + 1C0AD2AF069F1E9B00FABCE6 + 1C0AD2B0069F1E9B00FABCE6 + 1CA6456E063B45B4001379D8 + + ToolbarConfiguration + xcode.toolbar.config.classbrowser + WindowString + 385 179 630 352 0 0 1440 878 + WindowToolGUID + 1C0AD2AF069F1E9B00FABCE6 + WindowToolIsVisible + 0 + + + Identifier + windowTool.refactoring + IncludeInToolsMenu + 0 + Layout + + + Dock + + + BecomeActive + 1 + GeometryConfiguration + + Frame + {0, 0}, {500, 335} + RubberWindowFrame + {0, 0}, {500, 335} + + Module + XCRefactoringModule + Proportion + 100% + + + Proportion + 100% + + + Name + Refactoring + ServiceClasses + + XCRefactoringModule + + WindowString + 200 200 500 356 0 0 1920 1200 + + + + diff --git a/wolf3d/code/iphone/wolf3d.xcodeproj/greghodges.pbxuser b/wolf3d/code/iphone/wolf3d.xcodeproj/greghodges.pbxuser new file mode 100644 index 0000000..839a542 --- /dev/null +++ b/wolf3d/code/iphone/wolf3d.xcodeproj/greghodges.pbxuser @@ -0,0 +1,578 @@ +// !$*UTF8*$! +{ + 1D6058900D05DD3D006BFB54 /* wolf3d */ = { + activeExec = 0; + executables = ( + ED0A8CE41069A9CF001E0547 /* wolf3d */, + ); + }; + 29B97313FDCFA39411CA2CEA /* Project object */ = { + activeBuildConfigurationName = Release; + activeExecutable = ED0A8CE41069A9CF001E0547 /* wolf3d */; + activeSDKPreference = iphonesimulator2.0; + activeTarget = 1D6058900D05DD3D006BFB54 /* wolf3d */; + addToTargets = ( + 1D6058900D05DD3D006BFB54 /* wolf3d */, + ); + codeSenseManager = ED0A8CFA1069A9F5001E0547 /* Code sense */; + executables = ( + ED0A8CE41069A9CF001E0547 /* wolf3d */, + ); + perUserDictionary = { + PBXConfiguration.PBXFileTableDataSource3.PBXFileTableDataSource = { + PBXFileTableDataSourceColumnSortingDirectionKey = "-1"; + PBXFileTableDataSourceColumnSortingKey = PBXFileDataSource_Filename_ColumnID; + PBXFileTableDataSourceColumnWidthsKey = ( + 20, + 563, + 20, + 48, + 43, + 43, + 20, + ); + PBXFileTableDataSourceColumnsKey = ( + PBXFileDataSource_FiletypeID, + PBXFileDataSource_Filename_ColumnID, + PBXFileDataSource_Built_ColumnID, + PBXFileDataSource_ObjectSize_ColumnID, + PBXFileDataSource_Errors_ColumnID, + PBXFileDataSource_Warnings_ColumnID, + PBXFileDataSource_Target_ColumnID, + ); + }; + PBXConfiguration.PBXTargetDataSource.PBXTargetDataSource = { + PBXFileTableDataSourceColumnSortingDirectionKey = "-1"; + PBXFileTableDataSourceColumnSortingKey = PBXFileDataSource_Filename_ColumnID; + PBXFileTableDataSourceColumnWidthsKey = ( + 20, + 523, + 60, + 20, + 48, + 43, + 43, + ); + PBXFileTableDataSourceColumnsKey = ( + PBXFileDataSource_FiletypeID, + PBXFileDataSource_Filename_ColumnID, + PBXTargetDataSource_PrimaryAttribute, + PBXFileDataSource_Built_ColumnID, + PBXFileDataSource_ObjectSize_ColumnID, + PBXFileDataSource_Errors_ColumnID, + PBXFileDataSource_Warnings_ColumnID, + ); + }; + PBXPerProjectTemplateStateSaveDate = 280108689; + PBXWorkspaceStateSaveDate = 280108689; + }; + perUserProjectItems = { + ED0653B61087719F00E5450B /* PBXTextBookmark */ = ED0653B61087719F00E5450B /* PBXTextBookmark */; + ED0653B81087719F00E5450B /* PBXTextBookmark */ = ED0653B81087719F00E5450B /* PBXTextBookmark */; + ED2754531085141B009C891E /* PBXTextBookmark */ = ED2754531085141B009C891E /* PBXTextBookmark */; + ED2754551085141B009C891E /* PlistBookmark */ = ED2754551085141B009C891E /* PlistBookmark */; + ED2754561085141B009C891E /* PBXTextBookmark */ = ED2754561085141B009C891E /* PBXTextBookmark */; + ED2E5C05106C886700F57B9D /* PBXTextBookmark */ = ED2E5C05106C886700F57B9D /* PBXTextBookmark */; + ED2E5C06106C886700F57B9D /* PBXTextBookmark */ = ED2E5C06106C886700F57B9D /* PBXTextBookmark */; + ED2E5C0A106C886700F57B9D /* PBXTextBookmark */ = ED2E5C0A106C886700F57B9D /* PBXTextBookmark */; + ED2E5C0B106C886700F57B9D /* PBXTextBookmark */ = ED2E5C0B106C886700F57B9D /* PBXTextBookmark */; + ED3703CD106C20620059C5F8 /* PlistBookmark */ = ED3703CD106C20620059C5F8 /* PlistBookmark */; + ED45402110B21F0900DCFA32 /* PBXTextBookmark */ = ED45402110B21F0900DCFA32 /* PBXTextBookmark */; + ED45402210B21F0900DCFA32 /* PBXTextBookmark */ = ED45402210B21F0900DCFA32 /* PBXTextBookmark */; + ED45402310B21F0900DCFA32 /* PBXTextBookmark */ = ED45402310B21F0900DCFA32 /* PBXTextBookmark */; + ED45402410B21F0900DCFA32 /* PBXTextBookmark */ = ED45402410B21F0900DCFA32 /* PBXTextBookmark */; + ED45402510B21F0900DCFA32 /* PBXTextBookmark */ = ED45402510B21F0900DCFA32 /* PBXTextBookmark */; + ED45402610B21F0900DCFA32 /* PBXTextBookmark */ = ED45402610B21F0900DCFA32 /* PBXTextBookmark */; + ED45402710B21F0900DCFA32 /* PlistBookmark */ = ED45402710B21F0900DCFA32 /* PlistBookmark */; + ED4AC98010742365000706D8 /* PBXTextBookmark */ = ED4AC98010742365000706D8 /* PBXTextBookmark */; + ED4AC98210742365000706D8 /* PBXTextBookmark */ = ED4AC98210742365000706D8 /* PBXTextBookmark */; + ED4AC9AB10743318000706D8 /* PBXTextBookmark */ = ED4AC9AB10743318000706D8 /* PBXTextBookmark */; + ED4AC9AC10743318000706D8 /* PBXTextBookmark */ = ED4AC9AC10743318000706D8 /* PBXTextBookmark */; + ED845AB1109F5B3500F673AC /* PBXTextBookmark */ = ED845AB1109F5B3500F673AC /* PBXTextBookmark */; + ED845AB2109F5B3500F673AC /* PlistBookmark */ = ED845AB2109F5B3500F673AC /* PlistBookmark */; + EDAFC5A2109A14EE002C3487 /* PBXTextBookmark */ = EDAFC5A2109A14EE002C3487 /* PBXTextBookmark */; + EDDA5A7E107BA97F003F55D3 /* PBXTextBookmark */ = EDDA5A7E107BA97F003F55D3 /* PBXTextBookmark */; + EDDA5A80107BA97F003F55D3 /* PBXTextBookmark */ = EDDA5A80107BA97F003F55D3 /* PBXTextBookmark */; + EDDA5A82107BA97F003F55D3 /* PBXTextBookmark */ = EDDA5A82107BA97F003F55D3 /* PBXTextBookmark */; + EDDA5A83107BA97F003F55D3 /* PBXTextBookmark */ = EDDA5A83107BA97F003F55D3 /* PBXTextBookmark */; + EDDA5A84107BA97F003F55D3 /* PBXTextBookmark */ = EDDA5A84107BA97F003F55D3 /* PBXTextBookmark */; + }; + sourceControlManager = ED0A8CF91069A9F5001E0547 /* Source Control */; + userBuildSettings = { + }; + }; + 43AF6B940F996DA200777569 /* iphone_sys.m */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {852, 1050}}"; + sepNavSelRange = "{1419, 24}"; + sepNavVisRange = "{771, 1684}"; + }; + }; + 43CF03090F56D5C200E4A23D /* iphone_loop.c */ = { + uiCtxt = { + sepNavFolds = "{\n c = (\n {\n l = \"int y\";\n r = \"{41674, 9}\";\n s = 1;\n },\n {\n l = \"int width\";\n r = \"{41685, 13}\";\n s = 1;\n },\n {\n l = \"int height\";\n r = \"{41700, 14}\";\n s = 1;\n },\n {\n l = \"const char * str\";\n r = \"{41716, 20}\";\n s = 1;\n }\n );\n r = \"{0, 41792}\";\n s = 0;\n}"; + sepNavIntBoundsRect = "{{0, 0}, {1061, 24388}}"; + sepNavSelRange = "{27221, 8}"; + sepNavVisRange = "{26941, 622}"; + }; + }; + 43E8D2DF0F4FC61E003F09B2 /* iphone_main.c */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {741, 5418}}"; + sepNavSelRange = "{1413, 4}"; + sepNavVisRange = "{970, 956}"; + }; + }; + 7229CC270F6B3222004123C5 /* wolf_actors.c */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {741, 6398}}"; + sepNavSelRange = "{84, 0}"; + sepNavVisRange = "{0, 1418}"; + }; + }; + 7229CC8E0F6B3363004123C5 /* wolfiphone.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {741, 1260}}"; + sepNavSelRange = "{1906, 27}"; + sepNavVisRange = "{812, 1122}"; + }; + }; + 7229CE460F6C89F8004123C5 /* EAGLView.m */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {786, 6650}}"; + sepNavSelRange = "{0, 0}"; + sepNavVisRange = "{10594, 1878}"; + }; + }; + 7229CE480F6C89F8004123C5 /* wolf3dAppDelegate.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {741, 654}}"; + sepNavSelRange = "{0, 0}"; + sepNavVisRange = "{0, 1205}"; + }; + }; + 7229CE490F6C89F8004123C5 /* wolf3dAppDelegate.m */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1074, 5012}}"; + sepNavSelRange = "{3930, 0}"; + sepNavVisRange = "{5119, 1271}"; + }; + }; + 72935B1E0F6B2D9D0085DD28 /* arch.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {741, 3808}}"; + sepNavSelRange = "{2579, 0}"; + sepNavVisRange = "{2465, 1040}"; + }; + }; + 72935B630F6B2D9D0085DD28 /* tga.c */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {741, 10822}}"; + sepNavSelRange = "{1562, 2}"; + sepNavVisRange = "{1162, 856}"; + }; + }; + 72A7E8F30F5F2001005B83C0 /* iphone_wolf.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {741, 3850}}"; + sepNavSelRange = "{1009, 0}"; + sepNavVisRange = "{0, 1316}"; + }; + }; + 72A7E8F60F5F2063005B83C0 /* iphone_menus.c */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {900, 56714}}"; + sepNavSelRange = "{66944, 0}"; + sepNavVisRange = "{62588, 1640}"; + }; + }; + ED0653B61087719F00E5450B /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 43AF6B940F996DA200777569 /* iphone_sys.m */; + name = "iphone_sys.m: 40"; + rLen = 24; + rLoc = 1419; + rType = 0; + vrLen = 1684; + vrLoc = 771; + }; + ED0653B81087719F00E5450B /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 43AF6B940F996DA200777569 /* iphone_sys.m */; + name = "iphone_sys.m: 40"; + rLen = 24; + rLoc = 1419; + rType = 0; + vrLen = 1684; + vrLoc = 771; + }; + ED0A8CE41069A9CF001E0547 /* wolf3d */ = { + isa = PBXExecutable; + activeArgIndices = ( + ); + argumentStrings = ( + ); + autoAttachOnCrash = 1; + breakpointsEnabled = 0; + configStateDict = { + }; + customDataFormattersEnabled = 1; + debuggerPlugin = GDBDebugging; + disassemblyDisplayState = 0; + dylibVariantSuffix = ""; + enableDebugStr = 1; + environmentEntries = ( + ); + executableSystemSymbolLevel = 0; + executableUserSymbolLevel = 0; + libgmallocEnabled = 0; + name = wolf3d; + savedGlobals = { + }; + sourceDirectories = ( + ); + variableFormatDictionary = { + }; + }; + ED0A8CF91069A9F5001E0547 /* Source Control */ = { + isa = PBXSourceControlManager; + fallbackIsa = XCSourceControlManager; + isSCMEnabled = 0; + scmConfiguration = { + }; + }; + ED0A8CFA1069A9F5001E0547 /* Code sense */ = { + isa = PBXCodeSenseManager; + indexTemplatePath = ""; + }; + ED0A8D0D1069ACA8001E0547 /* iphone_downloadSOD.m */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {741, 7434}}"; + sepNavSelRange = "{1756, 0}"; + sepNavVisRange = "{1108, 1549}"; + }; + }; + ED0A8D0F1069ACA8001E0547 /* iphone_mapselector.c */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {948, 6706}}"; + sepNavSelRange = "{7865, 0}"; + sepNavVisRange = "{1577, 1229}"; + }; + }; + ED0A8D101069ACA8001E0547 /* iphone_store.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {741, 654}}"; + sepNavSelRange = "{0, 0}"; + sepNavVisRange = "{0, 1253}"; + }; + }; + ED0A8D111069ACA8001E0547 /* iphone_store.m */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {741, 5964}}"; + sepNavSelRange = "{673, 17}"; + sepNavVisRange = "{0, 1394}"; + }; + }; + ED2754531085141B009C891E /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 7229CE460F6C89F8004123C5 /* EAGLView.m */; + name = "EAGLView.m: 1"; + rLen = 0; + rLoc = 0; + rType = 0; + vrLen = 1878; + vrLoc = 10594; + }; + ED2754551085141B009C891E /* PlistBookmark */ = { + isa = PlistBookmark; + fRef = 8D1107310486CEB800E47090 /* Info.plist */; + fallbackIsa = PBXBookmark; + isK = 0; + kPath = ( + UIPrerenderedIcon, + ); + name = /Users/greghodges/wolf3dplatinum/wolf3d/code/iphone/Info.plist; + rLen = 0; + rLoc = 2147483647; + }; + ED2754561085141B009C891E /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 7229CE460F6C89F8004123C5 /* EAGLView.m */; + name = "EAGLView.m: 1"; + rLen = 0; + rLoc = 0; + rType = 0; + vrLen = 1878; + vrLoc = 10594; + }; + ED2E5C05106C886700F57B9D /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = ED0A8D0D1069ACA8001E0547 /* iphone_downloadSOD.m */; + name = "iphone_downloadSOD.m: 61"; + rLen = 0; + rLoc = 1756; + rType = 0; + vrLen = 1549; + vrLoc = 1108; + }; + ED2E5C06106C886700F57B9D /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = ED2E5C07106C886700F57B9D /* OpenTGA.c */; + name = "OpenTGA.c: 1"; + rLen = 0; + rLoc = 0; + rType = 0; + vrLen = 722; + vrLoc = 12240; + }; + ED2E5C07106C886700F57B9D /* OpenTGA.c */ = { + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.c; + name = OpenTGA.c; + path = /Users/greghodges/Desktop/MyTGAConverter/OpenTGA.c; + sourceTree = ""; + }; + ED2E5C0A106C886700F57B9D /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = ED0A8D0D1069ACA8001E0547 /* iphone_downloadSOD.m */; + name = "iphone_downloadSOD.m: 61"; + rLen = 0; + rLoc = 1756; + rType = 0; + vrLen = 1549; + vrLoc = 1108; + }; + ED2E5C0B106C886700F57B9D /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = ED2E5C0C106C886700F57B9D /* OpenTGA.c */; + name = "OpenTGA.c: 1"; + rLen = 0; + rLoc = 0; + rType = 0; + vrLen = 722; + vrLoc = 12240; + }; + ED2E5C0C106C886700F57B9D /* OpenTGA.c */ = { + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.c; + name = OpenTGA.c; + path = /Users/greghodges/Desktop/MyTGAConverter/OpenTGA.c; + sourceTree = ""; + }; + ED3703CD106C20620059C5F8 /* PlistBookmark */ = { + isa = PlistBookmark; + fRef = ED3703CE106C20620059C5F8; + fallbackIsa = PBXBookmark; + isK = 0; + kPath = ( + ); + rLen = 0; + rLoc = 2147483647; + }; + ED3703CE106C20620059C5F8 = { + isa = PBXFileReference; + lastKnownFileType = folder; + sourceTree = ""; + }; + ED45402110B21F0900DCFA32 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 7229CC270F6B3222004123C5 /* wolf_actors.c */; + name = "wolf_actors.c: 5"; + rLen = 0; + rLoc = 84; + rType = 0; + vrLen = 1418; + vrLoc = 0; + }; + ED45402210B21F0900DCFA32 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 7229CE490F6C89F8004123C5 /* wolf3dAppDelegate.m */; + name = "wolf3dAppDelegate.m: 124"; + rLen = 0; + rLoc = 3930; + rType = 0; + vrLen = 1271; + vrLoc = 5119; + }; + ED45402310B21F0900DCFA32 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 7229CE480F6C89F8004123C5 /* wolf3dAppDelegate.h */; + name = "wolf3dAppDelegate.h: 1"; + rLen = 0; + rLoc = 0; + rType = 0; + vrLen = 1205; + vrLoc = 0; + }; + ED45402410B21F0900DCFA32 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 7229CC270F6B3222004123C5 /* wolf_actors.c */; + name = "wolf_actors.c: 5"; + rLen = 0; + rLoc = 84; + rType = 0; + vrLen = 1418; + vrLoc = 0; + }; + ED45402510B21F0900DCFA32 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 7229CE490F6C89F8004123C5 /* wolf3dAppDelegate.m */; + name = "wolf3dAppDelegate.m: 124"; + rLen = 0; + rLoc = 3930; + rType = 0; + vrLen = 1271; + vrLoc = 5119; + }; + ED45402610B21F0900DCFA32 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 7229CE480F6C89F8004123C5 /* wolf3dAppDelegate.h */; + name = "wolf3dAppDelegate.h: 1"; + rLen = 0; + rLoc = 0; + rType = 0; + vrLen = 1205; + vrLoc = 0; + }; + ED45402710B21F0900DCFA32 /* PlistBookmark */ = { + isa = PlistBookmark; + fRef = 8D1107310486CEB800E47090 /* Info.plist */; + fallbackIsa = PBXBookmark; + isK = 0; + kPath = ( + UIPrerenderedIcon, + ); + name = /Users/greghodges/wolf3dplatinum/wolf3d/code/iphone/Info.plist; + rLen = 0; + rLoc = 2147483647; + }; + ED4AC98010742365000706D8 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = EDD56B7E10740B7A007C0E16 /* ConvertTGA.c */; + name = "ConvertTGA.c: 1"; + rLen = 0; + rLoc = 0; + rType = 0; + vrLen = 1082; + vrLoc = 18055; + }; + ED4AC98210742365000706D8 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = EDD56B7E10740B7A007C0E16 /* ConvertTGA.c */; + name = "ConvertTGA.c: 1"; + rLen = 0; + rLoc = 0; + rType = 0; + vrLen = 1082; + vrLoc = 18055; + }; + ED4AC9AB10743318000706D8 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 7229CE490F6C89F8004123C5 /* wolf3dAppDelegate.m */; + name = "wolf3dAppDelegate.m: 124"; + rLen = 0; + rLoc = 3930; + rType = 0; + vrLen = 1417; + vrLoc = 3274; + }; + ED4AC9AC10743318000706D8 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 72A7E8F60F5F2063005B83C0 /* iphone_menus.c */; + name = "iphone_menus.c: 1111"; + rLen = 13; + rLoc = 31543; + rType = 0; + vrLen = 2083; + vrLoc = 30775; + }; + ED845AB1109F5B3500F673AC /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 72A7E8F60F5F2063005B83C0 /* iphone_menus.c */; + name = "iphone_menus.c: 2407"; + rLen = 0; + rLoc = 66944; + rType = 0; + vrLen = 1640; + vrLoc = 62588; + }; + ED845AB2109F5B3500F673AC /* PlistBookmark */ = { + isa = PlistBookmark; + fRef = 8D1107310486CEB800E47090 /* Info.plist */; + fallbackIsa = PBXBookmark; + isK = 0; + kPath = ( + UIPrerenderedIcon, + ); + name = /Users/greghodges/wolf3dplatinum/wolf3d/code/iphone/Info.plist; + rLen = 0; + rLoc = 2147483647; + }; + EDAFC5A2109A14EE002C3487 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 43E8D2DF0F4FC61E003F09B2 /* iphone_main.c */; + name = "iphone_main.c: 50"; + rLen = 4; + rLoc = 1413; + rType = 0; + vrLen = 956; + vrLoc = 970; + }; + EDD56B7E10740B7A007C0E16 /* ConvertTGA.c */ = { + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.c; + name = ConvertTGA.c; + path = /Users/greghodges/Desktop/MyTGAConverter/ConvertTGA.c; + sourceTree = ""; + }; + EDDA5A7E107BA97F003F55D3 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 43CF03090F56D5C200E4A23D /* iphone_loop.c */; + name = "iphone_loop.c: 1226"; + rLen = 0; + rLoc = 28766; + rType = 0; + vrLen = 942; + vrLoc = 28216; + }; + EDDA5A80107BA97F003F55D3 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = ED0A8D0F1069ACA8001E0547 /* iphone_mapselector.c */; + name = "iphone_mapselector.c: 341"; + rLen = 0; + rLoc = 7865; + rType = 0; + vrLen = 1229; + vrLoc = 1577; + }; + EDDA5A82107BA97F003F55D3 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 43CF03090F56D5C200E4A23D /* iphone_loop.c */; + name = "iphone_loop.c: 1226"; + rLen = 0; + rLoc = 28766; + rType = 0; + vrLen = 942; + vrLoc = 28216; + }; + EDDA5A83107BA97F003F55D3 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 43E8D2DF0F4FC61E003F09B2 /* iphone_main.c */; + name = "iphone_main.c: 272"; + rLen = 0; + rLoc = 7878; + rType = 0; + vrLen = 1550; + vrLoc = 7519; + }; + EDDA5A84107BA97F003F55D3 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = ED0A8D0F1069ACA8001E0547 /* iphone_mapselector.c */; + name = "iphone_mapselector.c: 341"; + rLen = 0; + rLoc = 7865; + rType = 0; + vrLen = 1229; + vrLoc = 1577; + }; +} diff --git a/wolf3d/code/iphone/wolf3d.xcodeproj/project.pbxproj b/wolf3d/code/iphone/wolf3d.xcodeproj/project.pbxproj index 164064a..2192ff2 100644 --- a/wolf3d/code/iphone/wolf3d.xcodeproj/project.pbxproj +++ b/wolf3d/code/iphone/wolf3d.xcodeproj/project.pbxproj @@ -95,11 +95,17 @@ 72935BA20F6B2D9D0085DD28 /* zmem.c in Sources */ = {isa = PBXBuildFile; fileRef = 72935B720F6B2D9D0085DD28 /* zmem.c */; }; 72A7E8F70F5F2063005B83C0 /* iphone_menus.c in Sources */ = {isa = PBXBuildFile; fileRef = 72A7E8F60F5F2063005B83C0 /* iphone_menus.c */; }; 72B5FF390F7E5C3D00C8A372 /* hud.c in Sources */ = {isa = PBXBuildFile; fileRef = 72B5FF380F7E5C3D00C8A372 /* hud.c */; }; + ED0A8D121069ACA8001E0547 /* iphone_alerts.m in Sources */ = {isa = PBXBuildFile; fileRef = ED0A8D0C1069ACA8001E0547 /* iphone_alerts.m */; }; + ED0A8D131069ACA8001E0547 /* iphone_downloadSOD.m in Sources */ = {isa = PBXBuildFile; fileRef = ED0A8D0D1069ACA8001E0547 /* iphone_downloadSOD.m */; }; + ED0A8D141069ACA8001E0547 /* iphone_downloadUserMap.m in Sources */ = {isa = PBXBuildFile; fileRef = ED0A8D0E1069ACA8001E0547 /* iphone_downloadUserMap.m */; }; + ED0A8D151069ACA8001E0547 /* iphone_mapselector.c in Sources */ = {isa = PBXBuildFile; fileRef = ED0A8D0F1069ACA8001E0547 /* iphone_mapselector.c */; }; + ED0A8D161069ACA8001E0547 /* iphone_store.m in Sources */ = {isa = PBXBuildFile; fileRef = ED0A8D111069ACA8001E0547 /* iphone_store.m */; }; + ED3703B4106C1CD40059C5F8 /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = ED3703B3106C1CD40059C5F8 /* SystemConfiguration.framework */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ 1D30AB110D05D00D00671497 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; - 1D6058910D05DD3D006BFB54 /* wolf3dlite.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = wolf3dlite.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 1D6058910D05DD3D006BFB54 /* wolf3d.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = wolf3d.app; sourceTree = BUILT_PRODUCTS_DIR; }; 1DF5F4DF0D08C38300B7A737 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; 28AD733E0D9D9553002E5188 /* MainWindow.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = MainWindow.xib; sourceTree = ""; }; 28FD14FF0DC6FC520079059D /* OpenGLES.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OpenGLES.framework; path = System/Library/Frameworks/OpenGLES.framework; sourceTree = SDKROOT; }; @@ -257,6 +263,15 @@ 72A7E8F60F5F2063005B83C0 /* iphone_menus.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = iphone_menus.c; sourceTree = ""; }; 72B5FF380F7E5C3D00C8A372 /* hud.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = hud.c; sourceTree = ""; }; 8D1107310486CEB800E47090 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + ED0A8D0A1069ACA8001E0547 /* arialGlyphRects.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = arialGlyphRects.h; sourceTree = ""; }; + ED0A8D0B1069ACA8001E0547 /* iphone_alerts.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = iphone_alerts.h; sourceTree = ""; }; + ED0A8D0C1069ACA8001E0547 /* iphone_alerts.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = iphone_alerts.m; sourceTree = ""; }; + ED0A8D0D1069ACA8001E0547 /* iphone_downloadSOD.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = iphone_downloadSOD.m; sourceTree = ""; }; + ED0A8D0E1069ACA8001E0547 /* iphone_downloadUserMap.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = iphone_downloadUserMap.m; sourceTree = ""; }; + ED0A8D0F1069ACA8001E0547 /* iphone_mapselector.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = iphone_mapselector.c; sourceTree = ""; }; + ED0A8D101069ACA8001E0547 /* iphone_store.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = iphone_store.h; sourceTree = ""; }; + ED0A8D111069ACA8001E0547 /* iphone_store.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = iphone_store.m; sourceTree = ""; }; + ED3703B3106C1CD40059C5F8 /* SystemConfiguration.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SystemConfiguration.framework; path = System/Library/Frameworks/SystemConfiguration.framework; sourceTree = SDKROOT; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -271,6 +286,7 @@ 4333CCE80F5CC23E00AE2B6F /* AudioToolbox.framework in Frameworks */, 43AE7E9F0F67387500B2F562 /* CoreGraphics.framework in Frameworks */, 720EBBAE0F82E0BB003F989A /* QuartzCore.framework in Frameworks */, + ED3703B4106C1CD40059C5F8 /* SystemConfiguration.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -280,7 +296,7 @@ 19C28FACFE9D520D11CA2CBB /* Products */ = { isa = PBXGroup; children = ( - 1D6058910D05DD3D006BFB54 /* wolf3dlite.app */, + 1D6058910D05DD3D006BFB54 /* wolf3d.app */, 4364BF3E0F5CB25900F29317 /* dist.plist */, ); name = Products; @@ -301,6 +317,14 @@ 29B97315FDCFA39411CA2CEA /* Other Sources */ = { isa = PBXGroup; children = ( + ED0A8D0A1069ACA8001E0547 /* arialGlyphRects.h */, + ED0A8D0B1069ACA8001E0547 /* iphone_alerts.h */, + ED0A8D0C1069ACA8001E0547 /* iphone_alerts.m */, + ED0A8D0D1069ACA8001E0547 /* iphone_downloadSOD.m */, + ED0A8D0E1069ACA8001E0547 /* iphone_downloadUserMap.m */, + ED0A8D0F1069ACA8001E0547 /* iphone_mapselector.c */, + ED0A8D101069ACA8001E0547 /* iphone_store.h */, + ED0A8D111069ACA8001E0547 /* iphone_store.m */, 72935B180F6B2D630085DD28 /* env */, 7229CC5A0F6B324A004123C5 /* tremor */, 72935B190F6B2D720085DD28 /* wolf */, @@ -337,6 +361,7 @@ 29B97323FDCFA39411CA2CEA /* Frameworks */ = { isa = PBXGroup; children = ( + ED3703B3106C1CD40059C5F8 /* SystemConfiguration.framework */, 720EBBAD0F82E0BB003F989A /* QuartzCore.framework */, 4333CCE70F5CC23E00AE2B6F /* AudioToolbox.framework */, 43E8D4DF0F51B48B003F09B2 /* OpenAL.framework */, @@ -517,7 +542,7 @@ ); name = wolf3d; productName = wolf3d; - productReference = 1D6058910D05DD3D006BFB54 /* wolf3dlite.app */; + productReference = 1D6058910D05DD3D006BFB54 /* wolf3d.app */; productType = "com.apple.product-type.application"; }; /* End PBXNativeTarget section */ @@ -649,6 +674,11 @@ 7229CE550F6C8CDE004123C5 /* gles_glue.c in Sources */, 72B5FF390F7E5C3D00C8A372 /* hud.c in Sources */, 43AF6B950F996DA200777569 /* iphone_sys.m in Sources */, + ED0A8D121069ACA8001E0547 /* iphone_alerts.m in Sources */, + ED0A8D131069ACA8001E0547 /* iphone_downloadSOD.m in Sources */, + ED0A8D141069ACA8001E0547 /* iphone_downloadUserMap.m in Sources */, + ED0A8D151069ACA8001E0547 /* iphone_mapselector.c in Sources */, + ED0A8D161069ACA8001E0547 /* iphone_store.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -678,7 +708,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer: Cass Everitt"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer: John Carmack"; COPY_PHASE_STRIP = YES; GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = wolf3d_Prefix.pch; @@ -687,7 +717,7 @@ INFOPLIST_FILE = Info.plist; PRODUCT_NAME = wolf3d; PROFILE_PREFIX = nu.r3; - "PROVISIONING_PROFILE[sdk=iphoneos*]" = "94041F5C-2EDC-4F49-AF97-95ECE6BB398D"; + "PROVISIONING_PROFILE[sdk=iphoneos*]" = "156B7F53-1BDC-4116-B0CC-F8CB2252E722"; }; name = Release; }; @@ -766,12 +796,13 @@ isa = XCBuildConfiguration; buildSettings = { ARCHS = "$(ARCHS_STANDARD_32_BIT)"; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer: John Carmack"; GCC_C_LANGUAGE_STANDARD = c99; GCC_THUMB_SUPPORT = NO; GCC_WARN_ABOUT_RETURN_TYPE = YES; GCC_WARN_UNUSED_VARIABLE = YES; PREBINDING = NO; + "PROVISIONING_PROFILE[sdk=iphoneos*]" = "156B7F53-1BDC-4116-B0CC-F8CB2252E722"; SDKROOT = iphoneos2.0; SYMROOT = ../../build; }; @@ -781,7 +812,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer: Cass Everitt"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer: John Carmack"; COPY_PHASE_STRIP = YES; GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = wolf3d_Prefix.pch; @@ -793,7 +824,7 @@ INFOPLIST_FILE = Info.plist; PRODUCT_NAME = wolf3dlite; PROFILE_PREFIX = nu.r3; - "PROVISIONING_PROFILE[sdk=iphoneos*]" = "94041F5C-2EDC-4F49-AF97-95ECE6BB398D"; + "PROVISIONING_PROFILE[sdk=iphoneos*]" = "156B7F53-1BDC-4116-B0CC-F8CB2252E722"; }; name = ReleaseLite; }; @@ -853,17 +884,51 @@ isa = XCBuildConfiguration; buildSettings = { ARCHS = "$(ARCHS_STANDARD_32_BIT)"; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer: John Carmack"; GCC_C_LANGUAGE_STANDARD = c99; GCC_THUMB_SUPPORT = NO; GCC_WARN_ABOUT_RETURN_TYPE = YES; GCC_WARN_UNUSED_VARIABLE = YES; PREBINDING = NO; + "PROVISIONING_PROFILE[sdk=iphoneos*]" = "156B7F53-1BDC-4116-B0CC-F8CB2252E722"; SDKROOT = iphoneos2.0; SYMROOT = ../../build; }; name = Release; }; + ED2751531083BE8C009C891E /* Distribution */ = { + isa = XCBuildConfiguration; + buildSettings = { + ARCHS = "$(ARCHS_STANDARD_32_BIT)"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution: id Software"; + GCC_C_LANGUAGE_STANDARD = c99; + GCC_THUMB_SUPPORT = NO; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + PREBINDING = NO; + "PROVISIONING_PROFILE[sdk=iphoneos*]" = "31C73095-0DD9-4ABA-BB25-8D23F661F10F"; + SDKROOT = iphoneos2.0; + SYMROOT = ../../build; + }; + name = Distribution; + }; + ED2751541083BE8C009C891E /* Distribution */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution: id Software"; + COPY_PHASE_STRIP = YES; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = wolf3d_Prefix.pch; + GCC_PREPROCESSOR_DEFINITIONS = IPHONE; + HEADER_SEARCH_PATHS = ""; + INFOPLIST_FILE = Info.plist; + PRODUCT_NAME = wolf3d; + PROFILE_PREFIX = nu.r3; + "PROVISIONING_PROFILE[sdk=iphoneos*]" = "31C73095-0DD9-4ABA-BB25-8D23F661F10F"; + }; + name = Distribution; + }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ @@ -876,6 +941,7 @@ 43AE7CA50F61EC4E00B2F562 /* ReleaseLite */, 4364BF490F5CB27300F29317 /* AdHocDist */, 431181710F994C5400FF9351 /* AdHocDistLite */, + ED2751541083BE8C009C891E /* Distribution */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; @@ -889,6 +955,7 @@ 43AE7CA40F61EC4E00B2F562 /* ReleaseLite */, 4364BF480F5CB27300F29317 /* AdHocDist */, 431181700F994C5400FF9351 /* AdHocDistLite */, + ED2751531083BE8C009C891E /* Distribution */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; diff --git a/wolf3d/code/iphone/wolf3dAppDelegate.h b/wolf3d/code/iphone/wolf3dAppDelegate.h index 96e6653..3a14087 100644 --- a/wolf3d/code/iphone/wolf3dAppDelegate.h +++ b/wolf3d/code/iphone/wolf3dAppDelegate.h @@ -24,10 +24,12 @@ @class EAGLView; -@interface wolf3dAppDelegate : NSObject { +@interface wolf3dAppDelegate : NSObject { UIWindow *window; EAGLView *glView; int lastAccelUpdateMsec; + +// UIAlertView *alertPurchaseSpear; } @property (nonatomic, retain) IBOutlet UIWindow *window; diff --git a/wolf3d/code/iphone/wolf3dAppDelegate.m b/wolf3d/code/iphone/wolf3dAppDelegate.m index 4c030e6..64073aa 100644 --- a/wolf3d/code/iphone/wolf3dAppDelegate.m +++ b/wolf3d/code/iphone/wolf3dAppDelegate.m @@ -22,6 +22,36 @@ #import "EAGLView.h" #import #include "../wolfiphone.h" +#include + +//added for downloading SOD content from the internet... added by gsh +extern void Com_Printf(const char* fmt, ... ); +//extern void AppendData(NSData* data); +extern void AppendDataToFile(NSData* data); +extern void AppendUserDataToFile(NSData* data); +//extern void SaveData(); + +//used for downloading custom made map +extern void FinalizeUserDownload(); + +#if SPEARSTOREKIT +//was used for storekit +extern void FinalizeDownload(); +extern int IsSpearPurchased(); +extern int IsSpearInstalled(); +extern void BeginStoreKit(); +extern void GetSpear(); +#endif + +extern void DownloadURLConnection(char *url); + +extern int wasCalledFromDownloadInstructionsMenu; +extern void iphoneSet2D(); + + +//extern bool isAlive; + + @interface UIApplication (Private) @@ -67,11 +97,47 @@ void SysIPhoneVibrate() { UIAccelerometer *accelerometer = [UIAccelerometer sharedAccelerometer]; accelerometer.delegate = self; accelerometer.updateInterval = 0.01; - + // do all the game startup work iphoneStartup(); + +#if SPEARSTOREKIT + //check if user downloaded spear but didn't purchase + if (IsSpearInstalled() && !IsSpearPurchased()) + { + alertPurchaseSpear = [[UIAlertView alloc] initWithTitle:@""//Title" + message:@"You have downloaded and installed Spear of Destiny. Would you like to unlock those levels by purchasing it for $1.99?" + delegate:self + cancelButtonTitle:@"No" + otherButtonTitles:@"Yes", nil]; + + [alertPurchaseSpear show]; + } +#endif + } +//this is so that we can respond to alertView events (messageboxes) +//but this should only respond to the alertPurchaseSpear +- (void)alertView:(UIAlertView *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex +{ + //this is if they clicked yes to going to the map instructions website + if (wasCalledFromDownloadInstructionsMenu && buttonIndex == 1) { + SysIPhoneOpenURL( "http://www.idsoftware.com/wolfenstein-3d-classic-platinum/mapinstructions/" ); + return; + } + + //if they clicked yes to going to the idsoftware website + if (buttonIndex == 1) + SysIPhoneOpenURL( "http://www.idsoftware.com/wolfenstein-3d-classic-platinum/" ); + + /* + if (alertPurchaseSpear && (alertPurchaseSpear == actionSheet)) + { + if (buttonIndex != actionSheet.cancelButtonIndex) + BeginStoreKit(); + }*/ +} - (void)applicationWillResignActive:(UIApplication *)application { } @@ -84,10 +150,70 @@ void SysIPhoneVibrate() { iphoneShutdown(); } +#if 1 +extern char urlbuffer[1024]; - (BOOL)application:(UIApplication *)application handleOpenURL:(NSURL *)url { // wolf3d:foo should launch wolf3d now... next, add useful URL parameter encoding + + +#ifdef LITE + iphoneMessageBox("Lite version", "This is a Lite version. You must purchase Wolfenstein3D to get this feature."); + return NO; +#endif + + if (!url) + return NO; + + NSString *str = [url absoluteString]; //in the future we may wish to parse this string + //for commands that wolf3d might follow + //for now we'll just start the storekit + char buffer[1024]; + + if(![str getCString:buffer maxLength:1024 encoding:NSUTF8StringEncoding]) + { + iphoneMessageBox("Not Valid", "There were encoding errors. Make sure the link is less than 1023 characters in length."); + return NO; + } + + //iphoneMessageBox("URL", buffer); + /* + if (strcmp(buffer, "wolf3d:foo") == 0) + GetSpear(); + */ + char *pstr = buffer;//&buffer[strlen(buffer) - 5]; + pstr += strlen(buffer) - 4; + + if (strcmp(pstr, ".map") != 0) + { + iphoneMessageBox("Not Valid", "URL is not a valid map. Maps must end in \".map\""); + return NO; + } + + + if (strncmp(buffer, "wolf3dp:", 8) == 0) + { + + char cURL[1024]; + char *http = "http:"; + strcpy(cURL, http); + strcpy(cURL + 5, buffer + 8); + //strcpy(cURL, "http://gregory.hodges.googlepages.com/SODmm.tgz"); //TODO: delete me + Com_Printf("wolf3dp: installing map %s\n", cURL); + strcpy(urlbuffer, cURL); + //download and install this map + //iphoneMessageBox("wolf3d:", "it's a map TODO install"); + //InstallUserMap(); + DownloadURLConnection(cURL); + } + else + { + iphoneMessageBox("Not Valid", "URL is not a valid map. Identifier must start with \"wolf3dp:\""); + return NO; + } + return YES; } +#endif - (void)dealloc { @@ -101,7 +227,7 @@ void SysIPhoneVibrate() { // I have no idea why this seems to happen sometimes... if ( Sys_Milliseconds() - lastAccelUpdateMsec > 1000 ) { static int count; - if ( ++count < 100 ) { + if ( ++count < 5 ) { printf( "Restarting accelerometer updates.\n" ); } UIAccelerometer *accelerometer = [UIAccelerometer sharedAccelerometer]; @@ -121,6 +247,104 @@ void SysIPhoneVibrate() { lastAccelUpdateMsec = Sys_Milliseconds(); } +//------------------------------------------------------------ +// connection +// called by the app when data is received during download +// gsh +//------------------------------------------------------------- +- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data +{ + // lock the game out temporarily + pthread_mutex_t eventMutex; + pthread_mutex_lock( &eventMutex ); +/* + //display the network activity indicator + UIApplication* app = [UIApplication sharedApplication]; + app.networkActivityIndicatorVisible = YES; // to stop it, set this to NO + app.statusBarHidden = NO; +*/ + // append the new data to file + AppendUserDataToFile(data); + + // the game is free to copy the appendedData size now + pthread_mutex_unlock( &eventMutex ); +} + +//------------------------------------------------------------------------ +// connectionDidFinishLoading +// this is called when the app finishes downloading +// the new Spear of Destiny Levels +// gsh +//------------------------------------------------------------------------ +- (void) connectionDidFinishLoading:(NSURLConnection *)connection +{ + Com_Printf("connectionDidFinishLoading called\n"); +/* + //no need to display network traffic anymore + UIApplication* app = [UIApplication sharedApplication]; + app.networkActivityIndicatorVisible = NO; // to stop it, set this to NO + app.statusBarHidden = YES; +*/ + //release the connection + [connection release]; + + //uncompress/move files/delete unwanted + FinalizeUserDownload(); +} + +//------------------------------------------------------------------------ +// connection didFailWithError +// if the connection fails during the download then this is called +// gsh +//------------------------------------------------------------------------ +- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error +{ + //let user know that the connection failed + iphoneMessageBox("Connection Failed", "Please check your connection and try again later."); + + //return the user to the main menu + menuState = IPM_MAIN; +/* + //no need to display network traffic anymore + UIApplication* app = [UIApplication sharedApplication]; + app.networkActivityIndicatorVisible = NO; // to stop it, set this to NO + app.statusBarHidden = YES; */ +} +/* +- (void)setDownloadResponse:(NSURLResponse *)aDownloadResponse +{ + [aDownloadResponse retain]; + [downloadResponse release]; + downloadResponse = aDownloadResponse; +} + +- (void)download:(NSURLDownload *)download didReceiveResponse:(NSURLResponse *)response +{ + // reset the progress, this might be called multiple times + bytesReceived=0; + + // retain the response to use later + [self setDownloadResponse:response]; +} + +- (void)download:(NSURLDownload *)download didReceiveDataOfLength:(unsigned)length +{ + long long expectedLength=[[self downloadResponse] expectedContentLength]; + + bytesReceived=bytesReceived+length; + + if (expectedLength != NSURLResponseUnknownLength) { + // if the expected content length is + // available, display percent complete + float percentComplete=(bytesReceived/(float)expectedLength)*100.0; + NSLog(@"Percent complete - %f",percentComplete); + } else { + // if the expected content length is + // unknown just log the progress + NSLog(@"Bytes received - %d",bytesReceived); + } +} +*/ @end diff --git a/wolf3d/code/iphone/wolf3d_icon.png b/wolf3d/code/iphone/wolf3d_icon.png index 956a45a58e792de4b7948965e365d8d0726bf473..363eecabb8d4907bdb11a6a56bcd24480b9b0995 100644 GIT binary patch literal 8921 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 S84dXF0Hh$NDqACM8uA}%7ghZL literal 5935 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 REd>Ao002ovPDHLkV1hlXfaw4L diff --git a/wolf3d/code/wolf/wolf_actor_ai.c b/wolf3d/code/wolf/wolf_actor_ai.c index f07b8d8..ef46fe2 100644 --- a/wolf3d/code/wolf/wolf_actor_ai.c +++ b/wolf3d/code/wolf/wolf_actor_ai.c @@ -71,8 +71,8 @@ PUBLIC void A_DeathScream( entity_t *self ) { switch( self->type ) { - case en_mutant: - if( g_version->value == SPEAROFDESTINY ) + case en_mutant: //added 0's to all of these in order to check if sounds are correct... gsh + if( g_version->value == SPEAROFDESTINY && currentMap.episode >= 6 && currentMap.episode < 9)//added the episode check... gsh ) { Sound_StartSound( NULL, 1, CHAN_VOICE, Sound_RegisterSound( "sfx/033.wav" ), 1, ATTN_NORM, 0 ); } @@ -83,7 +83,7 @@ PUBLIC void A_DeathScream( entity_t *self ) break; case en_guard: - if( g_version->value == SPEAROFDESTINY ) + if( g_version->value == SPEAROFDESTINY && currentMap.episode >= 6 && currentMap.episode < 9)//added the episode check... gsh ) { Sound_StartSound( NULL, 1, CHAN_VOICE, Sound_RegisterSound( dsodsounds[ US_RndT() % 6 ] ), 1, ATTN_NORM, 0 ); } @@ -94,7 +94,7 @@ PUBLIC void A_DeathScream( entity_t *self ) break; case en_officer: - if( g_version->value == SPEAROFDESTINY ) + if( g_version->value == SPEAROFDESTINY && currentMap.episode >= 6 && currentMap.episode < 9)//added the episode check... gsh ) { Sound_StartSound( NULL, 1, CHAN_VOICE, Sound_RegisterSound( "sfx/046.wav" ), 1, ATTN_NORM, 0 ); } @@ -105,7 +105,7 @@ PUBLIC void A_DeathScream( entity_t *self ) break; case en_ss: - if( g_version->value == SPEAROFDESTINY ) + if( g_version->value == SPEAROFDESTINY && currentMap.episode >= 6 && currentMap.episode < 9)//added the episode check... gsh ) { Sound_StartSound( NULL, 1, CHAN_VOICE, Sound_RegisterSound( "sfx/035.wav" ), 1, ATTN_NORM, 0 ); } @@ -116,7 +116,7 @@ PUBLIC void A_DeathScream( entity_t *self ) break; case en_dog: - if( g_version->value == SPEAROFDESTINY ) + if( g_version->value == SPEAROFDESTINY && currentMap.episode >= 6 && currentMap.episode < 9)//added the episode check... gsh ) { Sound_StartSound( NULL, 1, CHAN_VOICE, Sound_RegisterSound( "sfx/031.wav" ), 1, ATTN_NORM, 0 ); } @@ -208,7 +208,7 @@ PUBLIC void A_FirstSighting( entity_t *self ) break; case en_officer: - if( g_version->value == SPEAROFDESTINY ) + if( g_version->value == SPEAROFDESTINY && currentMap.episode >= 6 && currentMap.episode < 9)//added the episode check... gsh ) { Sound_StartSound( NULL, 1, CHAN_VOICE, Sound_RegisterSound( "sfx/043.wav" ), 1, ATTN_NORM, 0 ); } @@ -918,7 +918,7 @@ PUBLIC void T_Projectile( entity_t *self ) { if( self->type == en_rocket || self->type == en_hrocket ) { // rocket ran into obstacle, draw explosion! - if( g_version->value == SPEAROFDESTINY ) + if( g_version->value == SPEAROFDESTINY && currentMap.episode >= 6 && currentMap.episode < 9)//added the episode check... gsh ) { Sound_StartSound( NULL, 1, CHAN_WEAPON, Sound_RegisterSound( "lsfx/001.wav" ), 1, ATTN_NORM, 0 ); } diff --git a/wolf3d/code/wolf/wolf_ai_com.c b/wolf3d/code/wolf/wolf_ai_com.c index dcdc6a4..59bf310 100644 --- a/wolf3d/code/wolf/wolf_ai_com.c +++ b/wolf3d/code/wolf/wolf_ai_com.c @@ -924,7 +924,8 @@ PUBLIC void T_Bite( entity_t *self ) { long dx, dy; - Sound_StartSound( NULL, 1, CHAN_VOICE, Sound_RegisterSound( "lsfx/076.wav" ), 1, ATTN_NORM, 0 ); +// Sound_StartSound( NULL, 1, CHAN_VOICE, Sound_RegisterSound( "lsfx/076.wav" ), 1, ATTN_NORM, 0 ); //gsh this was the original code + Sound_StartSound( NULL, 1, CHAN_VOICE, Sound_RegisterSound( "sfx/002.wav" ), 1, ATTN_NORM, 0 ); //gsh changed to this... the original code wasn't the correct sound file dx = ABS( Player.position.origin[ 0 ] - self->x ) - TILEGLOBAL; if( dx <= MINACTORDIST ) @@ -1156,7 +1157,7 @@ PUBLIC void T_Shoot( entity_t *self ) switch( self->type ) { case en_ss: - if( g_version->value == SPEAROFDESTINY ) + if( g_version->value == SPEAROFDESTINY && currentMap.episode >= 6 && currentMap.episode < 9 )//added the episode check... gsh) { Sound_StartSound( NULL, 1, CHAN_WEAPON, Sound_RegisterSound( "sfx/020.wav" ), 1, ATTN_NORM, 0 ); } @@ -1175,7 +1176,7 @@ PUBLIC void T_Shoot( entity_t *self ) break; default: - if( g_version->value == SPEAROFDESTINY ) + if( g_version->value == SPEAROFDESTINY && currentMap.episode >= 6 && currentMap.episode < 9)//added the episode check... gsh) { Sound_StartSound( NULL, 1, CHAN_WEAPON, Sound_RegisterSound( "sfx/038.wav" ), 1, ATTN_NORM, 0 ); } @@ -1302,7 +1303,7 @@ PUBLIC void T_Launch( entity_t *self ) default: proj->type = en_rocket; - if( g_version->value == SPEAROFDESTINY ) + if( g_version->value == SPEAROFDESTINY && currentMap.episode >= 6 && currentMap.episode < 9)//added the episode check... gsh) { Sound_StartSound( NULL, 1, CHAN_WEAPON, Sound_RegisterSound( "lsfx/008.wav" ), 1, ATTN_NORM, 0 ); } diff --git a/wolf3d/code/wolf/wolf_client_main.c b/wolf3d/code/wolf/wolf_client_main.c index 3b608d7..b6af805 100644 --- a/wolf3d/code/wolf/wolf_client_main.c +++ b/wolf3d/code/wolf/wolf_client_main.c @@ -19,6 +19,20 @@ */ #include "../wolfiphone.h" +/* +void ReplaceText(char* original, char* insert, int nInsertAt) +{ + int sizeInsert = sizeof(insert); + int sizeOriginal = sizeof(original); //should be 1024 + + for (int i = 0; i < sizeInsert; i++) + { + original[nInsertAt + i] = insert[i]; + } + + + +}*/ /* ----------------------------------------------------------------------------- @@ -34,14 +48,15 @@ */ PUBLIC void Client_PrepRefresh( const char *r_mapname ) { - char mapname[ 32 ]; +// char mapname[ 32 ]; + char mapname[ 64 ]; //gsh, decided to allow longer map names if( ! r_mapname || ! *r_mapname ) { return; } - if( g_version->value == SPEAROFDESTINY ) + if( g_version->value == SPEAROFDESTINY && currentMap.episode >= 6 && currentMap.episode < 10)//added the episode check... gsh) { spritelocation = SODSPRITESDIRNAME; } @@ -78,7 +93,47 @@ PUBLIC void Client_PrepRefresh( const char *r_mapname ) // clear any lines of console text Con_ClearNotify(); - + + //gsh + //this is a hack... to save space on the download... we've removed the music + //so instead we're going to replace the SOD music with wolf3d music here + //however, we don't have to worry about that now that we package the SOD music + //with the binary + /* + if (currentMap.episode >= 6) //if it's the SOD levels + { + char *source; + switch (currentMap.episode * 10 + currentMap.map) + { + case 60: source = "ZEROHOUR.ogg"; break; + case 61: source = "CORNER.ogg"; break; + case 62: source = "DUNGEON.ogg"; break; + case 63: source = "ENDLEVEL.ogg"; break; + case 64: source = "FUNKYOU.ogg"; break; + case 65: source = "HEADACHE.ogg"; break; + case 66: source = "HITLWLTZ.ogg"; break; + case 67: source = "INTROCW3.ogg"; break; + case 68: source = "NAZI_NOR.ogg"; break; + case 69: source = "NAZI_OMI.ogg"; break; + case 70: source = "NAZI_RAP.ogg"; break; + case 71: source = "PACMAN.ogg"; break; + case 72: source = "POW.ogg"; break; + case 73: source = "PREGNANT.ogg"; break; + case 74: source = "ROSTER.ogg"; break; + case 75: source = "SALUTE.ogg"; break; + case 76: source = "SEARCHN.ogg"; break; + case 77: source = "SUSPENSE.ogg"; break; + case 78: source = "TWELFTH.ogg"; break; + case 79: source = "URAHERO.ogg"; break; + case 80: source = "ULTIMATE.ogg"; break; + default: + source = "CORNER.ogg"; + } + strcpy(levelData.musicName + 6, source); //the '6' is to get us past the "music/" part of musicName + levelData.musicName[6 + strlen(source)] = '\0'; + }*/ + + Com_Printf("Starting Music Track: %s\n", levelData.musicName); Sound_StartBGTrack( levelData.musicName, levelData.musicName ); Player.playstate = ex_playing; diff --git a/wolf3d/code/wolf/wolf_level.c b/wolf3d/code/wolf/wolf_level.c index 617a33b..4b29339 100644 --- a/wolf3d/code/wolf/wolf_level.c +++ b/wolf3d/code/wolf/wolf_level.c @@ -905,7 +905,7 @@ PUBLIC LevelData_t *Level_LoadMap( const char *levelname ) - if( g_version->value == SPEAROFDESTINY ) + if( g_version->value == SPEAROFDESTINY && currentMap.episode >= 6 && currentMap.episode < 9)//added the episode check... gsh) { statinfo = static_sod; num_statics = sizeof( static_sod ) / sizeof( static_sod[ 0 ] ); @@ -933,6 +933,7 @@ PUBLIC LevelData_t *Level_LoadMap( const char *levelname ) filesize = FS_GetFileSize( fhandle ); if( filesize < MAPHEADER_SIZE ) { + Com_Printf("Map file size is smaller than mapheader size\n"); return NULL; } @@ -943,6 +944,7 @@ PUBLIC LevelData_t *Level_LoadMap( const char *levelname ) FS_ReadFile( &signature, 1, 4, fhandle ); if( signature != MAP_SIGNATURE ) { + Com_Printf("File signature does not match MAP_SIGNATURE\n"); return NULL; } @@ -973,6 +975,7 @@ PUBLIC LevelData_t *Level_LoadMap( const char *levelname ) if( filesize < (MAPHEADER_SIZE + mapNameLength + musicNameLength + length[ 0 ] + length[ 1 ] + length[ 2 ]) ) { + Com_Printf("filesize is less than MAPHEADER_SIZE + mapNameLength + musicNameLength + etc\n"); return NULL; } @@ -990,6 +993,7 @@ PUBLIC LevelData_t *Level_LoadMap( const char *levelname ) if( filesize < (MAPHEADER_SIZE + mapNameLength + musicNameLength) ) { + Com_Printf("filesize is less than MAPHEADER_SIZE + mapNameLength + musicNameLength\n"); return NULL; } @@ -1165,6 +1169,114 @@ PUBLIC LevelData_t *Level_LoadMap( const char *levelname ) return newMap; } +/* + ----------------------------------------------------------------------------- + Function: Level_VerifyMap + + Parameters: level file name + + Returns: 0 if invalid map, 1 otherwise + + Notes: + + ----------------------------------------------------------------------------- + */ +PUBLIC int Level_VerifyMap( const char *levelname ) +{ + W16 rle; + W32 offset[ 3 ]; + W16 length[ 3 ]; + W16 w, h; + W32 signature; + W32 ceiling, floor; + filehandle_t *fhandle; + W16 mapNameLength; + char *mapName = NULL; + W16 musicNameLength; + char *musicName = NULL; + SW32 filesize; + int value = 1; + + + fhandle = FS_OpenFile( levelname, FA_FILE_IPHONE_DOC_DIR ); + if( ! fhandle ) + { + value = 0; + goto cleanup; + } + + filesize = FS_GetFileSize( fhandle ); + if( filesize < MAPHEADER_SIZE ) + { + value = 0; + goto cleanup; + } + + FS_ReadFile( &signature, 1, 4, fhandle ); + if( signature != MAP_SIGNATURE ) + { + value = 0; + goto cleanup; + } + + FS_ReadFile( &rle, 2, 1, fhandle ); + + FS_ReadFile( &w, 2, 1, fhandle ); + FS_ReadFile( &h, 2, 1, fhandle ); + + FS_ReadFile( &ceiling, 4, 1, fhandle ); + FS_ReadFile( &floor, 4, 1, fhandle ); + + + FS_ReadFile( &length, 2, 3, fhandle ); + FS_ReadFile( &offset, 4, 3, fhandle ); + + + FS_ReadFile( &mapNameLength, 1, 2, fhandle ); + FS_ReadFile( &musicNameLength, 1, 2, fhandle ); + + FS_ReadFile( &levelstate.fpartime, sizeof( float ), 1, fhandle ); + + FS_ReadFile( levelstate.spartime, sizeof( W8 ), 5, fhandle ); + levelstate.spartime[ 5 ] = '\0'; + + + if( filesize < (MAPHEADER_SIZE + mapNameLength + musicNameLength + + length[ 0 ] + length[ 1 ] + length[ 2 ]) ) + { + value = 0; + goto cleanup; + } + + mapName = Z_Malloc( mapNameLength + 1 ); + musicName = Z_Malloc( musicNameLength + 1 ); + + + FS_ReadFile( mapName, 1, mapNameLength, fhandle ); + mapName[ mapNameLength ] = '\0'; + + + FS_ReadFile( musicName, 1, musicNameLength, fhandle ); + musicName[ musicNameLength ] = '\0'; + + + if( filesize < (MAPHEADER_SIZE + mapNameLength + musicNameLength) ) + { + value = 0; + goto cleanup; + } + +cleanup: + FS_CloseFile(fhandle); + if (mapName) { + Z_Free(mapName); + } + if (musicName) { + Z_Free(musicName); + } + return value; +} + diff --git a/wolf3d/code/wolf/wolf_local.h b/wolf3d/code/wolf/wolf_local.h index 0c84640..f0586e4 100644 --- a/wolf3d/code/wolf/wolf_local.h +++ b/wolf3d/code/wolf/wolf_local.h @@ -140,8 +140,7 @@ extern void ProcessGuards( void ); #define WL6SPRITESDIRNAME "sprites" -#define SODSPRITESDIRNAME "sodsprites" - +#define SODSPRITESDIRNAME "sodsprites"//"sprites" //gsh was originally "sodsprites" extern char *spritelocation; diff --git a/wolf3d/code/wolf/wolf_main.c b/wolf3d/code/wolf/wolf_main.c index 141b829..cda6e6c 100644 --- a/wolf3d/code/wolf/wolf_main.c +++ b/wolf3d/code/wolf/wolf_main.c @@ -70,7 +70,9 @@ PUBLIC void Game_Init( void ) episode = Cvar_Get( "episode", "0", CVAR_ARCHIVE ); skill = Cvar_Get( "skill", "1", CVAR_ARCHIVE ); - g_version = Cvar_Get( "g_version", "0", CVAR_ARCHIVE ); +// g_version = Cvar_Get( "g_version", "0", CVAR_ARCHIVE ); + g_version = Cvar_Get( "g_version", "1", CVAR_ARCHIVE ); //we should make a #ifdef for "special version" + //this version is to come with spear #ifndef LITE Cmd_AddCommand( "map", Map_f ); diff --git a/wolf3d/code/wolf/wolf_player.c b/wolf3d/code/wolf/wolf_player.c index db10119..b47617b 100644 --- a/wolf3d/code/wolf/wolf_player.c +++ b/wolf3d/code/wolf/wolf_player.c @@ -83,6 +83,7 @@ PRIVATE _boolean PL_ChangeWeapon( player_t *self, int weapon ) return false; } + self->weapon = self->pendingweapon = weapon; @@ -461,7 +462,7 @@ PRIVATE void PL_PlayerAttack( player_t *self, _boolean re_attack ) ----------------------------------------------------------------------------- */ PUBLIC void PL_Process( player_t *self, LevelData_t *lvl ) -{ +{ int n; self->madenoise = false; @@ -489,11 +490,34 @@ PUBLIC void PL_Process( player_t *self, LevelData_t *lvl ) if( Player.cmd.buttons & BUTTON_ATTACK ) { self->flags |= PL_FLAG_ATTCK; - + + //gsh + if (self->previousweapon != WEAPON_KNIFE && self->previousweapon) + { + //self->weapon = self->previousweapon; + PL_ChangeWeapon(self, self->previousweapon); + } + self->attackframe = 0; self->attackcount = attackinfo[ self->weapon ][ 0 ].tics; self->weaponframe = attackinfo[ self->weapon ][ 0 ].frame; - } else if ( Player.cmd.buttons & BUTTON_CHANGE_WEAPON ) { + } + else if ( Player.cmd.buttons & BUTTON_ALTERNATE_ATTACK ) //gsh + { + self->flags |= PL_FLAG_ATTCK; + +// PL_ChangeWeapon(self, WEAPON_KNIFE); + if (self->weapon != WEAPON_KNIFE) + { + self->previousweapon = self->weapon; + self->weapon = WEAPON_KNIFE; + } + + self->attackframe = 0; + self->attackcount = attackinfo[ self->weapon ][ 0 ].tics; + self->weaponframe = attackinfo[ self->weapon ][ 0 ].frame; + } + else if ( Player.cmd.buttons & BUTTON_CHANGE_WEAPON ) { self->pendingweapon=self->weapon; for( n = 0 ; n < 4; ++n ) { @@ -559,9 +583,51 @@ PUBLIC void PL_Spawn( placeonplane_t location, LevelData_t *lvl ) Areas_ConnectAreas( Player.areanumber ); + //gsh + iphoneSetLevelNotifyText(); + /* char str[128]; - sprintf( str, "Entering level E%iM%i", currentMap.episode + 1, currentMap.map + 1 ); + //sprintf( str, "Entering level E%iM%i", currentMap.episode + 1, currentMap.map + 1 ); + //gsh + if (currentMap.episode < 6) + sprintf( str, "Entering level E%iM%i", currentMap.episode+1, currentMap.map+1 ); + else if (currentMap.episode < 10) { + int currentLevel = currentMap.episode * 10 + currentMap.map; + switch (currentLevel) { + case 60: case 61: case 62: case 63: case 64: + sprintf( str, "Entering Tunnels %i", currentLevel-60+1); + break; + case 78: + sprintf( str, "Entering Tunnels %i", 6); + break; + case 65: case 66: case 67: case 68: case 69: + sprintf( str, "Entering Dungeons %i", currentLevel-65+1); + break; + case 79: + sprintf( str, "Entering Dungeons %i", 6); + break; + case 70: case 71: case 72: case 73: case 74: case 75: + sprintf( str, "Entering Castle"); + break; + case 76: + sprintf( str, "Entering Ramparts"); + break; + case 77: + sprintf( str, "Entering Death Knight"); + break; + case 80: + sprintf( str, "Entering Dungeon Dimension"); + break; + default: + sprintf( str, " "); + break; + } + } + else + sprintf( str, "Entering level custom %i", /*currentMap.episode+1,* currentMap.map+1 ); + iphoneSetNotifyText( str ); + */ } /* @@ -918,6 +984,7 @@ PUBLIC void PL_NewGame( player_t *self ) self->ammo[ AMMO_BULLETS ] = 16; // JDC: changed for iphone 8; self->lives = 3; + self->previousweapon = WEAPON_KNIFE; //gsh self->weapon = self->pendingweapon = WEAPON_PISTOL; self->items = ITEM_WEAPON_1 | ITEM_WEAPON_2; self->next_extra = EXTRAPOINTS; @@ -973,6 +1040,7 @@ PUBLIC _boolean PL_Reborn( player_t *self ) self->weaponframe = 0; self->flags = 0; + self->previousweapon = WEAPON_KNIFE; //gsh self->weapon = self->pendingweapon = WEAPON_PISTOL; self->items = ITEM_WEAPON_1 | ITEM_WEAPON_2; diff --git a/wolf3d/code/wolf/wolf_player.h b/wolf3d/code/wolf/wolf_player.h index fb4eff5..2cf1012 100644 --- a/wolf3d/code/wolf/wolf_player.h +++ b/wolf3d/code/wolf/wolf_player.h @@ -135,6 +135,7 @@ typedef struct player_s int old_score, score, next_extra; unsigned items; // (keys, weapon) int weapon, pendingweapon; + int previousweapon; //gsh // additional info int attackframe, attackcount, weaponframe; // attack info unsigned flags; diff --git a/wolf3d/code/wolf/wolf_powerups.c b/wolf3d/code/wolf/wolf_powerups.c index 508e63f..0e6f08e 100644 --- a/wolf3d/code/wolf/wolf_powerups.c +++ b/wolf3d/code/wolf/wolf_powerups.c @@ -254,7 +254,8 @@ PRIVATE int Pow_Give( pow_t type ) { return 0; } - Sound_StartSound( NULL, 0, CHAN_ITEM, Sound_RegisterSound( "lsfx/064.wav" ), 1, ATTN_NORM, 0 ); +// Sound_StartSound( NULL, 0, CHAN_ITEM, Sound_RegisterSound( "lsfx/064.wav" ), 1, ATTN_NORM, 0 ); //gsh, I don't like this sound + Sound_StartSound( NULL, 0, CHAN_ITEM, Sound_RegisterSound( "lsfx/031.wav" ), 1, ATTN_NORM, 0 ); //gsh, I like this sound break; case pow_machinegun: @@ -290,14 +291,22 @@ PRIVATE int Pow_Give( pow_t type ) case pow_spear: { - char szTextMsg[ 256 ]; + //gsh char szTextMsg[ 256 ]; + + Com_Printf("Spear of Destiny picked up!!\n"); - Sound_StartSound( NULL, 0, CHAN_ITEM, Sound_RegisterSound( "sodsfx/109.wav" ), 1, ATTN_NORM, 0 ); + Sound_StartSound( NULL, 0, CHAN_ITEM, Sound_RegisterSound( "sfx/109.wav" ), 1, ATTN_NORM, 0 ); //gsh iphoneSetNotifyText( "Spear of Destiny" ); - - my_snprintf( szTextMsg, sizeof( szTextMsg ), +/* //gsh + my_snprintf( szTextMsg, sizeof( szTextMsg ), //this is supposed to load the last level... but it isn't "loading ; map s%.2d.map\n", 20 ); Cbuf_AddText( szTextMsg ); + */ + + //this might be a bit of a hack. But it works. + //Load the last level... gsh + iphoneStartMap(8, 0, currentMap.skill); + } break; diff --git a/wolf3d/code/wolf/wolf_pushwalls.c b/wolf3d/code/wolf/wolf_pushwalls.c index b040d7d..41fb6c4 100644 --- a/wolf3d/code/wolf/wolf_pushwalls.c +++ b/wolf3d/code/wolf/wolf_pushwalls.c @@ -96,7 +96,7 @@ PUBLIC _boolean PushWall_Push( int x, int y, dir4type dir ) iphoneSetNotifyText( "You found a secret!" ); } - if( g_version->value == SPEAROFDESTINY ) + if( g_version->value == SPEAROFDESTINY && currentMap.episode >= 6 && currentMap.episode < 9)//added the episode check... gsh ).. TODO: fix sfx and other media { Sound_StartSound( NULL, 1, CHAN_AUTO, Sound_RegisterSound( "sfx/030.wav" ), 1, ATTN_STATIC, 0 ); } diff --git a/wolf3d/code/wolf/wolf_renderer.c b/wolf3d/code/wolf/wolf_renderer.c index cf11bad..5a26c96 100644 --- a/wolf3d/code/wolf/wolf_renderer.c +++ b/wolf3d/code/wolf/wolf_renderer.c @@ -59,7 +59,14 @@ PUBLIC void R_BeginRegistration( const char *map ) ++texture_registration_sequence; - my_snprintf( fullname, sizeof( fullname ), "maps/%s.map", map ); + //my_snprintf( fullname, sizeof( fullname ), "maps/%s.map", map ); + if ( g_version->value == SPEAROFDESTINY && currentMap.episode >= 6 && currentMap.episode < 10) //add if/else... gsh + my_snprintf( fullname, sizeof( fullname ), "%s.map", map ); + else if (currentMap.episode >= 10) + my_snprintf( fullname, sizeof( fullname ), "%s.map", map ); + else + my_snprintf( fullname, sizeof( fullname ), "maps/%s.map", map ); + // Door_ResetDoors( &r_world->Doors ); Powerup_Reset(); @@ -74,13 +81,13 @@ PUBLIC void R_BeginRegistration( const char *map ) if( r_world == NULL ) { - Com_Printf( "Could not load map (%s)\n", map ); + Com_Printf( "Could not load map (%s) in R_BeginRegistration\n", map ); return; } levelstate.floornum = floornumber; - if( g_version->value == SPEAROFDESTINY ) + if( g_version->value == SPEAROFDESTINY && currentMap.episode >= 6 && currentMap.episode < 9) //added the episode check... gsh) { if( strlen( map ) >= 2 ) { diff --git a/wolf3d/code/wolfiphone.h b/wolf3d/code/wolfiphone.h index e44404f..84a2ab4 100644 --- a/wolf3d/code/wolfiphone.h +++ b/wolf3d/code/wolfiphone.h @@ -85,3 +85,5 @@ #include "iphone/iphone_wolf.h" +#include "iphone_alerts.h" +