mirror of
https://github.com/id-Software/DOOM-iOS.git
synced 2026-05-14 03:49:10 +02:00
The DOOM Classic for iPhone 1.0 source as released on November 3, 2009
This commit is contained in:
Vendored
BIN
Binary file not shown.
@@ -0,0 +1,539 @@
|
||||
/*
|
||||
* BackgroundMusic.cpp
|
||||
* doom
|
||||
*
|
||||
* Created by John Carmack on 5/15/09.
|
||||
* Copyright 2009 Id Software. All rights reserved.
|
||||
*
|
||||
*/
|
||||
/*
|
||||
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.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
//==================================================================================================
|
||||
// Includes
|
||||
//==================================================================================================
|
||||
|
||||
// System Includes
|
||||
#include <AudioToolbox/AudioToolbox.h>
|
||||
#include <CoreFoundation/CFURL.h>
|
||||
#include <OpenAL/al.h>
|
||||
#include <OpenAL/alc.h>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <pthread.h>
|
||||
#include <mach/mach.h>
|
||||
#include <string>
|
||||
|
||||
extern "C" {
|
||||
#include "doomiphone.h"
|
||||
}
|
||||
|
||||
enum {
|
||||
kSoundEngineErrUnitialized = 1,
|
||||
kSoundEngineErrInvalidID = 2,
|
||||
kSoundEngineErrFileNotFound = 3,
|
||||
kSoundEngineErrInvalidFileFormat = 4,
|
||||
kSoundEngineErrDeviceNotFound = 5
|
||||
};
|
||||
|
||||
|
||||
#define AssertNoError(inMessage, inHandler) \
|
||||
if(result != noErr) \
|
||||
{ \
|
||||
printf("%s: %d\n", inMessage, (int)result); \
|
||||
goto inHandler; \
|
||||
}
|
||||
|
||||
|
||||
#define kNumberBuffers 3
|
||||
|
||||
|
||||
static Float32 gMasterVolumeGain = 0.5f;
|
||||
|
||||
|
||||
//==================================================================================================
|
||||
// Helper functions
|
||||
//==================================================================================================
|
||||
|
||||
OSStatus LoadFileDataInfo(const char *inFilePath, AudioFileID &outAFID, AudioStreamBasicDescription &outFormat, UInt64 &outDataSize)
|
||||
{
|
||||
UInt32 thePropSize;
|
||||
|
||||
CFURLRef theURL = CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault, (UInt8*)inFilePath, strlen(inFilePath), false);
|
||||
if (theURL == NULL)
|
||||
return kSoundEngineErrFileNotFound;
|
||||
|
||||
OSStatus result = AudioFileOpenURL(theURL, kAudioFileReadPermission, 0, &outAFID);
|
||||
CFRelease(theURL);
|
||||
AssertNoError("Error opening file", end);
|
||||
|
||||
thePropSize = sizeof(outFormat);
|
||||
result = AudioFileGetProperty(outAFID, kAudioFilePropertyDataFormat, &thePropSize, &outFormat);
|
||||
AssertNoError("Error getting file format", end);
|
||||
|
||||
thePropSize = sizeof(UInt64);
|
||||
result = AudioFileGetProperty(outAFID, kAudioFilePropertyAudioDataByteCount, &thePropSize, &outDataSize);
|
||||
AssertNoError("Error getting file data size", end);
|
||||
|
||||
end:
|
||||
return result;
|
||||
}
|
||||
|
||||
void CalculateBytesForTime (AudioStreamBasicDescription & inDesc, UInt32 inMaxPacketSize, Float64 inSeconds, UInt32 *outBufferSize, UInt32 *outNumPackets)
|
||||
{
|
||||
static const UInt32 maxBufferSize = 0x10000; // limit size to 64K
|
||||
static const UInt32 minBufferSize = 0x4000; // limit size to 16K
|
||||
|
||||
if (inDesc.mFramesPerPacket) {
|
||||
Float64 numPacketsForTime = inDesc.mSampleRate / inDesc.mFramesPerPacket * inSeconds;
|
||||
*outBufferSize = (long unsigned int)numPacketsForTime * inMaxPacketSize;
|
||||
} else {
|
||||
// if frames per packet is zero, then the codec has no predictable packet == time
|
||||
// so we can't tailor this (we don't know how many Packets represent a time period
|
||||
// we'll just return a default buffer size
|
||||
*outBufferSize = maxBufferSize > inMaxPacketSize ? maxBufferSize : inMaxPacketSize;
|
||||
}
|
||||
|
||||
// we're going to limit our size to our default
|
||||
if (*outBufferSize > maxBufferSize && *outBufferSize > inMaxPacketSize)
|
||||
*outBufferSize = maxBufferSize;
|
||||
else {
|
||||
// also make sure we're not too small - we don't want to go the disk for too small chunks
|
||||
if (*outBufferSize < minBufferSize)
|
||||
*outBufferSize = minBufferSize;
|
||||
}
|
||||
*outNumPackets = *outBufferSize / inMaxPacketSize;
|
||||
}
|
||||
|
||||
static Boolean MatchFormatFlags(const AudioStreamBasicDescription& x, const AudioStreamBasicDescription& y)
|
||||
{
|
||||
UInt32 xFlags = x.mFormatFlags;
|
||||
UInt32 yFlags = y.mFormatFlags;
|
||||
|
||||
// match wildcards
|
||||
if (x.mFormatID == 0 || y.mFormatID == 0 || xFlags == 0 || yFlags == 0)
|
||||
return true;
|
||||
|
||||
if (x.mFormatID == kAudioFormatLinearPCM)
|
||||
{
|
||||
// knock off the all clear flag
|
||||
xFlags = xFlags & ~kAudioFormatFlagsAreAllClear;
|
||||
yFlags = yFlags & ~kAudioFormatFlagsAreAllClear;
|
||||
|
||||
// if both kAudioFormatFlagIsPacked bits are set, then we don't care about the kAudioFormatFlagIsAlignedHigh bit.
|
||||
if (xFlags & yFlags & kAudioFormatFlagIsPacked) {
|
||||
xFlags = xFlags & ~kAudioFormatFlagIsAlignedHigh;
|
||||
yFlags = yFlags & ~kAudioFormatFlagIsAlignedHigh;
|
||||
}
|
||||
|
||||
// if both kAudioFormatFlagIsFloat bits are set, then we don't care about the kAudioFormatFlagIsSignedInteger bit.
|
||||
if (xFlags & yFlags & kAudioFormatFlagIsFloat) {
|
||||
xFlags = xFlags & ~kAudioFormatFlagIsSignedInteger;
|
||||
yFlags = yFlags & ~kAudioFormatFlagIsSignedInteger;
|
||||
}
|
||||
|
||||
// if the bit depth is 8 bits or less and the format is packed, we don't care about endianness
|
||||
if((x.mBitsPerChannel <= 8) && ((xFlags & kAudioFormatFlagIsPacked) == kAudioFormatFlagIsPacked))
|
||||
{
|
||||
xFlags = xFlags & ~kAudioFormatFlagIsBigEndian;
|
||||
}
|
||||
if((y.mBitsPerChannel <= 8) && ((yFlags & kAudioFormatFlagIsPacked) == kAudioFormatFlagIsPacked))
|
||||
{
|
||||
yFlags = yFlags & ~kAudioFormatFlagIsBigEndian;
|
||||
}
|
||||
|
||||
// if the number of channels is 0 or 1, we don't care about non-interleavedness
|
||||
if (x.mChannelsPerFrame <= 1 && y.mChannelsPerFrame <= 1) {
|
||||
xFlags &= ~kLinearPCMFormatFlagIsNonInterleaved;
|
||||
yFlags &= ~kLinearPCMFormatFlagIsNonInterleaved;
|
||||
}
|
||||
}
|
||||
return xFlags == yFlags;
|
||||
}
|
||||
|
||||
Boolean FormatIsEqual(AudioStreamBasicDescription x, AudioStreamBasicDescription y)
|
||||
{
|
||||
// the semantics for equality are:
|
||||
// 1) Values must match exactly
|
||||
// 2) wildcard's are ignored in the comparison
|
||||
|
||||
#define MATCH(name) ((x.name) == 0 || (y.name) == 0 || (x.name) == (y.name))
|
||||
|
||||
return
|
||||
((x.mSampleRate==0.) || (y.mSampleRate==0.) || (x.mSampleRate==y.mSampleRate))
|
||||
&& MATCH(mFormatID)
|
||||
&& MatchFormatFlags(x, y)
|
||||
&& MATCH(mBytesPerPacket)
|
||||
&& MATCH(mFramesPerPacket)
|
||||
&& MATCH(mBytesPerFrame)
|
||||
&& MATCH(mChannelsPerFrame)
|
||||
&& MATCH(mBitsPerChannel) ;
|
||||
}
|
||||
|
||||
#pragma mark ***** BackgroundTrackMgr *****
|
||||
//==================================================================================================
|
||||
// BackgroundTrackMgr class
|
||||
//==================================================================================================
|
||||
typedef struct BG_FileInfo {
|
||||
std::string mFilePath;
|
||||
AudioFileID mAFID;
|
||||
AudioStreamBasicDescription mFileFormat;
|
||||
UInt64 mFileDataSize;
|
||||
//UInt64 mFileNumPackets; // this is only used if loading file to memory
|
||||
Boolean mLoadAtOnce;
|
||||
Boolean mFileDataInQueue;
|
||||
} BackgroundMusicFileInfo;
|
||||
|
||||
class BackgroundTrackMgr
|
||||
{
|
||||
public:
|
||||
BackgroundTrackMgr();
|
||||
~BackgroundTrackMgr();
|
||||
|
||||
void Teardown();
|
||||
|
||||
static void QueueCallback( void * inUserData, AudioQueueRef inAQ, AudioQueueBufferRef inCompleteAQBuffer);
|
||||
|
||||
OSStatus SetupQueue(BG_FileInfo *inFileInfo);
|
||||
OSStatus SetupBuffers(BG_FileInfo *inFileInfo);
|
||||
OSStatus LoadTrack(const char* inFilePath, Boolean inAddToQueue, Boolean inLoadAtOnce);
|
||||
|
||||
OSStatus SetVolume(Float32 inVolume);
|
||||
Float32 GetVolume() const;
|
||||
|
||||
OSStatus Start();
|
||||
OSStatus Stop(Boolean inStopAtEnd);
|
||||
|
||||
AudioQueueRef mQueue;
|
||||
AudioQueueBufferRef mBuffers[kNumberBuffers];
|
||||
UInt32 mBufferByteSize;
|
||||
SInt64 mCurrentPacket;
|
||||
UInt32 mNumPacketsToRead;
|
||||
Float32 mVolume;
|
||||
AudioStreamPacketDescription * mPacketDescs;
|
||||
static BG_FileInfo * CurFileInfo;
|
||||
Boolean mStopAtEnd;
|
||||
};
|
||||
|
||||
BG_FileInfo *BackgroundTrackMgr::CurFileInfo;
|
||||
|
||||
|
||||
BackgroundTrackMgr::BackgroundTrackMgr()
|
||||
: mQueue(0),
|
||||
mBufferByteSize(0),
|
||||
mCurrentPacket(0),
|
||||
mNumPacketsToRead(0),
|
||||
mVolume(1.0f),
|
||||
mPacketDescs(NULL),
|
||||
mStopAtEnd(false)
|
||||
{ }
|
||||
|
||||
BackgroundTrackMgr::~BackgroundTrackMgr() {
|
||||
Teardown();
|
||||
}
|
||||
|
||||
void BackgroundTrackMgr::Teardown() {
|
||||
if (mQueue) {
|
||||
AudioQueueDispose(mQueue, true);
|
||||
mQueue = NULL;
|
||||
}
|
||||
if ( CurFileInfo ) {
|
||||
AudioFileClose( CurFileInfo->mAFID);
|
||||
delete CurFileInfo;
|
||||
CurFileInfo = NULL;
|
||||
}
|
||||
if (mPacketDescs) {
|
||||
delete[] mPacketDescs;
|
||||
mPacketDescs = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void BackgroundTrackMgr::QueueCallback( void * inUserData, AudioQueueRef inAQ, AudioQueueBufferRef inCompleteAQBuffer ) {
|
||||
// dispose of the buffer if no longer in use
|
||||
OSStatus result = noErr;
|
||||
BackgroundTrackMgr *THIS = (BackgroundTrackMgr*)inUserData;
|
||||
UInt32 nPackets = 0;
|
||||
// loop the current buffer if the following:
|
||||
// 1. file was loaded into the buffer previously
|
||||
// 2. only one file in the queue
|
||||
// 3. we have not been told to stop at playlist completion
|
||||
if ((CurFileInfo->mFileDataInQueue) && (!THIS->mStopAtEnd)) {
|
||||
nPackets = THIS->mNumPacketsToRead;
|
||||
} else {
|
||||
UInt32 numBytes;
|
||||
while (nPackets == 0) {
|
||||
// if loadAtOnce, get all packets in the file, otherwise ~.5 seconds of data
|
||||
nPackets = THIS->mNumPacketsToRead;
|
||||
result = AudioFileReadPackets(CurFileInfo->mAFID, false, &numBytes, THIS->mPacketDescs, THIS->mCurrentPacket, &nPackets,
|
||||
inCompleteAQBuffer->mAudioData);
|
||||
AssertNoError("Error reading file data", end);
|
||||
|
||||
inCompleteAQBuffer->mAudioDataByteSize = numBytes;
|
||||
|
||||
if (nPackets == 0) { // no packets were read, this file has ended.
|
||||
if (CurFileInfo->mLoadAtOnce) {
|
||||
CurFileInfo->mFileDataInQueue = true;
|
||||
}
|
||||
|
||||
THIS->mCurrentPacket = 0;
|
||||
|
||||
// we have gone through the playlist. if mStopAtEnd, stop the queue here
|
||||
if ( THIS->mStopAtEnd ) {
|
||||
result = AudioQueueStop(inAQ, false);
|
||||
AssertNoError("Error stopping queue", end);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result = AudioQueueEnqueueBuffer(inAQ, inCompleteAQBuffer, (THIS->mPacketDescs ? nPackets : 0), THIS->mPacketDescs);
|
||||
if(result != noErr) {
|
||||
result = AudioQueueFreeBuffer(inAQ, inCompleteAQBuffer);
|
||||
AssertNoError("Error freeing buffers that didn't enqueue", end);
|
||||
}
|
||||
AssertNoError("Error enqueuing new buffer", end);
|
||||
if (CurFileInfo->mLoadAtOnce) {
|
||||
CurFileInfo->mFileDataInQueue = true;
|
||||
}
|
||||
|
||||
THIS->mCurrentPacket += nPackets;
|
||||
|
||||
end:
|
||||
return;
|
||||
}
|
||||
|
||||
OSStatus BackgroundTrackMgr::SetupQueue(BG_FileInfo *inFileInfo) {
|
||||
UInt32 size = 0;
|
||||
OSStatus result = AudioQueueNewOutput(&inFileInfo->mFileFormat, QueueCallback, this,
|
||||
CFRunLoopGetMain() /* CFRunLoopGetCurrent() */, kCFRunLoopCommonModes, 0, &mQueue);
|
||||
AssertNoError("Error creating queue", end);
|
||||
#if 0
|
||||
// (2) If the file has a cookie, we should get it and set it on the AQ
|
||||
size = sizeof(UInt32);
|
||||
result = AudioFileGetPropertyInfo (inFileInfo->mAFID, kAudioFilePropertyMagicCookieData, &size, NULL);
|
||||
|
||||
if (!result && size) {
|
||||
char* cookie = new char [size];
|
||||
result = AudioFileGetProperty (inFileInfo->mAFID, kAudioFilePropertyMagicCookieData, &size, cookie);
|
||||
AssertNoError("Error getting magic cookie", end);
|
||||
result = AudioQueueSetProperty(mQueue, kAudioQueueProperty_MagicCookie, cookie, size);
|
||||
delete [] cookie;
|
||||
AssertNoError("Error setting magic cookie", end);
|
||||
}
|
||||
#endif
|
||||
// channel layout
|
||||
OSStatus err = AudioFileGetPropertyInfo(inFileInfo->mAFID, kAudioFilePropertyChannelLayout, &size, NULL);
|
||||
if (err == noErr && size > 0) {
|
||||
AudioChannelLayout *acl = (AudioChannelLayout *)malloc(size);
|
||||
result = AudioFileGetProperty(inFileInfo->mAFID, kAudioFilePropertyChannelLayout, &size, acl);
|
||||
AssertNoError("Error getting channel layout from file", end);
|
||||
result = AudioQueueSetProperty(mQueue, kAudioQueueProperty_ChannelLayout, acl, size);
|
||||
free(acl);
|
||||
AssertNoError("Error setting channel layout on queue", end);
|
||||
}
|
||||
|
||||
// volume
|
||||
result = SetVolume(mVolume);
|
||||
|
||||
end:
|
||||
return result;
|
||||
}
|
||||
|
||||
OSStatus BackgroundTrackMgr::SetupBuffers(BG_FileInfo *inFileInfo) {
|
||||
int numBuffersToQueue = kNumberBuffers;
|
||||
UInt32 maxPacketSize;
|
||||
UInt32 size = sizeof(maxPacketSize);
|
||||
// we need to calculate how many packets we read at a time, and how big a buffer we need
|
||||
// we base this on the size of the packets in the file and an approximate duration for each buffer
|
||||
|
||||
// first check to see what the max size of a packet is - if it is bigger
|
||||
// than our allocation default size, that needs to become larger
|
||||
OSStatus result = AudioFileGetProperty(inFileInfo->mAFID, kAudioFilePropertyPacketSizeUpperBound, &size, &maxPacketSize);
|
||||
AssertNoError("Error getting packet upper bound size", end);
|
||||
bool isFormatVBR = (inFileInfo->mFileFormat.mBytesPerPacket == 0 || inFileInfo->mFileFormat.mFramesPerPacket == 0);
|
||||
|
||||
CalculateBytesForTime(inFileInfo->mFileFormat, maxPacketSize, 0.5/*seconds*/, &mBufferByteSize, &mNumPacketsToRead);
|
||||
|
||||
// if the file is smaller than the capacity of all the buffer queues, always load it at once
|
||||
if ((mBufferByteSize * numBuffersToQueue) > inFileInfo->mFileDataSize) {
|
||||
inFileInfo->mLoadAtOnce = true;
|
||||
}
|
||||
|
||||
if (inFileInfo->mLoadAtOnce) {
|
||||
UInt64 theFileNumPackets;
|
||||
size = sizeof(UInt64);
|
||||
result = AudioFileGetProperty(inFileInfo->mAFID, kAudioFilePropertyAudioDataPacketCount, &size, &theFileNumPackets);
|
||||
AssertNoError("Error getting packet count for file", end);
|
||||
|
||||
mNumPacketsToRead = (UInt32)theFileNumPackets;
|
||||
mBufferByteSize = (UInt32)inFileInfo->mFileDataSize;
|
||||
numBuffersToQueue = 1;
|
||||
} else {
|
||||
mNumPacketsToRead = mBufferByteSize / maxPacketSize;
|
||||
}
|
||||
|
||||
if (isFormatVBR) {
|
||||
mPacketDescs = new AudioStreamPacketDescription [mNumPacketsToRead];
|
||||
} else {
|
||||
mPacketDescs = NULL; // we don't provide packet descriptions for constant bit rate formats (like linear PCM)
|
||||
}
|
||||
|
||||
// allocate the queue's buffers
|
||||
for (int i = 0; i < numBuffersToQueue; ++i) {
|
||||
result = AudioQueueAllocateBuffer(mQueue, mBufferByteSize, &mBuffers[i]);
|
||||
AssertNoError("Error allocating buffer for queue", end);
|
||||
QueueCallback (this, mQueue, mBuffers[i]);
|
||||
if (inFileInfo->mLoadAtOnce) {
|
||||
inFileInfo->mFileDataInQueue = true;
|
||||
}
|
||||
}
|
||||
|
||||
end:
|
||||
return result;
|
||||
}
|
||||
|
||||
OSStatus BackgroundTrackMgr::LoadTrack(const char* inFilePath, Boolean inAddToQueue, Boolean inLoadAtOnce) {
|
||||
// OSStatus result = LoadFileDataInfo(CurFileInfo->mFilePath.c_str(), CurFileInfo->mAFID, CurFileInfo->mFileFormat, CurFileInfo->mFileDataSize);
|
||||
// AssertNoError("Error getting file data info", fail);
|
||||
OSStatus result;
|
||||
UInt32 thePropSize;
|
||||
|
||||
CFURLRef theURL = CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault, (UInt8*)inFilePath, strlen(inFilePath), false);
|
||||
if (theURL == NULL)
|
||||
result = kSoundEngineErrFileNotFound;
|
||||
else
|
||||
result = 0;
|
||||
AssertNoError("Error opening URL", fail);
|
||||
|
||||
CurFileInfo = new BG_FileInfo;
|
||||
CurFileInfo->mFilePath = inFilePath;
|
||||
|
||||
result = AudioFileOpenURL(theURL, kAudioFileReadPermission, 0, &CurFileInfo->mAFID);
|
||||
CFRelease(theURL);
|
||||
AssertNoError("Error opening file", fail);
|
||||
|
||||
thePropSize = sizeof(CurFileInfo->mFileFormat);
|
||||
result = AudioFileGetProperty(CurFileInfo->mAFID, kAudioFilePropertyDataFormat, &thePropSize, &CurFileInfo->mFileFormat);
|
||||
AssertNoError("Error getting file format", fail);
|
||||
|
||||
thePropSize = sizeof(UInt64);
|
||||
result = AudioFileGetProperty(CurFileInfo->mAFID, kAudioFilePropertyAudioDataByteCount, &thePropSize, &CurFileInfo->mFileDataSize);
|
||||
AssertNoError("Error getting file data size", fail);
|
||||
|
||||
CurFileInfo->mLoadAtOnce = inLoadAtOnce;
|
||||
CurFileInfo->mFileDataInQueue = false;
|
||||
|
||||
result = SetupQueue(CurFileInfo);
|
||||
AssertNoError("Error setting up queue", fail);
|
||||
|
||||
result = SetupBuffers(CurFileInfo);
|
||||
AssertNoError("Error setting up queue buffers", fail);
|
||||
|
||||
return result;
|
||||
|
||||
fail:
|
||||
if (CurFileInfo) {
|
||||
delete CurFileInfo;
|
||||
CurFileInfo = NULL;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
OSStatus BackgroundTrackMgr::SetVolume(Float32 inVolume) {
|
||||
mVolume = inVolume;
|
||||
return AudioQueueSetParameter(mQueue, kAudioQueueParam_Volume, mVolume * gMasterVolumeGain);
|
||||
}
|
||||
|
||||
Float32 BackgroundTrackMgr::GetVolume() const {
|
||||
return mVolume;
|
||||
}
|
||||
|
||||
OSStatus BackgroundTrackMgr::Start() {
|
||||
OSStatus result = AudioQueuePrime(mQueue, 1, NULL);
|
||||
if (result) {
|
||||
printf("BackgroundTrackMgr: Error priming queue: %d\n", (int)result);
|
||||
return result;
|
||||
}
|
||||
return AudioQueueStart(mQueue, NULL);
|
||||
}
|
||||
|
||||
OSStatus BackgroundTrackMgr::Stop(Boolean inStopAtEnd) {
|
||||
if (inStopAtEnd) {
|
||||
mStopAtEnd = true;
|
||||
return noErr;
|
||||
} else {
|
||||
return AudioQueueStop(mQueue, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static BackgroundTrackMgr sBackgroundTrackMgr;
|
||||
|
||||
static char currentMusicName[1024];
|
||||
|
||||
void iphonePauseMusic() {
|
||||
if ( music->value == 0 ) {
|
||||
// music is disabled
|
||||
return;
|
||||
}
|
||||
AudioQueuePause(sBackgroundTrackMgr.mQueue);
|
||||
}
|
||||
void iphoneResumeMusic() {
|
||||
if ( music->value == 0 ) {
|
||||
// music is disabled
|
||||
return;
|
||||
}
|
||||
AudioQueueStart(sBackgroundTrackMgr.mQueue,NULL);
|
||||
}
|
||||
void iphoneStopMusic() {
|
||||
sBackgroundTrackMgr.Teardown();
|
||||
}
|
||||
|
||||
void iphoneStartMusic() {
|
||||
if ( music->value == 0 ) {
|
||||
// music is disabled
|
||||
return;
|
||||
}
|
||||
char fullName[1024];
|
||||
sprintf( fullName, "%s/base/music/d_%s.mp3", SysIphoneGetAppDir(), currentMusicName );
|
||||
|
||||
printf( "Starting music '%s'\n", fullName );
|
||||
|
||||
iphoneStopMusic();
|
||||
sBackgroundTrackMgr.LoadTrack( fullName, false, true);
|
||||
sBackgroundTrackMgr.Start();
|
||||
|
||||
if ( !strcmp( currentMusicName, "intro" ) ) {
|
||||
// stop the intro music at end, don't loop
|
||||
sBackgroundTrackMgr.mStopAtEnd = true;
|
||||
} else {
|
||||
sBackgroundTrackMgr.mStopAtEnd = false;
|
||||
}
|
||||
}
|
||||
|
||||
void iphonePlayMusic( const char *name ) {
|
||||
strcpy( currentMusicName, name );
|
||||
|
||||
iphoneStartMusic();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,574 @@
|
||||
// !$*UTF8*$!
|
||||
{
|
||||
1D6058900D05DD3D006BFB54 /* Doom */ = {
|
||||
activeExec = 0;
|
||||
executables = (
|
||||
ED9BAB90108380AC00166CDA /* Doom */,
|
||||
);
|
||||
};
|
||||
29B97313FDCFA39411CA2CEA /* Project object */ = {
|
||||
activeBuildConfigurationName = Debug;
|
||||
activeExecutable = ED9BAB90108380AC00166CDA /* Doom */;
|
||||
activeSDKPreference = iphonesimulator2.0;
|
||||
activeTarget = 1D6058900D05DD3D006BFB54 /* Doom */;
|
||||
addToTargets = (
|
||||
1D6058900D05DD3D006BFB54 /* Doom */,
|
||||
);
|
||||
codeSenseManager = ED9BABB0108380C600166CDA /* Code sense */;
|
||||
executables = (
|
||||
ED9BAB90108380AC00166CDA /* Doom */,
|
||||
);
|
||||
perUserDictionary = {
|
||||
PBXConfiguration.PBXFileTableDataSource3.PBXBookmarksDataSource = {
|
||||
PBXFileTableDataSourceColumnSortingDirectionKey = "-1";
|
||||
PBXFileTableDataSourceColumnSortingKey = PBXBookmarksDataSource_NameID;
|
||||
PBXFileTableDataSourceColumnWidthsKey = (
|
||||
200,
|
||||
200,
|
||||
293.58349609375,
|
||||
);
|
||||
PBXFileTableDataSourceColumnsKey = (
|
||||
PBXBookmarksDataSource_LocationID,
|
||||
PBXBookmarksDataSource_NameID,
|
||||
PBXBookmarksDataSource_CommentsID,
|
||||
);
|
||||
};
|
||||
PBXConfiguration.PBXFileTableDataSource3.PBXFileTableDataSource = {
|
||||
PBXFileTableDataSourceColumnSortingDirectionKey = "-1";
|
||||
PBXFileTableDataSourceColumnSortingKey = PBXFileDataSource_Filename_ColumnID;
|
||||
PBXFileTableDataSourceColumnWidthsKey = (
|
||||
20,
|
||||
484,
|
||||
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.PBXFileTableDataSource3.PBXFindDataSource = {
|
||||
PBXFileTableDataSourceColumnSortingDirectionKey = "-1";
|
||||
PBXFileTableDataSourceColumnSortingKey = PBXFindDataSource_LocationID;
|
||||
PBXFileTableDataSourceColumnWidthsKey = (
|
||||
200,
|
||||
498,
|
||||
);
|
||||
PBXFileTableDataSourceColumnsKey = (
|
||||
PBXFindDataSource_MessageID,
|
||||
PBXFindDataSource_LocationID,
|
||||
);
|
||||
};
|
||||
PBXConfiguration.PBXTargetDataSource.PBXTargetDataSource = {
|
||||
PBXFileTableDataSourceColumnSortingDirectionKey = "-1";
|
||||
PBXFileTableDataSourceColumnSortingKey = PBXFileDataSource_Filename_ColumnID;
|
||||
PBXFileTableDataSourceColumnWidthsKey = (
|
||||
20,
|
||||
444,
|
||||
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 = 279053176;
|
||||
PBXWorkspaceStateSaveDate = 279053176;
|
||||
};
|
||||
perUserProjectItems = {
|
||||
ED2D127F10838A65003A9380 /* PBXBookmark */ = ED2D127F10838A65003A9380 /* PBXBookmark */;
|
||||
ED2D128210838A65003A9380 /* PBXBookmark */ = ED2D128210838A65003A9380 /* PBXBookmark */;
|
||||
ED9AB3E410967730000B5852 /* PlistBookmark */ = ED9AB3E410967730000B5852 /* PlistBookmark */;
|
||||
ED9AB3E510967730000B5852 /* PBXTextBookmark */ = ED9AB3E510967730000B5852 /* PBXTextBookmark */;
|
||||
ED9AB3E610967730000B5852 /* PBXTextBookmark */ = ED9AB3E610967730000B5852 /* PBXTextBookmark */;
|
||||
ED9AB3E710967730000B5852 /* PBXTextBookmark */ = ED9AB3E710967730000B5852 /* PBXTextBookmark */;
|
||||
ED9AB3E910967730000B5852 /* PBXTextBookmark */ = ED9AB3E910967730000B5852 /* PBXTextBookmark */;
|
||||
ED9AB3EC10967730000B5852 /* PBXTextBookmark */ = ED9AB3EC10967730000B5852 /* PBXTextBookmark */;
|
||||
ED9AB3ED10967730000B5852 /* PBXTextBookmark */ = ED9AB3ED10967730000B5852 /* PBXTextBookmark */;
|
||||
ED9AB3EF10967730000B5852 /* PBXTextBookmark */ = ED9AB3EF10967730000B5852 /* PBXTextBookmark */;
|
||||
ED9AB3F210967730000B5852 /* PBXTextBookmark */ = ED9AB3F210967730000B5852 /* PBXTextBookmark */;
|
||||
ED9AB3F310967730000B5852 /* PBXTextBookmark */ = ED9AB3F310967730000B5852 /* PBXTextBookmark */;
|
||||
ED9BABDF1083850C00166CDA /* PlistBookmark */ = ED9BABDF1083850C00166CDA /* PlistBookmark */;
|
||||
ED9BABE11083850C00166CDA /* PlistBookmark */ = ED9BABE11083850C00166CDA /* PlistBookmark */;
|
||||
ED9BABE21083850C00166CDA /* PlistBookmark */ = ED9BABE21083850C00166CDA /* PlistBookmark */;
|
||||
EDAFC7F4109A575F002C3487 /* PBXTextBookmark */ = EDAFC7F4109A575F002C3487 /* PBXTextBookmark */;
|
||||
EDAFC7F5109A575F002C3487 /* PBXTextBookmark */ = EDAFC7F5109A575F002C3487 /* PBXTextBookmark */;
|
||||
EDAFC7F7109A575F002C3487 /* PBXTextBookmark */ = EDAFC7F7109A575F002C3487 /* PBXTextBookmark */;
|
||||
EDAFC7F8109A575F002C3487 /* PBXTextBookmark */ = EDAFC7F8109A575F002C3487 /* PBXTextBookmark */;
|
||||
EDAFC7F9109A575F002C3487 /* PBXTextBookmark */ = EDAFC7F9109A575F002C3487 /* PBXTextBookmark */;
|
||||
EDAFC818109A5B94002C3487 /* PBXTextBookmark */ = EDAFC818109A5B94002C3487 /* PBXTextBookmark */;
|
||||
EDAFC819109A5B94002C3487 /* PBXTextBookmark */ = EDAFC819109A5B94002C3487 /* PBXTextBookmark */;
|
||||
EDAFC81B109A5B94002C3487 /* PBXTextBookmark */ = EDAFC81B109A5B94002C3487 /* PBXTextBookmark */;
|
||||
EDFDF9B310A2054E0071CB9B /* PBXTextBookmark */ = EDFDF9B310A2054E0071CB9B /* PBXTextBookmark */;
|
||||
EDFDF9B410A2054E0071CB9B /* PBXTextBookmark */ = EDFDF9B410A2054E0071CB9B /* PBXTextBookmark */;
|
||||
EDFDF9B610A2054E0071CB9B /* PBXTextBookmark */ = EDFDF9B610A2054E0071CB9B /* PBXTextBookmark */;
|
||||
EDFDF9B710A2054E0071CB9B /* PBXTextBookmark */ = EDFDF9B710A2054E0071CB9B /* PBXTextBookmark */;
|
||||
EDFDF9C110A205F50071CB9B /* PBXTextBookmark */ = EDFDF9C110A205F50071CB9B /* PBXTextBookmark */;
|
||||
EDFDF9C310A205F50071CB9B /* PBXTextBookmark */ = EDFDF9C310A205F50071CB9B /* PBXTextBookmark */;
|
||||
EDFDF9C410A205F50071CB9B /* PBXTextBookmark */ = EDFDF9C410A205F50071CB9B /* PBXTextBookmark */;
|
||||
EDFDF9C810A2114E0071CB9B /* PBXTextBookmark */ = EDFDF9C810A2114E0071CB9B /* PBXTextBookmark */;
|
||||
};
|
||||
sourceControlManager = ED9BABAF108380C600166CDA /* Source Control */;
|
||||
userBuildSettings = {
|
||||
};
|
||||
};
|
||||
434669950F8D058400EA7D6D /* Doom_icon.png */ = {
|
||||
uiCtxt = {
|
||||
sepNavWindowFrame = "{{15, 250}, {800, 773}}";
|
||||
};
|
||||
};
|
||||
43CF03090F56D5C200E4A23D /* iphone_loop.c */ = {
|
||||
uiCtxt = {
|
||||
sepNavIntBoundsRect = "{{0, 0}, {732, 24640}}";
|
||||
sepNavSelRange = "{6159, 14}";
|
||||
sepNavVisRange = "{5677, 1070}";
|
||||
};
|
||||
};
|
||||
43E8D2DF0F4FC61E003F09B2 /* iphone_main.c */ = {
|
||||
uiCtxt = {
|
||||
sepNavIntBoundsRect = "{{0, 0}, {720, 7154}}";
|
||||
sepNavSelRange = "{8344, 0}";
|
||||
sepNavVisRange = "{8241, 409}";
|
||||
};
|
||||
};
|
||||
7229CC8E0F6B3363004123C5 /* doomiphone.h */ = {
|
||||
uiCtxt = {
|
||||
sepNavIntBoundsRect = "{{0, 0}, {662, 1512}}";
|
||||
sepNavSelRange = "{2492, 0}";
|
||||
sepNavVisRange = "{1199, 1295}";
|
||||
};
|
||||
};
|
||||
72A55EEE1003A94300F788A5 /* iphone_start.c */ = {
|
||||
uiCtxt = {
|
||||
sepNavIntBoundsRect = "{{0, 0}, {662, 3066}}";
|
||||
sepNavSelRange = "{0, 0}";
|
||||
sepNavVisRange = "{0, 757}";
|
||||
};
|
||||
};
|
||||
72A7E8F60F5F2063005B83C0 /* iphone_menus.c */ = {
|
||||
uiCtxt = {
|
||||
sepNavIntBoundsRect = "{{0, 0}, {662, 13314}}";
|
||||
sepNavSelRange = "{5284, 13}";
|
||||
sepNavVisRange = "{4701, 1236}";
|
||||
};
|
||||
};
|
||||
72D50DBA0F8ED98000BB49E6 /* ipak.h */ = {
|
||||
uiCtxt = {
|
||||
sepNavIntBoundsRect = "{{0, 0}, {662, 2478}}";
|
||||
sepNavSelRange = "{0, 0}";
|
||||
sepNavVisRange = "{287, 1502}";
|
||||
};
|
||||
};
|
||||
72D50DBB0F8ED98000BB49E6 /* ipak.c */ = {
|
||||
uiCtxt = {
|
||||
sepNavIntBoundsRect = "{{0, 0}, {864, 5586}}";
|
||||
sepNavSelRange = "{578, 8}";
|
||||
sepNavVisRange = "{1113, 1914}";
|
||||
};
|
||||
};
|
||||
72E731EA0F97E68100E702CD /* iphone_sound.c */ = {
|
||||
uiCtxt = {
|
||||
sepNavIntBoundsRect = "{{0, 0}, {864, 4214}}";
|
||||
sepNavSelRange = "{4675, 0}";
|
||||
sepNavVisRange = "{3563, 1771}";
|
||||
};
|
||||
};
|
||||
72E847CA0F94096C00AB3C99 /* SDL_opengl.h */ = {
|
||||
uiCtxt = {
|
||||
sepNavIntBoundsRect = "{{0, 0}, {870, 3024}}";
|
||||
sepNavSelRange = "{0, 0}";
|
||||
sepNavVisRange = "{0, 1327}";
|
||||
};
|
||||
};
|
||||
72E8495E0F942B9300AB3C99 /* misc.c */ = {
|
||||
uiCtxt = {
|
||||
sepNavIntBoundsRect = "{{0, 0}, {742, 1218}}";
|
||||
sepNavSelRange = "{1221, 0}";
|
||||
sepNavVisRange = "{1132, 433}";
|
||||
};
|
||||
};
|
||||
ED2D127F10838A65003A9380 /* PBXBookmark */ = {
|
||||
isa = PBXBookmark;
|
||||
fRef = 434669950F8D058400EA7D6D /* Doom_icon.png */;
|
||||
};
|
||||
ED2D128210838A65003A9380 /* PBXBookmark */ = {
|
||||
isa = PBXBookmark;
|
||||
fRef = 434669950F8D058400EA7D6D /* Doom_icon.png */;
|
||||
};
|
||||
ED9AB3CF10966E85000B5852 /* iphone_email.h */ = {
|
||||
uiCtxt = {
|
||||
sepNavIntBoundsRect = "{{0, 0}, {662, 621}}";
|
||||
sepNavSelRange = "{319, 0}";
|
||||
sepNavVisRange = "{0, 319}";
|
||||
};
|
||||
};
|
||||
ED9AB3D010966E85000B5852 /* iphone_email.m */ = {
|
||||
uiCtxt = {
|
||||
sepNavIntBoundsRect = "{{0, 0}, {750, 3024}}";
|
||||
sepNavSelRange = "{0, 0}";
|
||||
sepNavVisRange = "{0, 1042}";
|
||||
};
|
||||
};
|
||||
ED9AB3E410967730000B5852 /* PlistBookmark */ = {
|
||||
isa = PlistBookmark;
|
||||
fRef = 8D1107310486CEB800E47090 /* Info.plist */;
|
||||
fallbackIsa = PBXBookmark;
|
||||
isK = 0;
|
||||
kPath = (
|
||||
UIPrerenderedIcon,
|
||||
);
|
||||
name = /Users/greghodges/doom/code/iphone/Info.plist;
|
||||
rLen = 0;
|
||||
rLoc = 2147483647;
|
||||
};
|
||||
ED9AB3E510967730000B5852 /* PBXTextBookmark */ = {
|
||||
isa = PBXTextBookmark;
|
||||
fRef = 72A55EEE1003A94300F788A5 /* iphone_start.c */;
|
||||
name = "iphone_start.c: 1";
|
||||
rLen = 0;
|
||||
rLoc = 0;
|
||||
rType = 0;
|
||||
vrLen = 757;
|
||||
vrLoc = 0;
|
||||
};
|
||||
ED9AB3E610967730000B5852 /* PBXTextBookmark */ = {
|
||||
isa = PBXTextBookmark;
|
||||
fRef = ED9AB3CF10966E85000B5852 /* iphone_email.h */;
|
||||
name = "iphone_email.h: 15";
|
||||
rLen = 0;
|
||||
rLoc = 319;
|
||||
rType = 0;
|
||||
vrLen = 319;
|
||||
vrLoc = 0;
|
||||
};
|
||||
ED9AB3E710967730000B5852 /* PBXTextBookmark */ = {
|
||||
isa = PBXTextBookmark;
|
||||
fRef = ED9AB3D010966E85000B5852 /* iphone_email.m */;
|
||||
name = "iphone_email.m: 1";
|
||||
rLen = 0;
|
||||
rLoc = 0;
|
||||
rType = 0;
|
||||
vrLen = 1042;
|
||||
vrLoc = 0;
|
||||
};
|
||||
ED9AB3E910967730000B5852 /* PBXTextBookmark */ = {
|
||||
isa = PBXTextBookmark;
|
||||
fRef = 7229CC8E0F6B3363004123C5 /* doomiphone.h */;
|
||||
name = "doomiphone.h: 106";
|
||||
rLen = 0;
|
||||
rLoc = 2492;
|
||||
rType = 0;
|
||||
vrLen = 1295;
|
||||
vrLoc = 1199;
|
||||
};
|
||||
ED9AB3EC10967730000B5852 /* PBXTextBookmark */ = {
|
||||
isa = PBXTextBookmark;
|
||||
fRef = ED9AB3CF10966E85000B5852 /* iphone_email.h */;
|
||||
name = "iphone_email.h: 15";
|
||||
rLen = 0;
|
||||
rLoc = 319;
|
||||
rType = 0;
|
||||
vrLen = 319;
|
||||
vrLoc = 0;
|
||||
};
|
||||
ED9AB3ED10967730000B5852 /* PBXTextBookmark */ = {
|
||||
isa = PBXTextBookmark;
|
||||
fRef = 72A55EEE1003A94300F788A5 /* iphone_start.c */;
|
||||
name = "iphone_start.c: 1";
|
||||
rLen = 0;
|
||||
rLoc = 0;
|
||||
rType = 0;
|
||||
vrLen = 757;
|
||||
vrLoc = 0;
|
||||
};
|
||||
ED9AB3EF10967730000B5852 /* PBXTextBookmark */ = {
|
||||
isa = PBXTextBookmark;
|
||||
fRef = ED9AB3D010966E85000B5852 /* iphone_email.m */;
|
||||
name = "iphone_email.m: 1";
|
||||
rLen = 0;
|
||||
rLoc = 0;
|
||||
rType = 0;
|
||||
vrLen = 1042;
|
||||
vrLoc = 0;
|
||||
};
|
||||
ED9AB3F210967730000B5852 /* PBXTextBookmark */ = {
|
||||
isa = PBXTextBookmark;
|
||||
fRef = 43E8D2DF0F4FC61E003F09B2 /* iphone_main.c */;
|
||||
name = "iphone_main.c: 334";
|
||||
rLen = 0;
|
||||
rLoc = 8790;
|
||||
rType = 0;
|
||||
vrLen = 1755;
|
||||
vrLoc = 7979;
|
||||
};
|
||||
ED9AB3F310967730000B5852 /* PBXTextBookmark */ = {
|
||||
isa = PBXTextBookmark;
|
||||
fRef = 7229CC8E0F6B3363004123C5 /* doomiphone.h */;
|
||||
name = "doomiphone.h: 106";
|
||||
rLen = 0;
|
||||
rLoc = 2492;
|
||||
rType = 0;
|
||||
vrLen = 1295;
|
||||
vrLoc = 1199;
|
||||
};
|
||||
ED9BAB90108380AC00166CDA /* Doom */ = {
|
||||
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 = Doom;
|
||||
savedGlobals = {
|
||||
};
|
||||
sourceDirectories = (
|
||||
);
|
||||
};
|
||||
ED9BABAF108380C600166CDA /* Source Control */ = {
|
||||
isa = PBXSourceControlManager;
|
||||
fallbackIsa = XCSourceControlManager;
|
||||
isSCMEnabled = 0;
|
||||
scmConfiguration = {
|
||||
};
|
||||
};
|
||||
ED9BABB0108380C600166CDA /* Code sense */ = {
|
||||
isa = PBXCodeSenseManager;
|
||||
indexTemplatePath = "";
|
||||
};
|
||||
ED9BABDF1083850C00166CDA /* PlistBookmark */ = {
|
||||
isa = PlistBookmark;
|
||||
fRef = 4364BF3E0F5CB25900F29317 /* dist.plist */;
|
||||
fallbackIsa = PBXBookmark;
|
||||
isK = 0;
|
||||
kPath = (
|
||||
);
|
||||
name = /Users/greghodges/doom/code/iphone/dist.plist;
|
||||
rLen = 0;
|
||||
rLoc = 2147483647;
|
||||
};
|
||||
ED9BABE11083850C00166CDA /* PlistBookmark */ = {
|
||||
isa = PlistBookmark;
|
||||
fRef = 8D1107310486CEB800E47090 /* Info.plist */;
|
||||
fallbackIsa = PBXBookmark;
|
||||
isK = 0;
|
||||
kPath = (
|
||||
CFBundleIconFile,
|
||||
);
|
||||
name = /Users/greghodges/doom/code/iphone/Info.plist;
|
||||
rLen = 0;
|
||||
rLoc = 2147483647;
|
||||
};
|
||||
ED9BABE21083850C00166CDA /* PlistBookmark */ = {
|
||||
isa = PlistBookmark;
|
||||
fRef = 4364BF3E0F5CB25900F29317 /* dist.plist */;
|
||||
fallbackIsa = PBXBookmark;
|
||||
isK = 0;
|
||||
kPath = (
|
||||
);
|
||||
name = /Users/greghodges/doom/code/iphone/dist.plist;
|
||||
rLen = 0;
|
||||
rLoc = 2147483647;
|
||||
};
|
||||
EDAFC7F4109A575F002C3487 /* PBXTextBookmark */ = {
|
||||
isa = PBXTextBookmark;
|
||||
fRef = 72A7E8F60F5F2063005B83C0 /* iphone_menus.c */;
|
||||
name = "iphone_menus.c: 201";
|
||||
rLen = 13;
|
||||
rLoc = 5284;
|
||||
rType = 0;
|
||||
vrLen = 1236;
|
||||
vrLoc = 4701;
|
||||
};
|
||||
EDAFC7F5109A575F002C3487 /* PBXTextBookmark */ = {
|
||||
isa = PBXTextBookmark;
|
||||
fRef = 43CF03090F56D5C200E4A23D /* iphone_loop.c */;
|
||||
name = "iphone_loop.c: 212";
|
||||
rLen = 14;
|
||||
rLoc = 6159;
|
||||
rType = 0;
|
||||
vrLen = 1070;
|
||||
vrLoc = 5677;
|
||||
};
|
||||
EDAFC7F7109A575F002C3487 /* PBXTextBookmark */ = {
|
||||
isa = PBXTextBookmark;
|
||||
fRef = 72E8495E0F942B9300AB3C99 /* misc.c */;
|
||||
name = "misc.c: 40";
|
||||
rLen = 0;
|
||||
rLoc = 768;
|
||||
rType = 0;
|
||||
vrLen = 807;
|
||||
vrLoc = 0;
|
||||
};
|
||||
EDAFC7F8109A575F002C3487 /* PBXTextBookmark */ = {
|
||||
isa = PBXTextBookmark;
|
||||
fRef = 72A7E8F60F5F2063005B83C0 /* iphone_menus.c */;
|
||||
name = "iphone_menus.c: 201";
|
||||
rLen = 13;
|
||||
rLoc = 5284;
|
||||
rType = 0;
|
||||
vrLen = 1236;
|
||||
vrLoc = 4701;
|
||||
};
|
||||
EDAFC7F9109A575F002C3487 /* PBXTextBookmark */ = {
|
||||
isa = PBXTextBookmark;
|
||||
fRef = 43CF03090F56D5C200E4A23D /* iphone_loop.c */;
|
||||
name = "iphone_loop.c: 212";
|
||||
rLen = 14;
|
||||
rLoc = 6159;
|
||||
rType = 0;
|
||||
vrLen = 1070;
|
||||
vrLoc = 5677;
|
||||
};
|
||||
EDAFC818109A5B94002C3487 /* PBXTextBookmark */ = {
|
||||
isa = PBXTextBookmark;
|
||||
fRef = 72D50DBB0F8ED98000BB49E6 /* ipak.c */;
|
||||
name = "ipak.c: 28";
|
||||
rLen = 7;
|
||||
rLoc = 467;
|
||||
rType = 0;
|
||||
vrLen = 1190;
|
||||
vrLoc = 359;
|
||||
};
|
||||
EDAFC819109A5B94002C3487 /* PBXTextBookmark */ = {
|
||||
isa = PBXTextBookmark;
|
||||
fRef = 43E8D2DF0F4FC61E003F09B2 /* iphone_main.c */;
|
||||
name = "iphone_main.c: 325";
|
||||
rLen = 7;
|
||||
rLoc = 8411;
|
||||
rType = 0;
|
||||
vrLen = 1755;
|
||||
vrLoc = 7979;
|
||||
};
|
||||
EDAFC81B109A5B94002C3487 /* PBXTextBookmark */ = {
|
||||
isa = PBXTextBookmark;
|
||||
fRef = 72D50DBB0F8ED98000BB49E6 /* ipak.c */;
|
||||
name = "ipak.c: 28";
|
||||
rLen = 7;
|
||||
rLoc = 467;
|
||||
rType = 0;
|
||||
vrLen = 1190;
|
||||
vrLoc = 359;
|
||||
};
|
||||
EDFDF9B310A2054E0071CB9B /* PBXTextBookmark */ = {
|
||||
isa = PBXTextBookmark;
|
||||
fRef = 72D50DBA0F8ED98000BB49E6 /* ipak.h */;
|
||||
name = "ipak.h: 1";
|
||||
rLen = 0;
|
||||
rLoc = 0;
|
||||
rType = 0;
|
||||
vrLen = 1502;
|
||||
vrLoc = 287;
|
||||
};
|
||||
EDFDF9B410A2054E0071CB9B /* PBXTextBookmark */ = {
|
||||
isa = PBXTextBookmark;
|
||||
fRef = 72E8495E0F942B9300AB3C99 /* misc.c */;
|
||||
name = "misc.c: 60";
|
||||
rLen = 0;
|
||||
rLoc = 1496;
|
||||
rType = 0;
|
||||
vrLen = 964;
|
||||
vrLoc = 869;
|
||||
};
|
||||
EDFDF9B610A2054E0071CB9B /* PBXTextBookmark */ = {
|
||||
isa = PBXTextBookmark;
|
||||
fRef = 72D50DBA0F8ED98000BB49E6 /* ipak.h */;
|
||||
name = "ipak.h: 1";
|
||||
rLen = 0;
|
||||
rLoc = 0;
|
||||
rType = 0;
|
||||
vrLen = 1502;
|
||||
vrLoc = 287;
|
||||
};
|
||||
EDFDF9B710A2054E0071CB9B /* PBXTextBookmark */ = {
|
||||
isa = PBXTextBookmark;
|
||||
fRef = 72E8495E0F942B9300AB3C99 /* misc.c */;
|
||||
name = "misc.c: 60";
|
||||
rLen = 0;
|
||||
rLoc = 1496;
|
||||
rType = 0;
|
||||
vrLen = 964;
|
||||
vrLoc = 869;
|
||||
};
|
||||
EDFDF9C110A205F50071CB9B /* PBXTextBookmark */ = {
|
||||
isa = PBXTextBookmark;
|
||||
fRef = EDFDF9C210A205F50071CB9B /* alc.h */;
|
||||
name = "alc.h: 186";
|
||||
rLen = 0;
|
||||
rLoc = 3852;
|
||||
rType = 0;
|
||||
vrLen = 1365;
|
||||
vrLoc = 3211;
|
||||
};
|
||||
EDFDF9C210A205F50071CB9B /* alc.h */ = {
|
||||
isa = PBXFileReference;
|
||||
name = alc.h;
|
||||
path = /Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator2.0.sdk/System/Library/Frameworks/OpenAL.framework/Headers/alc.h;
|
||||
sourceTree = "<absolute>";
|
||||
};
|
||||
EDFDF9C310A205F50071CB9B /* PBXTextBookmark */ = {
|
||||
isa = PBXTextBookmark;
|
||||
fRef = 72E731EA0F97E68100E702CD /* iphone_sound.c */;
|
||||
name = "iphone_sound.c: 160";
|
||||
rLen = 16;
|
||||
rLoc = 5037;
|
||||
rType = 0;
|
||||
vrLen = 1820;
|
||||
vrLoc = 3514;
|
||||
};
|
||||
EDFDF9C410A205F50071CB9B /* PBXTextBookmark */ = {
|
||||
isa = PBXTextBookmark;
|
||||
fRef = EDFDF9C510A205F50071CB9B /* alc.h */;
|
||||
name = "alc.h: 186";
|
||||
rLen = 0;
|
||||
rLoc = 3852;
|
||||
rType = 0;
|
||||
vrLen = 1365;
|
||||
vrLoc = 3211;
|
||||
};
|
||||
EDFDF9C510A205F50071CB9B /* alc.h */ = {
|
||||
isa = PBXFileReference;
|
||||
name = alc.h;
|
||||
path = /Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator2.0.sdk/System/Library/Frameworks/OpenAL.framework/Headers/alc.h;
|
||||
sourceTree = "<absolute>";
|
||||
};
|
||||
EDFDF9C810A2114E0071CB9B /* PBXTextBookmark */ = {
|
||||
isa = PBXTextBookmark;
|
||||
fRef = 72E731EA0F97E68100E702CD /* iphone_sound.c */;
|
||||
name = "iphone_sound.c: 148";
|
||||
rLen = 0;
|
||||
rLoc = 4675;
|
||||
rType = 0;
|
||||
vrLen = 1771;
|
||||
vrLoc = 3563;
|
||||
};
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Binary file not shown.
|
After Width: | Height: | Size: 5.3 KiB |
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
|
||||
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 <UIKit/UIKit.h>
|
||||
#import <OpenGLES/EAGL.h>
|
||||
#import <OpenGLES/ES1/gl.h>
|
||||
#import <OpenGLES/ES1/glext.h>
|
||||
|
||||
/*
|
||||
This class wraps the CAEAGLLayer from CoreAnimation into a convenient UIView subclass.
|
||||
The view content is basically an EAGL surface you render your OpenGL scene into.
|
||||
Note that setting the view non-opaque will only work if the EAGL surface has an alpha channel.
|
||||
*/
|
||||
@interface EAGLView : UIView <UITextFieldDelegate> {
|
||||
@public
|
||||
UITextField *textField;
|
||||
|
||||
@private
|
||||
/* The pixel dimensions of the backbuffer */
|
||||
GLint backingWidth;
|
||||
GLint backingHeight;
|
||||
|
||||
/* OpenGL names for the renderbuffer and framebuffers used to render to this view */
|
||||
GLuint viewRenderbuffer, viewFramebuffer;
|
||||
|
||||
/* OpenGL name for the depth buffer that is attached to viewFramebuffer, if it exists (0 if it does not exist) */
|
||||
GLuint depthRenderbuffer;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,354 @@
|
||||
//
|
||||
// EAGLView.m
|
||||
// Doom
|
||||
//
|
||||
// Created by Cass Everitt on 2/20/09.
|
||||
// Copyright Id Software 2009. 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 <QuartzCore/QuartzCore.h>
|
||||
#import <OpenGLES/EAGLDrawable.h>
|
||||
|
||||
#import "EAGLView.h"
|
||||
#import "doomAppDelegate.h"
|
||||
|
||||
#include "doomiphone.h"
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
EAGLView *eaglview;
|
||||
EAGLContext *context;
|
||||
|
||||
@implementation EAGLView
|
||||
|
||||
// You must implement this method
|
||||
+ (Class)layerClass {
|
||||
return [CAEAGLLayer class];
|
||||
}
|
||||
|
||||
CAEAGLLayer *eaglLayer;
|
||||
|
||||
//The GL view is stored in the nib file. When it's unarchived it's sent -initWithCoder:
|
||||
- (id)initWithCoder:(NSCoder*)coder {
|
||||
self = [super initWithCoder:coder];
|
||||
|
||||
eaglview = self;
|
||||
|
||||
// allow multiple touch events
|
||||
self.multipleTouchEnabled = true;
|
||||
|
||||
// Get the layer
|
||||
eaglLayer = (CAEAGLLayer *)self.layer;
|
||||
|
||||
// set opaque so UIKit doesn't try to blend it over other layers
|
||||
eaglLayer.opaque = YES;
|
||||
|
||||
// set it to no-backing-retained so it can do ast pageflips instead
|
||||
// of update copies, and go to 565 bit depth for higher performance.
|
||||
eaglLayer.drawableProperties = [NSDictionary dictionaryWithObjectsAndKeys:
|
||||
|
||||
[NSNumber numberWithBool:NO],
|
||||
kEAGLDrawablePropertyRetainedBacking,
|
||||
|
||||
kEAGLColorFormatRGB565,
|
||||
/* kEAGLColorFormatRGBA8, */
|
||||
kEAGLDrawablePropertyColorFormat,
|
||||
|
||||
nil];
|
||||
|
||||
context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES1];
|
||||
assert( context );
|
||||
|
||||
if ( ![EAGLContext setCurrentContext:context]) {
|
||||
[self release];
|
||||
return nil;
|
||||
}
|
||||
|
||||
glGenFramebuffersOES(1, &viewFramebuffer);
|
||||
glGenRenderbuffersOES(1, &viewRenderbuffer);
|
||||
|
||||
glBindFramebufferOES(GL_FRAMEBUFFER_OES, viewFramebuffer);
|
||||
glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer);
|
||||
[context renderbufferStorage:GL_RENDERBUFFER_OES fromDrawable:(CAEAGLLayer*)self.layer];
|
||||
glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_COLOR_ATTACHMENT0_OES, GL_RENDERBUFFER_OES, viewRenderbuffer);
|
||||
|
||||
glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_WIDTH_OES, &backingWidth);
|
||||
glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_HEIGHT_OES, &backingHeight);
|
||||
|
||||
glGenRenderbuffersOES(1, &depthRenderbuffer);
|
||||
glBindRenderbufferOES(GL_RENDERBUFFER_OES, depthRenderbuffer);
|
||||
glRenderbufferStorageOES(GL_RENDERBUFFER_OES, GL_DEPTH_COMPONENT16_OES, backingWidth, backingHeight);
|
||||
glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_DEPTH_ATTACHMENT_OES, GL_RENDERBUFFER_OES, depthRenderbuffer);
|
||||
|
||||
if(glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES) != GL_FRAMEBUFFER_COMPLETE_OES) {
|
||||
NSLog(@"failed to make complete framebuffer object %x", glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES));
|
||||
}
|
||||
|
||||
glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer);
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void) handleTouches:(UIEvent*)event {
|
||||
int touchCount = 0;
|
||||
static int previousTouchCount;
|
||||
static int touchRover;
|
||||
int touchThisSequence[MAX_TOUCHES];
|
||||
|
||||
memset( touchThisSequence, 0, sizeof( touchThisSequence ) );
|
||||
|
||||
NSSet *touches = [event allTouches];
|
||||
// printf( "count: %i\n", [touches count] );
|
||||
|
||||
// lock the game out temporarily
|
||||
pthread_mutex_lock( &eventMutex );
|
||||
|
||||
for (UITouch *myTouch in touches)
|
||||
{
|
||||
CGPoint touchLocation = [myTouch locationInView:nil];
|
||||
|
||||
// handle landscape mode and flipping
|
||||
int x, y;
|
||||
if ( revLand->value ) {
|
||||
x = touchLocation.y;
|
||||
y = 319 - touchLocation.x;
|
||||
} else {
|
||||
x = 479 - touchLocation.y;
|
||||
y = touchLocation.x;
|
||||
}
|
||||
// printf( "%i, %i\n", x, y );
|
||||
touchCount++;
|
||||
|
||||
touch_t *t2;
|
||||
|
||||
// find which one it is closest to
|
||||
int minDist = 64 * 64; // allow up to 64 unit moves to be drags
|
||||
int minIndex = -1;
|
||||
for ( int i = 0 ; i < MAX_TOUCHES ; i++ ) {
|
||||
t2 = &sysTouches[i];
|
||||
if ( !t2->down ) {
|
||||
continue;
|
||||
}
|
||||
int dist = ( t2->x - x ) * ( t2->x - x ) + ( t2->y - y ) * ( t2->y - y );
|
||||
if ( dist < minDist ) {
|
||||
minDist = dist;
|
||||
minIndex = i;
|
||||
}
|
||||
}
|
||||
if ( minIndex != -1 ) {
|
||||
// reuse a touch
|
||||
sysTouches[minIndex].x = x;
|
||||
sysTouches[minIndex].y = y;
|
||||
if (myTouch.phase == UITouchPhaseEnded) {
|
||||
// if this was released before the game got to see it,
|
||||
// make it a special case
|
||||
if ( sysTouches[minIndex].stateCount == 1 ) {
|
||||
// leave it in the down state with a special count
|
||||
sysTouches[minIndex].stateCount = -1;
|
||||
// printf( "Tap release touch on a reuse\n" );
|
||||
} else {
|
||||
sysTouches[minIndex].down = false;
|
||||
sysTouches[minIndex].stateCount = 1;
|
||||
// printf( "Release touch on a reuse\n" );
|
||||
}
|
||||
} else {
|
||||
if (myTouch.phase == UITouchPhaseBegan) {
|
||||
sysTouches[minIndex].stateCount = 1;
|
||||
sysTouches[minIndex].controlOwner = NULL;
|
||||
// printf( "Begin touch on a reuse\n" );
|
||||
} else {
|
||||
// printf( "Drag touch on a reuse\n" );
|
||||
}
|
||||
sysTouches[minIndex].down = true;
|
||||
}
|
||||
touchThisSequence[minIndex] = true;
|
||||
} else {
|
||||
if ( myTouch.phase != UITouchPhaseBegan ) {
|
||||
printf( "Non-local touch wasn't a begin\n" );
|
||||
} else {
|
||||
// allocate a new one
|
||||
// grab the next rover spot
|
||||
// don't just use first-not-down, because that might
|
||||
// cause the release to be missed by the game code.
|
||||
int i, j;
|
||||
for ( j = 0 ; j < MAX_TOUCHES ; j++ ) {
|
||||
i = touchRover;
|
||||
t2 = &sysTouches[i];
|
||||
touchRover = ( touchRover + 1 ) % MAX_TOUCHES;
|
||||
if ( !t2->down ) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ( j == MAX_TOUCHES ) {
|
||||
printf( "MAX_TOUCHES, clearing everything!\n" );
|
||||
memset( sysTouches, 0, sizeof( sysTouches ) );
|
||||
continue;
|
||||
}
|
||||
// printf( "new touch down\n" );
|
||||
t2->x = x;
|
||||
t2->y = y;
|
||||
t2->down = true;
|
||||
t2->controlOwner = NULL;
|
||||
t2->stateCount = 1;
|
||||
|
||||
touchThisSequence[i] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Change any active touches to released if they weren't
|
||||
// in the touch set. This will happen if we forced a break because
|
||||
// a "moved" event was so large that it was very likely a release and
|
||||
// press of a different finger that happened to be in the same frame.
|
||||
for ( int i = 0 ; i < MAX_TOUCHES ; i++ ) {
|
||||
if ( sysTouches[i].down && !touchThisSequence[i] ) {
|
||||
printf( "clearing touch %i\n", i );
|
||||
sysTouches[i].down = false;
|
||||
sysTouches[i].stateCount = 0;
|
||||
touchCount--;
|
||||
}
|
||||
}
|
||||
|
||||
// toggle the console with four touches
|
||||
if ( touchCount == 4 && previousTouchCount != 4 ) {
|
||||
touchCount = 0; // won't get the releases, because the text field will eat them
|
||||
|
||||
if ( textField == nil ) {
|
||||
// do this before starting the textField, which
|
||||
// takes a long time
|
||||
// iphoneActivateConsole();
|
||||
|
||||
textField = [UITextField alloc];
|
||||
[textField initWithFrame:CGRectMake( 0, 0, 20, 20 ) ];
|
||||
[self addSubview:textField];
|
||||
[textField release];
|
||||
textField.hidden = true;
|
||||
textField.delegate = self;
|
||||
textField.autocapitalizationType = UITextAutocapitalizationTypeNone;
|
||||
textField.autocorrectionType = UITextAutocorrectionTypeNo;
|
||||
[textField becomeFirstResponder];
|
||||
} else {
|
||||
}
|
||||
}
|
||||
|
||||
// the game is free to copy the touches now
|
||||
pthread_mutex_unlock( &eventMutex );
|
||||
|
||||
previousTouchCount = touchCount;
|
||||
}
|
||||
|
||||
|
||||
- (void) touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event {
|
||||
// printf( "touchesBegan\n" );
|
||||
[self handleTouches:event];
|
||||
}
|
||||
|
||||
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
|
||||
// printf( "touchesMoved\n" );
|
||||
[self handleTouches:event];
|
||||
}
|
||||
|
||||
- (void) touchesEnded:(NSSet*)touches withEvent:(UIEvent*)event {
|
||||
// printf( "touchesEnded\n" );
|
||||
[self handleTouches:event];
|
||||
}
|
||||
|
||||
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
|
||||
// printf( "touchesCancelled\n" );
|
||||
[self handleTouches:event];
|
||||
}
|
||||
|
||||
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@implementation EAGLView (UITextFieldDelegate)
|
||||
|
||||
char consoleCommand[1024];
|
||||
|
||||
- (BOOL)textFieldShouldReturn:(UITextField *)_textField
|
||||
{
|
||||
if ( eaglview->textField == nil ) {
|
||||
return YES;
|
||||
}
|
||||
|
||||
// we can't just execute this, because we are running in another
|
||||
// thread, so fetch the line and the game will catch it next time
|
||||
// around
|
||||
|
||||
// lock the game out temporarily
|
||||
pthread_mutex_lock( &eventMutex );
|
||||
|
||||
const char *line = [ eaglview->textField.text UTF8String ];
|
||||
strncpy( consoleCommand, line, sizeof(consoleCommand)-1 );
|
||||
eaglview->textField.text = [ NSString stringWithUTF8String: "" ];
|
||||
|
||||
// put it away
|
||||
[textField resignFirstResponder];
|
||||
[textField removeFromSuperview];
|
||||
textField = nil;
|
||||
|
||||
// lock the game out temporarily
|
||||
pthread_mutex_unlock( &eventMutex );
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
const char * SysIPhoneGetConsoleTextField() {
|
||||
if ( eaglview->textField == nil ) {
|
||||
return "";
|
||||
}
|
||||
return [ eaglview->textField.text UTF8String ];
|
||||
}
|
||||
|
||||
void SysIPhoneSetConsoleTextField( const char * str) {
|
||||
assert( eaglview->textField != nil );
|
||||
eaglview->textField.text = [ NSString stringWithUTF8String: str ];
|
||||
}
|
||||
|
||||
void SysIPhoneSwapBuffers() {
|
||||
[context presentRenderbuffer:GL_RENDERBUFFER_OES];
|
||||
}
|
||||
|
||||
void SysIPhoneOpenURL( const char *url ) {
|
||||
Com_Printf( "OpenURL char *: %s\n", url );
|
||||
|
||||
NSString *nss = [NSString stringWithCString: url encoding: NSASCIIStringEncoding];
|
||||
[[UIApplication sharedApplication] openURL:[NSURL URLWithString: nss]];
|
||||
}
|
||||
|
||||
void SysIPhoneSetUIKitOrientation( int isLandscapeRight ) {
|
||||
if ( isLandscapeRight ) {
|
||||
[UIApplication sharedApplication].statusBarOrientation = UIInterfaceOrientationLandscapeRight;
|
||||
} else {
|
||||
[UIApplication sharedApplication].statusBarOrientation = UIInterfaceOrientationLandscapeLeft;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key></key>
|
||||
<string></string>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>English</string>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>${PRODUCT_NAME}</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>${EXECUTABLE_NAME}</string>
|
||||
<key>CFBundleIconFile</key>
|
||||
<string>${PRODUCT_NAME}_icon.png</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>${PROFILE_PREFIX}.${PRODUCT_NAME:identifier}</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>${PRODUCT_NAME}</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1.0</string>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
<true/>
|
||||
<key>NSMainNibFile</key>
|
||||
<string>MainWindow</string>
|
||||
<key>UIInterfaceOrientation</key>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<key>UIRequiresPersistentWiFi</key>
|
||||
<true/>
|
||||
<key>UIStatusBarHidden</key>
|
||||
<true/>
|
||||
<key>UIPrerenderedIcon</key>
|
||||
<false/>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -0,0 +1,233 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<archive type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="7.03">
|
||||
<data>
|
||||
<int key="IBDocument.SystemTarget">528</int>
|
||||
<string key="IBDocument.SystemVersion">9G55</string>
|
||||
<string key="IBDocument.InterfaceBuilderVersion">677</string>
|
||||
<string key="IBDocument.AppKitVersion">949.43</string>
|
||||
<string key="IBDocument.HIToolboxVersion">353.00</string>
|
||||
<object class="NSMutableArray" key="IBDocument.EditedObjectIDs">
|
||||
<bool key="EncodedWithXMLCoder">YES</bool>
|
||||
<integer value="8"/>
|
||||
</object>
|
||||
<object class="NSArray" key="IBDocument.PluginDependencies">
|
||||
<bool key="EncodedWithXMLCoder">YES</bool>
|
||||
<string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
|
||||
</object>
|
||||
<object class="NSMutableDictionary" key="IBDocument.Metadata">
|
||||
<bool key="EncodedWithXMLCoder">YES</bool>
|
||||
<object class="NSArray" key="dict.sortedKeys">
|
||||
<bool key="EncodedWithXMLCoder">YES</bool>
|
||||
</object>
|
||||
<object class="NSMutableArray" key="dict.values">
|
||||
<bool key="EncodedWithXMLCoder">YES</bool>
|
||||
</object>
|
||||
</object>
|
||||
<object class="NSMutableArray" key="IBDocument.RootObjects" id="1000">
|
||||
<bool key="EncodedWithXMLCoder">YES</bool>
|
||||
<object class="IBProxyObject" id="841351856">
|
||||
<string key="IBProxiedObjectIdentifier">IBFilesOwner</string>
|
||||
</object>
|
||||
<object class="IBProxyObject" id="191355593">
|
||||
<string key="IBProxiedObjectIdentifier">IBFirstResponder</string>
|
||||
</object>
|
||||
<object class="IBUICustomObject" id="664661524"/>
|
||||
<object class="IBUIWindow" id="380026005">
|
||||
<reference key="NSNextResponder"/>
|
||||
<int key="NSvFlags">1316</int>
|
||||
<object class="NSMutableArray" key="NSSubviews">
|
||||
<bool key="EncodedWithXMLCoder">YES</bool>
|
||||
<object class="IBUIView" id="773737154">
|
||||
<reference key="NSNextResponder" ref="380026005"/>
|
||||
<int key="NSvFlags">1298</int>
|
||||
<string key="NSFrameSize">{320, 480}</string>
|
||||
<reference key="NSSuperview" ref="380026005"/>
|
||||
<object class="NSColor" key="IBUIBackgroundColor">
|
||||
<int key="NSColorSpace">3</int>
|
||||
<bytes key="NSWhite">MQA</bytes>
|
||||
<object class="NSColorSpace" key="NSCustomColorSpace">
|
||||
<int key="NSID">2</int>
|
||||
</object>
|
||||
</object>
|
||||
<bool key="IBUIClearsContextBeforeDrawing">NO</bool>
|
||||
</object>
|
||||
</object>
|
||||
<object class="NSPSMatrix" key="NSFrameMatrix"/>
|
||||
<string key="NSFrameSize">{320, 480}</string>
|
||||
<reference key="NSSuperview"/>
|
||||
<object class="NSColor" key="IBUIBackgroundColor">
|
||||
<int key="NSColorSpace">1</int>
|
||||
<bytes key="NSRGB">MSAxIDEAA</bytes>
|
||||
</object>
|
||||
<bool key="IBUIClearsContextBeforeDrawing">NO</bool>
|
||||
<bool key="IBUIVisibleAtLaunch">YES</bool>
|
||||
</object>
|
||||
</object>
|
||||
<object class="IBObjectContainer" key="IBDocument.Objects">
|
||||
<object class="NSMutableArray" key="connectionRecords">
|
||||
<bool key="EncodedWithXMLCoder">YES</bool>
|
||||
<object class="IBConnectionRecord">
|
||||
<object class="IBCocoaTouchOutletConnection" key="connection">
|
||||
<string key="label">delegate</string>
|
||||
<reference key="source" ref="841351856"/>
|
||||
<reference key="destination" ref="664661524"/>
|
||||
</object>
|
||||
<int key="connectionID">4</int>
|
||||
</object>
|
||||
<object class="IBConnectionRecord">
|
||||
<object class="IBCocoaTouchOutletConnection" key="connection">
|
||||
<string key="label">window</string>
|
||||
<reference key="source" ref="664661524"/>
|
||||
<reference key="destination" ref="380026005"/>
|
||||
</object>
|
||||
<int key="connectionID">5</int>
|
||||
</object>
|
||||
<object class="IBConnectionRecord">
|
||||
<object class="IBCocoaTouchOutletConnection" key="connection">
|
||||
<string key="label">glView</string>
|
||||
<reference key="source" ref="664661524"/>
|
||||
<reference key="destination" ref="773737154"/>
|
||||
</object>
|
||||
<int key="connectionID">9</int>
|
||||
</object>
|
||||
</object>
|
||||
<object class="IBMutableOrderedSet" key="objectRecords">
|
||||
<object class="NSArray" key="orderedObjects">
|
||||
<bool key="EncodedWithXMLCoder">YES</bool>
|
||||
<object class="IBObjectRecord">
|
||||
<int key="objectID">0</int>
|
||||
<object class="NSArray" key="object" id="957960031">
|
||||
<bool key="EncodedWithXMLCoder">YES</bool>
|
||||
</object>
|
||||
<reference key="children" ref="1000"/>
|
||||
<nil key="parent"/>
|
||||
</object>
|
||||
<object class="IBObjectRecord">
|
||||
<int key="objectID">2</int>
|
||||
<reference key="object" ref="380026005"/>
|
||||
<object class="NSMutableArray" key="children">
|
||||
<bool key="EncodedWithXMLCoder">YES</bool>
|
||||
<reference ref="773737154"/>
|
||||
</object>
|
||||
<reference key="parent" ref="957960031"/>
|
||||
</object>
|
||||
<object class="IBObjectRecord">
|
||||
<int key="objectID">-1</int>
|
||||
<reference key="object" ref="841351856"/>
|
||||
<reference key="parent" ref="957960031"/>
|
||||
<string type="base64-UTF8" key="objectName">RmlsZSdzIE93bmVyA</string>
|
||||
</object>
|
||||
<object class="IBObjectRecord">
|
||||
<int key="objectID">3</int>
|
||||
<reference key="object" ref="664661524"/>
|
||||
<reference key="parent" ref="957960031"/>
|
||||
<string key="objectName">Game App Delegate</string>
|
||||
</object>
|
||||
<object class="IBObjectRecord">
|
||||
<int key="objectID">8</int>
|
||||
<reference key="object" ref="773737154"/>
|
||||
<reference key="parent" ref="380026005"/>
|
||||
</object>
|
||||
<object class="IBObjectRecord">
|
||||
<int key="objectID">-2</int>
|
||||
<reference key="object" ref="191355593"/>
|
||||
<reference key="parent" ref="957960031"/>
|
||||
</object>
|
||||
</object>
|
||||
</object>
|
||||
<object class="NSMutableDictionary" key="flattenedProperties">
|
||||
<bool key="EncodedWithXMLCoder">YES</bool>
|
||||
<object class="NSMutableArray" key="dict.sortedKeys">
|
||||
<bool key="EncodedWithXMLCoder">YES</bool>
|
||||
<string>-1.CustomClassName</string>
|
||||
<string>-2.CustomClassName</string>
|
||||
<string>2.IBAttributePlaceholdersKey</string>
|
||||
<string>2.IBEditorWindowLastContentRect</string>
|
||||
<string>2.IBPluginDependency</string>
|
||||
<string>3.CustomClassName</string>
|
||||
<string>3.IBPluginDependency</string>
|
||||
<string>8.CustomClassName</string>
|
||||
<string>8.IBPluginDependency</string>
|
||||
</object>
|
||||
<object class="NSMutableArray" key="dict.values">
|
||||
<bool key="EncodedWithXMLCoder">YES</bool>
|
||||
<string>UIApplication</string>
|
||||
<string>UIResponder</string>
|
||||
<object class="NSMutableDictionary">
|
||||
<bool key="EncodedWithXMLCoder">YES</bool>
|
||||
<object class="NSArray" key="dict.sortedKeys">
|
||||
<bool key="EncodedWithXMLCoder">YES</bool>
|
||||
</object>
|
||||
<object class="NSMutableArray" key="dict.values">
|
||||
<bool key="EncodedWithXMLCoder">YES</bool>
|
||||
</object>
|
||||
</object>
|
||||
<string>{{500, 343}, {320, 480}}</string>
|
||||
<string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
|
||||
<string>gameAppDelegate</string>
|
||||
<string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
|
||||
<string>EAGLView</string>
|
||||
<string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
|
||||
</object>
|
||||
</object>
|
||||
<object class="NSMutableDictionary" key="unlocalizedProperties">
|
||||
<bool key="EncodedWithXMLCoder">YES</bool>
|
||||
<object class="NSArray" key="dict.sortedKeys">
|
||||
<bool key="EncodedWithXMLCoder">YES</bool>
|
||||
</object>
|
||||
<object class="NSMutableArray" key="dict.values">
|
||||
<bool key="EncodedWithXMLCoder">YES</bool>
|
||||
</object>
|
||||
</object>
|
||||
<nil key="activeLocalization"/>
|
||||
<object class="NSMutableDictionary" key="localizations">
|
||||
<bool key="EncodedWithXMLCoder">YES</bool>
|
||||
<object class="NSArray" key="dict.sortedKeys">
|
||||
<bool key="EncodedWithXMLCoder">YES</bool>
|
||||
</object>
|
||||
<object class="NSMutableArray" key="dict.values">
|
||||
<bool key="EncodedWithXMLCoder">YES</bool>
|
||||
</object>
|
||||
</object>
|
||||
<nil key="sourceID"/>
|
||||
<int key="maxID">10</int>
|
||||
</object>
|
||||
<object class="IBClassDescriber" key="IBDocument.Classes">
|
||||
<object class="NSMutableArray" key="referencedPartialClassDescriptions">
|
||||
<bool key="EncodedWithXMLCoder">YES</bool>
|
||||
<object class="IBPartialClassDescription">
|
||||
<string key="className">EAGLView</string>
|
||||
<string key="superclassName">UIView</string>
|
||||
<object class="IBClassDescriptionSource" key="sourceIdentifier">
|
||||
<string key="majorKey">IBProjectSource</string>
|
||||
<string key="minorKey">EAGLView.h</string>
|
||||
</object>
|
||||
</object>
|
||||
<object class="IBPartialClassDescription">
|
||||
<string key="className">gameAppDelegate</string>
|
||||
<string key="superclassName">NSObject</string>
|
||||
<object class="NSMutableDictionary" key="outlets">
|
||||
<bool key="EncodedWithXMLCoder">YES</bool>
|
||||
<object class="NSMutableArray" key="dict.sortedKeys">
|
||||
<bool key="EncodedWithXMLCoder">YES</bool>
|
||||
<string>glView</string>
|
||||
<string>window</string>
|
||||
</object>
|
||||
<object class="NSMutableArray" key="dict.values">
|
||||
<bool key="EncodedWithXMLCoder">YES</bool>
|
||||
<string>EAGLView</string>
|
||||
<string>UIWindow</string>
|
||||
</object>
|
||||
</object>
|
||||
<object class="IBClassDescriptionSource" key="sourceIdentifier">
|
||||
<string key="majorKey">IBProjectSource</string>
|
||||
<string key="minorKey">doomAppDelegate.h</string>
|
||||
</object>
|
||||
</object>
|
||||
</object>
|
||||
</object>
|
||||
<int key="IBDocument.localizationMode">0</int>
|
||||
<string key="IBDocument.LastKnownRelativeProjectPath">doom.xcodeproj</string>
|
||||
<int key="IBDocument.defaultPropertyAccessControl">3</int>
|
||||
</data>
|
||||
</archive>
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,383 @@
|
||||
/*
|
||||
|
||||
File: SoundEngine.h
|
||||
Abstract: These functions play background music tracks, multiple sound effects,
|
||||
and support stereo panning with a low-latency response.
|
||||
|
||||
Version: 1.7
|
||||
|
||||
Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Inc.
|
||||
("Apple") in consideration of your agreement to the following terms, and your
|
||||
use, installation, modification or redistribution of this Apple software
|
||||
constitutes acceptance of these terms. If you do not agree with these terms,
|
||||
please do not use, install, modify or redistribute this Apple software.
|
||||
|
||||
In consideration of your agreement to abide by the following terms, and subject
|
||||
to these terms, Apple grants you a personal, non-exclusive license, under
|
||||
Apple's copyrights in this original Apple software (the "Apple Software"), to
|
||||
use, reproduce, modify and redistribute the Apple Software, with or without
|
||||
modifications, in source and/or binary forms; provided that if you redistribute
|
||||
the Apple Software in its entirety and without modifications, you must retain
|
||||
this notice and the following text and disclaimers in all such redistributions
|
||||
of the Apple Software.
|
||||
Neither the name, trademarks, service marks or logos of Apple Inc. may be used
|
||||
to endorse or promote products derived from the Apple Software without specific
|
||||
prior written permission from Apple. Except as expressly stated in this notice,
|
||||
no other rights or licenses, express or implied, are granted by Apple herein,
|
||||
including but not limited to any patent rights that may be infringed by your
|
||||
derivative works or by other works in which the Apple Software may be
|
||||
incorporated.
|
||||
|
||||
The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO
|
||||
WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED
|
||||
WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN
|
||||
COMBINATION WITH YOUR PRODUCTS.
|
||||
|
||||
IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
|
||||
GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR
|
||||
DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF
|
||||
CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF
|
||||
APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
Copyright (C) 2008 Apple Inc. All Rights Reserved.
|
||||
|
||||
*/
|
||||
|
||||
/*==================================================================================================
|
||||
SoundEngine.h
|
||||
==================================================================================================*/
|
||||
#if !defined(__SoundEngine_h__)
|
||||
#define __SoundEngine_h__
|
||||
|
||||
//==================================================================================================
|
||||
// Includes
|
||||
//==================================================================================================
|
||||
|
||||
// System Includes
|
||||
#ifndef WIN32
|
||||
#include <CoreAudio/CoreAudioTypes.h>
|
||||
#include <AudioToolbox/AudioToolbox.h>
|
||||
#else
|
||||
typedef int OSStatus;
|
||||
typedef int SInt32;
|
||||
typedef unsigned int UInt32;
|
||||
typedef float Float32;
|
||||
typedef unsigned char Boolean;
|
||||
#endif
|
||||
|
||||
#if defined(__cplusplus)
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
//==================================================================================================
|
||||
// Sound Engine
|
||||
//==================================================================================================
|
||||
/*!
|
||||
@enum SoundEngine error codes
|
||||
@abstract These are the error codes returned from the SoundEngine API.
|
||||
@constant kSoundEngineErrUnitialized
|
||||
The SoundEngine has not been initialized. Use SoundEngine_Initialize().
|
||||
@constant kSoundEngineErrInvalidID
|
||||
The specified EffectID was not found. This can occur if the effect has not been loaded, or
|
||||
if an unloaded is trying to be accessed.
|
||||
@constant kSoundEngineErrFileNotFound
|
||||
The specified file was not found.
|
||||
@constant kSoundEngineErrInvalidFileFormat
|
||||
The format of the file is invalid. Effect data must be little-endian 8 or 16 bit LPCM.
|
||||
@constant kSoundEngineErrDeviceNotFound
|
||||
The output device was not found.
|
||||
|
||||
*/
|
||||
enum {
|
||||
kSoundEngineErrUnitialized = 1,
|
||||
kSoundEngineErrInvalidID = 2,
|
||||
kSoundEngineErrFileNotFound = 3,
|
||||
kSoundEngineErrInvalidFileFormat = 4,
|
||||
kSoundEngineErrDeviceNotFound = 5
|
||||
};
|
||||
|
||||
|
||||
/*!
|
||||
@function SoundEngine_Initialize
|
||||
@abstract Initializes and sets up the sound engine. Calling after a previous initialize will
|
||||
reset the state of the SoundEngine, with all previous effects and music tracks
|
||||
erased. Note: This is not required, loading an effect or background track will
|
||||
initialize as necessary.
|
||||
@param inMixerOutputRate
|
||||
A Float32 that represents the output sample rate of the mixer unit. Setting this to
|
||||
0 will use the default rate (the sample rate of the device)
|
||||
@result A OSStatus indicating success or failure.
|
||||
*/
|
||||
OSStatus SoundEngine_Initialize(Float32 inMixerOutputRate);
|
||||
|
||||
|
||||
OSStatus SoundEngine_Reactivate();
|
||||
|
||||
|
||||
/*!
|
||||
@function SoundEngine_Teardown
|
||||
@abstract Tearsdown the sound engine.
|
||||
@result A OSStatus indicating success or failure.
|
||||
*/
|
||||
OSStatus SoundEngine_Teardown();
|
||||
|
||||
/*!
|
||||
@function SoundEngine_SetMasterVolume
|
||||
@abstract Sets the overall volume of all sounds coming from the process
|
||||
@param inValue
|
||||
A Float32 that represents the level. The range is between 0.0 and 1.0 (inclusive).
|
||||
@result A OSStatus indicating success or failure.
|
||||
*/
|
||||
OSStatus SoundEngine_SetMasterVolume(Float32 inValue);
|
||||
|
||||
/*!
|
||||
@function SoundEngine_SetListenerPosition
|
||||
@abstract Sets the position of the listener in the 3D space
|
||||
@param inX
|
||||
A Float32 that represents the listener's position along the X axis.
|
||||
@param inY
|
||||
A Float32 that represents the listener's position along the Y axis.
|
||||
@param inZ
|
||||
A Float32 that represents the listener's position along the Z axis.
|
||||
@result A OSStatus indicating success or failure.
|
||||
*/
|
||||
OSStatus SoundEngine_SetListenerPosition(Float32 inX, Float32 inY, Float32 inZ);
|
||||
OSStatus SoundEngine_SetListenerDirection(Float32 inX, Float32 inY, Float32 inZ);
|
||||
|
||||
/*!
|
||||
@function SoundEngine_SetListenerGain
|
||||
@abstract Sets the gain of the listener. Must be >= 0.0
|
||||
@param inValue
|
||||
A Float32 that represents the listener's gain
|
||||
@result A OSStatus indicating success or failure.
|
||||
*/
|
||||
OSStatus SoundEngine_SetListenerGain(Float32 inValue);
|
||||
|
||||
/*!
|
||||
@function SoundEngine_LoadBackgroundMusicTrack
|
||||
@abstract Tells the background music player which file to play
|
||||
@param inPath
|
||||
The absolute path to the file to play.
|
||||
@param inAddToQueue
|
||||
If true, file will be added to the current background music queue. If
|
||||
false, queue will be cleared and only loop the specified file.
|
||||
@param inLoadAtOnce
|
||||
If true, file will be loaded completely into memory. If false, data will be
|
||||
streamed from the file as needed. For games without large memory pressure and/or
|
||||
small background music files, this can save memory access and improve power efficiency
|
||||
@result A OSStatus indicating success or failure.
|
||||
*/
|
||||
OSStatus SoundEngine_LoadBackgroundMusicTrack(const char* inPath, Boolean inAddToQueue, Boolean inLoadAtOnce);
|
||||
|
||||
/*!
|
||||
@function SoundEngine_UnloadBackgroundMusicTrack
|
||||
@abstract Tells the background music player to release all resources and stop playing.
|
||||
@result A OSStatus indicating success or failure.
|
||||
*/
|
||||
OSStatus SoundEngine_UnloadBackgroundMusicTrack();
|
||||
|
||||
/*!
|
||||
@function SoundEngine_StartBackgroundMusic
|
||||
@abstract Tells the background music player to start playing.
|
||||
@result A OSStatus indicating success or failure.
|
||||
*/
|
||||
OSStatus SoundEngine_StartBackgroundMusic();
|
||||
|
||||
/*!
|
||||
@function SoundEngine_StopBackgroundMusic
|
||||
@abstract Tells the background music player to stop playing.
|
||||
@param inAddToQueue
|
||||
If true, playback will stop when all tracks have completed. If false, playback
|
||||
will stop immediately.
|
||||
@result A OSStatus indicating success or failure.
|
||||
*/
|
||||
OSStatus SoundEngine_StopBackgroundMusic(Boolean inStopAtEnd);
|
||||
|
||||
/*!
|
||||
@function SoundEngine_SetBackgroundMusicVolume
|
||||
@abstract Sets the volume for the background music player
|
||||
@param inValue
|
||||
A Float32 that represents the level. The range is between 0.0 and 1.0 (inclusive).
|
||||
@result A OSStatus indicating success or failure.
|
||||
*/
|
||||
OSStatus SoundEngine_SetBackgroundMusicVolume(Float32 inValue);
|
||||
|
||||
/*!
|
||||
@function SoundEngine_GetBackgroundMusicVolume
|
||||
@abstract Gets the volume for the background music player
|
||||
@result A Float32 representing the background music player volume, or 0 if it's not enabled
|
||||
*/
|
||||
Float32 SoundEngine_GetBackgroundMusicVolume();
|
||||
|
||||
/*!
|
||||
@function SoundEngine_LoadLoopingEffect
|
||||
@abstract Loads a sound effect from a file and return an ID to refer to that effect. Note: The files
|
||||
MUST all be in the same data format and sample rate
|
||||
@param inLoopFilePath
|
||||
The absolute path to the file to load. This is the file that will loop continually.
|
||||
@param inAttackFilePath
|
||||
The absolute path to the file to load. This will play once at the start of the effect.
|
||||
Set to NULL if no attack is desired, looping file will play immediately.
|
||||
@param inDecayFilePath
|
||||
The absolute path to the file to load. This will play once after looping has been ended.
|
||||
Triggered using SoundEngine_StopEffect with the inDoDecay set to true. Set to NULL if no
|
||||
decay is desired, looping file will stop immediately.
|
||||
@param outEffectID
|
||||
A UInt32 ID that refers to the effect.
|
||||
@result A OSStatus indicating success or failure.
|
||||
*/
|
||||
OSStatus SoundEngine_LoadLoopingEffect(const char* inLoopFilePath, const char* inAttackFilePath, const char* inDecayFilePath, UInt32* outEffectID);
|
||||
|
||||
/*!
|
||||
@function SoundEngine_LoadEffect
|
||||
@abstract Loads a sound effect from a file and return an ID to refer to that effect.
|
||||
@param inPath
|
||||
The absolute path to the file to load.
|
||||
@param outEffectID
|
||||
A UInt32 ID that refers to the effect.
|
||||
@result A OSStatus indicating success or failure.
|
||||
*/
|
||||
OSStatus SoundEngine_LoadEffect(const char* inPath, UInt32* outEffectID);
|
||||
|
||||
/*!
|
||||
@function SoundEngine_UnloadEffect
|
||||
@abstract Releases all resources associated with the given effect ID
|
||||
@param inEffectID
|
||||
The ID of the effect to unload.
|
||||
@result A OSStatus indicating success or failure.
|
||||
*/
|
||||
OSStatus SoundEngine_UnloadEffect(UInt32 inEffectID);
|
||||
|
||||
/*!
|
||||
@function SoundEngine_StartEffect
|
||||
@abstract Starts playback of an effect
|
||||
@param inEffectID
|
||||
The ID of the effect to start.
|
||||
@result A OSStatus indicating success or failure.
|
||||
*/
|
||||
OSStatus SoundEngine_StartEffect(UInt32 inEffectID);
|
||||
|
||||
/*!
|
||||
@function SoundEngine_StopEffect
|
||||
@abstract Stops playback of an effect
|
||||
@param inEffectID
|
||||
The ID of the effect to stop.
|
||||
@param inDoDecay
|
||||
Whether to play the decay file or stop immmediately (this is ignored
|
||||
for non-looping effects)
|
||||
@result A OSStatus indicating success or failure.
|
||||
*/
|
||||
OSStatus SoundEngine_StopEffect(UInt32 inEffectID, Boolean inDoDecay);
|
||||
|
||||
/*!
|
||||
@function SoundEngine_Vibrate
|
||||
@abstract Tells the device to vibrate
|
||||
*/
|
||||
#if TARGET_OS_IPHONE
|
||||
#define SoundEngine_Vibrate() AudioServicesPlaySystemSound(kSystemSoundID_Vibrate)
|
||||
#endif
|
||||
|
||||
/*!
|
||||
@function SoundEngine_SetEffectPitch
|
||||
@abstract Applies pitch shifting to an effect
|
||||
@param inEffectID
|
||||
The ID of the effect to adjust
|
||||
@param inValue
|
||||
A Float32 that represents the pitch scalar, with 1.0 being unchanged. Must
|
||||
be greater than 0.
|
||||
@result A OSStatus indicating success or failure.
|
||||
*/
|
||||
OSStatus SoundEngine_SetEffectPitch(UInt32 inEffectID, Float32 inValue);
|
||||
|
||||
/*!
|
||||
@function SoundEngine_SetEffectVolume
|
||||
@abstract Sets the volume for an effect
|
||||
@param inEffectID
|
||||
The ID of the effect to adjust
|
||||
@param inValue
|
||||
A Float32 that represents the level. The range is between 0.0 and 1.0 (inclusive).
|
||||
@result A OSStatus indicating success or failure.
|
||||
*/
|
||||
OSStatus SoundEngine_SetEffectLevel(UInt32 inEffectID, Float32 inValue);
|
||||
|
||||
/*!
|
||||
@function SoundEngine_SetEffectPosition
|
||||
@abstract Tells the engine whether a given effect should loop when played or if it should
|
||||
play through just once and stop.
|
||||
@param inEffectID
|
||||
The ID of the effect to adjust
|
||||
@param inX
|
||||
A Float32 that represents the effect's position along the X axis. Maximum distance
|
||||
is 100000.0 (absolute, not per axis), reference distance (distance from which gain
|
||||
begins to attenuate) is 1.0
|
||||
@param inY
|
||||
A Float32 that represents the effect's position along the Y axis. Maximum distance
|
||||
is 100000.0 (absolute, not per axis), reference distance (distance from which gain
|
||||
begins to attenuate) is 1.0
|
||||
@param inZ
|
||||
A Float32 that represents the effect's position along the Z axis. Maximum distance
|
||||
is 100000.0 by default (absolute, not per axis), reference distance (distance from
|
||||
which gain begins to attenuate) is 1.0
|
||||
@result A OSStatus indicating success or failure.
|
||||
*/
|
||||
OSStatus SoundEngine_SetEffectPosition(UInt32 inEffectID, Float32 inX, Float32 inY, Float32 inZ);
|
||||
|
||||
/*!
|
||||
@function SoundEngine_SetEffectsVolume
|
||||
@abstract Sets the overall volume for the effects
|
||||
@param inValue
|
||||
A Float32 that represents the level. The range is between 0.0 and 1.0 (inclusive).
|
||||
@result A OSStatus indicating success or failure.
|
||||
*/
|
||||
OSStatus SoundEngine_SetEffectsVolume(Float32 inValue);
|
||||
|
||||
/*!
|
||||
@function SoundEngine_GetEffectsVolume
|
||||
@abstract Gets the overall volume for the effects
|
||||
@result A Float32 representing the effects volume, or 0 if it's disabled
|
||||
*/
|
||||
Float32 SoundEngine_GetEffectsVolume();
|
||||
|
||||
/*!
|
||||
@function SoundEngine_SetMaxDistance
|
||||
@abstract Sets the maximum distance for effect attenuation. Gain will attenuate between the
|
||||
reference distance and the maximum distance, after which gain will be 0.0
|
||||
@param inValue
|
||||
A Float32 that represents the level. Must be greater than 0.0.
|
||||
@result A OSStatus indicating success or failure.
|
||||
*/
|
||||
OSStatus SoundEngine_SetMaxDistance(Float32 inValue);
|
||||
|
||||
/*!
|
||||
@function SoundEngine_SetReferenceDistance
|
||||
@abstract Sets the distance at which effect gain attenuation begins. It will attenuate between
|
||||
the reference distance and the maximum distance. Sounds closer than the reference
|
||||
distance will have no attenuation applied
|
||||
@param inValue
|
||||
A Float32 that represents the level. Must be greater than 0.0.
|
||||
@result A OSStatus indicating success or failure.
|
||||
*/
|
||||
OSStatus SoundEngine_SetReferenceDistance(Float32 inValue);
|
||||
|
||||
/*!
|
||||
@function SoundEngine_SetEffectReferenceDistance
|
||||
@abstract Sets the distance at which effect gain attenuation begins. It will attenuate between
|
||||
the reference distance and the maximum distance. Sounds closer than the reference
|
||||
distance will have no attenuation applied
|
||||
@param inEffectId
|
||||
The sound engine effect Id
|
||||
@param inValue
|
||||
A Float32 that represents the level. Must be greater than 0.0.
|
||||
@result A OSStatus indicating success or failure.
|
||||
*/
|
||||
OSStatus SoundEngine_SetEffectReferenceDistance(UInt32 inEffectID, Float32 inValue);
|
||||
|
||||
#if defined(__cplusplus)
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
|
||||
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.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
// generated by fontimg
|
||||
//
|
||||
|
||||
// struct GlyphRect { unsigned short x0, y0, x1, y1; float xoff, yoff, xadvance; };
|
||||
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 }
|
||||
};
|
||||
@@ -0,0 +1,166 @@
|
||||
/*
|
||||
* cmd.c
|
||||
* doom
|
||||
*
|
||||
* Created by John Carmack on 4/14/09.
|
||||
* Copyright 2009 id Software. All rights reserved.
|
||||
*
|
||||
*/
|
||||
/*
|
||||
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.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#include "../doomiphone.h"
|
||||
|
||||
typedef struct cmd_function_s {
|
||||
struct cmd_function_s *next;
|
||||
const char *name;
|
||||
int hashid;
|
||||
xcommand_t function;
|
||||
} cmd_function_t;
|
||||
|
||||
|
||||
#define MAX_STRING_TOKENS 16
|
||||
#define MAX_STRING_CHARS 1024
|
||||
|
||||
int cmd_argc;
|
||||
char *cmd_argv[ MAX_STRING_TOKENS ];
|
||||
|
||||
cmd_function_t *cmd_functions; // possible commands to execute
|
||||
|
||||
int Cmd_Argc( void ) {
|
||||
return cmd_argc;
|
||||
}
|
||||
|
||||
const char *Cmd_Argv( int arg ) {
|
||||
if( arg >= cmd_argc ) {
|
||||
return "";
|
||||
}
|
||||
|
||||
return cmd_argv[ arg ];
|
||||
}
|
||||
|
||||
|
||||
void Cmd_TokenizeString( const char *text ) {
|
||||
static char *stringCopy;
|
||||
|
||||
// clear the args from the last string
|
||||
// This better not be called recursively...
|
||||
if ( stringCopy ) {
|
||||
free( stringCopy );
|
||||
stringCopy = NULL;
|
||||
}
|
||||
|
||||
cmd_argc = 0;
|
||||
|
||||
if( ! text ) {
|
||||
return;
|
||||
}
|
||||
|
||||
stringCopy = strdup( text );
|
||||
char *strval = stringCopy;
|
||||
|
||||
while( 1 ) {
|
||||
char *start = strsep( &strval," \t\r\n");
|
||||
if ( !start ) {
|
||||
break;
|
||||
}
|
||||
if ( start[0] != 0 ) {
|
||||
cmd_argv[cmd_argc] = start;
|
||||
if ( ++cmd_argc == MAX_STRING_TOKENS ) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Cmd_ListCommands_f() {
|
||||
for( cmd_function_t *cmd = cmd_functions ; cmd ; cmd = cmd->next ) {
|
||||
Com_Printf( "%s\n", cmd->name );
|
||||
}
|
||||
}
|
||||
|
||||
void Cmd_AddCommand( const char *cmd_name, xcommand_t function ) {
|
||||
cmd_function_t *cmd;
|
||||
int hashid;
|
||||
|
||||
hashid = HashString( cmd_name );
|
||||
|
||||
// fail if the command already exists
|
||||
for( cmd = cmd_functions ; cmd ; cmd = cmd->next ) {
|
||||
if( hashid == cmd->hashid && !strcmp( cmd_name, cmd->name ) ) {
|
||||
Com_Printf( "Cmd_AddCommand: \"%s\" already defined\n", cmd_name );
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
cmd = malloc( sizeof( cmd_function_t ) );
|
||||
cmd->name = cmd_name;
|
||||
cmd->hashid = hashid;
|
||||
cmd->function = function;
|
||||
cmd->next = cmd_functions;
|
||||
cmd_functions = cmd;
|
||||
|
||||
}
|
||||
|
||||
void Cmd_ExecuteString( const char *str ) {
|
||||
int l = strlen( str );
|
||||
if ( str[l-1] == '\n' ) {
|
||||
char *stripped = alloca( l+1 );
|
||||
strcpy( stripped, str );
|
||||
str = stripped;
|
||||
stripped[l-1] = 0;
|
||||
}
|
||||
|
||||
Com_Printf( "%s\n", str );
|
||||
Cmd_TokenizeString( str );
|
||||
|
||||
const char *arg0 = Cmd_Argv( 0 );
|
||||
int hashid = HashString( arg0 );
|
||||
|
||||
// check commands first
|
||||
for( cmd_function_t *cmd = cmd_functions ; cmd ; cmd = cmd->next ) {
|
||||
if( hashid == cmd->hashid && !strcmp( arg0, cmd->name ) ) {
|
||||
cmd->function();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// then check cvars
|
||||
cvar_t *cvar = Cvar_FindVar( arg0 );
|
||||
if ( cvar ) {
|
||||
Cvar_Set( arg0, Cmd_Argv( 1 ) );
|
||||
return;
|
||||
}
|
||||
Com_Printf( "Unknown command: %s\n", arg0 );
|
||||
}
|
||||
|
||||
// execute each line of the config file
|
||||
void Cmd_ExecuteFile( const char *fullPathName ) {
|
||||
Com_Printf( "Executing command file '%s'\n", fullPathName );
|
||||
FILE *f = fopen( fullPathName, "rb" );
|
||||
if ( !f ) {
|
||||
Com_Printf( "Failed to open.\n" );
|
||||
return;
|
||||
}
|
||||
char line[1024];
|
||||
while( fgets( line, sizeof( line ), f ) ) {
|
||||
Cmd_ExecuteString( line );
|
||||
}
|
||||
fclose( f );
|
||||
}
|
||||
|
||||
@@ -0,0 +1,374 @@
|
||||
/*
|
||||
|
||||
Copyright (C) 2004 Michael Liebscher
|
||||
Copyright (C) 1997-2001 Id Software, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General 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 License for more details.
|
||||
|
||||
You should have received a copy of the GNU General License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
|
||||
*/
|
||||
|
||||
#include "../doomiphone.h"
|
||||
|
||||
|
||||
cvar_t *cvar_vars;
|
||||
|
||||
|
||||
/*
|
||||
-----------------------------------------------------------------------------
|
||||
Function: Cvar_FindVar -Return cvar;
|
||||
|
||||
Parameters: var_name -[in] Name of cvar to lookup.
|
||||
|
||||
Returns: NULL if cvar not found, otherwise returns the cvar.
|
||||
|
||||
Notes:
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
*/
|
||||
cvar_t *Cvar_FindVar( const char *var_name )
|
||||
{
|
||||
cvar_t *var;
|
||||
int hashid;
|
||||
|
||||
hashid = HashString( var_name );
|
||||
|
||||
for( var = cvar_vars ; var ; var = var->next )
|
||||
{
|
||||
if( hashid == var->hashid && !strcasecmp( var_name, var->name ) )
|
||||
{
|
||||
return var;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
-----------------------------------------------------------------------------
|
||||
Function: Cvar_VariableValue -Get value of cvar.
|
||||
|
||||
Parameters: var_name -[in] Name of cvar to get value.
|
||||
|
||||
Returns: 0 if not found, other the value of the cvar.
|
||||
|
||||
Notes:
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
*/
|
||||
float Cvar_VariableValue( const char *var_name )
|
||||
{
|
||||
cvar_t *var;
|
||||
|
||||
var = Cvar_FindVar( var_name );
|
||||
if( ! var )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return (float)atof( var->string );
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
-----------------------------------------------------------------------------
|
||||
Function: Cvar_VariableString -Get cvar variable as string.
|
||||
|
||||
Parameters: var_name -[in] Name of cvar to get value.
|
||||
|
||||
Returns: Blank string on error, otherwise value string.
|
||||
|
||||
Notes:
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
*/
|
||||
char *Cvar_VariableString( const char *var_name )
|
||||
{
|
||||
cvar_t *var;
|
||||
|
||||
var = Cvar_FindVar( var_name );
|
||||
if( ! var )
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
return var->string;
|
||||
}
|
||||
|
||||
/*
|
||||
-----------------------------------------------------------------------------
|
||||
Function: Cvar_CompleteVariable -Complete cvar string name.
|
||||
|
||||
Parameters: partial -[in] Partial name of string to look up.
|
||||
|
||||
Returns: NULL if partial string not found, otherwise the complete
|
||||
string name.
|
||||
|
||||
Notes:
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
*/
|
||||
char *Cvar_CompleteVariable( const char *partial )
|
||||
{
|
||||
cvar_t *cvar;
|
||||
size_t len;
|
||||
|
||||
len = strlen( partial );
|
||||
|
||||
if( ! len )
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//
|
||||
// Check partial match.
|
||||
//
|
||||
for( cvar = cvar_vars ; cvar ; cvar = cvar->next )
|
||||
{
|
||||
if( ! strncmp( partial, cvar->name, len ) )
|
||||
{
|
||||
return cvar->name;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
-----------------------------------------------------------------------------
|
||||
Function: Cvar_Get -Get cvar structure.
|
||||
|
||||
Parameters:
|
||||
var_name -[in] the name of the cvar variable.
|
||||
var_value -[in] string value of the cvar variable.
|
||||
flags -[in] see CVARFlags for more information.
|
||||
|
||||
Returns: NULL on error, otherwise valid pointer to cvar_t structure.
|
||||
|
||||
Notes:
|
||||
If the variable already exists, the value will not be set and
|
||||
the flags will be or'ed.
|
||||
-----------------------------------------------------------------------------
|
||||
*/
|
||||
cvar_t *Cvar_Get( const char *var_name, const char *var_value, CVARFlags flags ) {
|
||||
cvar_t *var;
|
||||
|
||||
var = Cvar_FindVar( var_name );
|
||||
if( var ) {
|
||||
var->flags |= flags;
|
||||
return var;
|
||||
}
|
||||
|
||||
if( ! var_value ) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
var = malloc( sizeof( *var ) );
|
||||
var->name = strdup( var_name );
|
||||
var->string = strdup( var_value );
|
||||
var->defaultString = strdup( var_value );
|
||||
var->hashid = HashString( var_name );
|
||||
var->modified = true;
|
||||
var->value = (float)atof( var->string );
|
||||
|
||||
// link the variable in
|
||||
var->next = cvar_vars;
|
||||
cvar_vars = var;
|
||||
|
||||
var->flags = flags;
|
||||
|
||||
return var;
|
||||
}
|
||||
|
||||
/*
|
||||
-----------------------------------------------------------------------------
|
||||
Function:
|
||||
|
||||
Parameters:
|
||||
|
||||
Returns:
|
||||
|
||||
Notes:
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
*/
|
||||
void Cvar_Set( const char *var_name, const char *value ) {
|
||||
cvar_t *var;
|
||||
|
||||
var = Cvar_FindVar( var_name );
|
||||
if( ! var ) {
|
||||
Com_Printf( "Cvar '%s' doesn't exist\n", var_name );
|
||||
return;
|
||||
}
|
||||
|
||||
if( var->flags & CVAR_NOSET ) {
|
||||
Com_Printf( "%s is write protected.\n", var_name );
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if( ! strcmp( value, var->string ) ) {
|
||||
return; // not changed
|
||||
}
|
||||
|
||||
var->modified = true;
|
||||
|
||||
free( var->string ); // free the old value string
|
||||
|
||||
var->string = strdup( value );
|
||||
var->value = (float)atof( var->string );
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
-----------------------------------------------------------------------------
|
||||
Function:
|
||||
|
||||
Parameters:
|
||||
|
||||
Returns:
|
||||
|
||||
Notes:
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
*/
|
||||
void Cvar_SetValue( const char *var_name, float value )
|
||||
{
|
||||
char val[ 32 ];
|
||||
|
||||
if( value == (int)value )
|
||||
{
|
||||
snprintf( val, sizeof( val ), "%i", (int)value );
|
||||
}
|
||||
else
|
||||
{
|
||||
snprintf( val, sizeof( val ), "%f", value );
|
||||
}
|
||||
|
||||
Cvar_Set( var_name, val );
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
-----------------------------------------------------------------------------
|
||||
Function: Cvar_Command -Handles variable inspection and changing from
|
||||
the console.
|
||||
|
||||
Parameters: Nothing.
|
||||
|
||||
Returns: false if variable not found, otherwise true.
|
||||
|
||||
Notes:
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
*/
|
||||
boolean Cvar_Command( void )
|
||||
{
|
||||
cvar_t *v;
|
||||
|
||||
// check variables
|
||||
v = Cvar_FindVar( Cmd_Argv( 0 ) );
|
||||
if( ! v )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// perform a variable print or set
|
||||
if( Cmd_Argc() == 1 )
|
||||
{
|
||||
Com_Printf( "\"%s\" is \"%s\"\n", v->name, v->string );
|
||||
return true;
|
||||
}
|
||||
|
||||
Cvar_Set( v->name, Cmd_Argv( 1 ) );
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
-----------------------------------------------------------------------------
|
||||
Function: Cvar_WriteVariables -Appends lines containing "set variable value"
|
||||
for all variables with the archive flag set
|
||||
to true.
|
||||
|
||||
Parameters:
|
||||
|
||||
Returns: Nothing.
|
||||
|
||||
Notes:
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
*/
|
||||
void Cvar_WriteVariables( const char *path )
|
||||
{
|
||||
cvar_t *var;
|
||||
char buffer[1024];
|
||||
FILE *f;
|
||||
|
||||
f = fopen( path, "a" );
|
||||
for( var = cvar_vars ; var ; var = var->next )
|
||||
{
|
||||
if( var->flags & CVAR_ARCHIVE )
|
||||
{
|
||||
snprintf( buffer, sizeof( buffer ), "set %s %s\n", var->name, var->string );
|
||||
fprintf( f, "%s", buffer );
|
||||
}
|
||||
}
|
||||
fclose( f );
|
||||
}
|
||||
|
||||
/*
|
||||
-----------------------------------------------------------------------------
|
||||
Function: Cvar_List_f -Print all cvars to the console.
|
||||
|
||||
Parameters: Nothing.
|
||||
|
||||
Returns: Nothing.
|
||||
|
||||
Notes:
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
*/
|
||||
void Cvar_List_f( void )
|
||||
{
|
||||
cvar_t *var;
|
||||
int i;
|
||||
|
||||
i = 0;
|
||||
for( var = cvar_vars ; var ; var = var->next, ++i )
|
||||
{
|
||||
if( var->flags & CVAR_ARCHIVE )
|
||||
{
|
||||
Com_Printf ("*");
|
||||
}
|
||||
else
|
||||
{
|
||||
Com_Printf (" ");
|
||||
}
|
||||
|
||||
|
||||
Com_Printf (" %s \"%s\"\n", var->name, var->string);
|
||||
}
|
||||
|
||||
Com_Printf ("%i cvars\n", i);
|
||||
}
|
||||
|
||||
|
||||
void Cvar_Reset_f( void ) {
|
||||
for( cvar_t *var = cvar_vars ; var ; var = var->next ) {
|
||||
Cvar_Set( var->name, var->defaultString );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,130 @@
|
||||
/*
|
||||
|
||||
Copyright (C) 2004 Michael Liebscher
|
||||
Copyright (C) 1997-2001 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.
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
* cvar.h: Dynamic variable tracking.
|
||||
*
|
||||
* Author: Michael Liebscher <johnnycanuck@users.sourceforge.net>
|
||||
* Date: 2004
|
||||
*
|
||||
* Acknowledgement:
|
||||
* This code was derived from Quake II, and was originally
|
||||
* written by Id Software, Inc.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
Notes:
|
||||
|
||||
Dynamic variable tracking.
|
||||
|
||||
cvar_t variables are used to hold scalar or string variables
|
||||
that can be changed or displayed at the console or prog code
|
||||
as well as accessed directly in C code.
|
||||
|
||||
The user can access cvars from the console in three ways:
|
||||
r_draworder -prints the current value
|
||||
r_draworder 0 -sets the current value to 0
|
||||
set r_draworder 0 -as above, but creates the cvar if not present
|
||||
|
||||
Cvars are restricted from having the same names as commands to keep this
|
||||
module from being ambiguous.
|
||||
|
||||
This module is implemented by cvar.c
|
||||
|
||||
*/
|
||||
|
||||
#ifndef __CVAR_H__
|
||||
#define __CVAR_H__
|
||||
|
||||
typedef enum _CVARFlags
|
||||
{
|
||||
CVAR_INIT = 0x0, // Just create it with no flag value.
|
||||
CVAR_ARCHIVE = 0x1, // Set to cause it to be saved to the config file
|
||||
CVAR_NOSET = 0x8, // Don't allow change from console at all,
|
||||
// but can be set from the command line.
|
||||
} CVARFlags;
|
||||
|
||||
|
||||
// nothing outside the Cvar_*() functions should modify these fields!
|
||||
typedef struct cvar_s {
|
||||
// By putting the value first, cvars can be referenced in other code
|
||||
// as just extern float *name, without having to include the cvar_t
|
||||
// declaration. This is probably a bad idea that I will
|
||||
// regret at some point.
|
||||
float value;
|
||||
|
||||
char *name;
|
||||
char *string;
|
||||
char *defaultString;
|
||||
int hashid;
|
||||
int flags;
|
||||
boolean modified; // set each time the cvar is changed
|
||||
struct cvar_s *next;
|
||||
} cvar_t;
|
||||
|
||||
extern cvar_t *cvar_vars;
|
||||
|
||||
void Cvar_List_f();
|
||||
void Cvar_Reset_f();
|
||||
|
||||
extern cvar_t *Cvar_Get( const char *var_name, const char *value, CVARFlags flags );
|
||||
// creates the variable if it doesn't exist, or returns the existing one
|
||||
// if it exists, the value will not be changed, but flags will be ORed in
|
||||
// that allows variables to be unarchived without needing bitflags
|
||||
|
||||
extern cvar_t *Cvar_FindVar( const char *var_name );
|
||||
// returns NULL if it doesn't exist
|
||||
|
||||
extern void Cvar_Set( const char *var_name, const char *value );
|
||||
// prints warning if it doesn't exist
|
||||
|
||||
extern void Cvar_SetValue( const char *var_name, float value );
|
||||
// expands value to a string and calls Cvar_Set
|
||||
|
||||
extern float Cvar_VariableValue( const char *var_name );
|
||||
// returns 0 if not defined or non numeric
|
||||
|
||||
extern char *Cvar_VariableString( const char *var_name );
|
||||
// returns an empty string if not defined
|
||||
|
||||
extern char *Cvar_CompleteVariable( const char *partial );
|
||||
// attempts to match a partial variable name for command line completion
|
||||
// returns NULL if nothing fits
|
||||
|
||||
extern void Cvar_GetLatchedVars( void );
|
||||
// any CVAR_LATCHED variables that have been set will now take effect
|
||||
|
||||
extern void Cvar_WriteVariables( const char *path );
|
||||
// appends lines containing "set variable value" for all variables
|
||||
// with the archive flag set to true.
|
||||
|
||||
extern void Cvar_Init( void );
|
||||
|
||||
extern char *Cvar_Userinfo( void );
|
||||
// returns an info string containing all the CVAR_USERINFO cvars
|
||||
|
||||
extern char *Cvar_Serverinfo( void );
|
||||
// returns an info string containing all the CVAR_SERVERINFO cvars
|
||||
|
||||
#endif /* __CVAR_H__ */
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 186 KiB |
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>get-task-allow</key>
|
||||
<false/>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
|
||||
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 <UIKit/UIKit.h>
|
||||
#import <UIKit/UIAccelerometer.h>
|
||||
|
||||
|
||||
@class EAGLView;
|
||||
|
||||
@interface gameAppDelegate : NSObject <UIApplicationDelegate, UIAccelerometerDelegate> {
|
||||
UIWindow *window;
|
||||
EAGLView *glView;
|
||||
int lastAccelUpdateMsec;
|
||||
}
|
||||
|
||||
@property (nonatomic, retain) IBOutlet UIWindow *window;
|
||||
@property (nonatomic, retain) IBOutlet EAGLView *glView;
|
||||
|
||||
- (void)restartAccelerometerIfNeeded;
|
||||
|
||||
@end
|
||||
|
||||
@@ -0,0 +1,234 @@
|
||||
/*
|
||||
|
||||
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 "doomAppDelegate.h"
|
||||
#import "EAGLView.h"
|
||||
#import <AudioToolbox/AudioServices.h>
|
||||
#include "../doomiphone.h"
|
||||
|
||||
@interface UIApplication (Private)
|
||||
|
||||
- (void)setSystemVolumeHUDEnabled:(BOOL)enabled forAudioCategory:(NSString *)category;
|
||||
- (void)setSystemVolumeHUDEnabled:(BOOL)enabled;
|
||||
|
||||
- (void)runFrame;
|
||||
- (void)asyncTic;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
char iphoneDocDirectory[1024];
|
||||
char iphoneAppDirectory[1024];
|
||||
|
||||
|
||||
@implementation gameAppDelegate
|
||||
|
||||
@synthesize window;
|
||||
@synthesize glView;
|
||||
|
||||
extern EAGLContext *context;
|
||||
|
||||
NSTimer *animationTimer;
|
||||
|
||||
touch_t sysTouches[MAX_TOUCHES];
|
||||
touch_t gameTouches[MAX_TOUCHES];
|
||||
pthread_mutex_t eventMutex; // used to sync between game and event threads
|
||||
|
||||
|
||||
pthread_t gameThreadHandle;
|
||||
volatile boolean startupCompleted;
|
||||
void *GameThread( void *args ) {
|
||||
if ( ![EAGLContext setCurrentContext:context]) {
|
||||
printf( "Couldn't setCurrentContext for game thread\n" );
|
||||
exit( 1 );
|
||||
}
|
||||
|
||||
printf( "original game thread priority: %f\n", (float)[NSThread threadPriority] );
|
||||
[NSThread setThreadPriority: 0.5];
|
||||
printf( "new game thread priority: %f\n", (float)[NSThread threadPriority] );
|
||||
|
||||
iphoneStartup();
|
||||
|
||||
// make sure one frame has been run before setting
|
||||
// startupCompleted, so we don't get one grey frame
|
||||
iphoneFrame();
|
||||
|
||||
startupCompleted = TRUE; // OK to start touch / accel callbacks
|
||||
while( 1 ) {
|
||||
iphoneFrame();
|
||||
}
|
||||
}
|
||||
|
||||
- (void)asyncTic {
|
||||
iphoneAsyncTic();
|
||||
[ self restartAccelerometerIfNeeded];
|
||||
}
|
||||
|
||||
- (void)runFrame {
|
||||
iphoneFrame();
|
||||
}
|
||||
|
||||
- (void)applicationDidFinishLaunching:(UIApplication *)application {
|
||||
application.statusBarHidden = YES;
|
||||
application.statusBarOrientation = UIInterfaceOrientationLandscapeLeft;
|
||||
|
||||
// get the documents directory, where we will write configs and save games
|
||||
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
|
||||
NSString *documentsDirectory = [paths objectAtIndex:0];
|
||||
[documentsDirectory getCString: iphoneDocDirectory
|
||||
maxLength: sizeof( iphoneDocDirectory ) - 1
|
||||
encoding: NSASCIIStringEncoding ];
|
||||
|
||||
// get the app directory, where our data files live
|
||||
// this gives something like:
|
||||
// /var/mobile/Applications/71355F9F-6400-4267-B07D-E7980764F5A8/Applications
|
||||
// when what we want is:
|
||||
// /var/mobile/Applications/71355F9F-6400-4267-B07D-E7980764F5A8/doom.app
|
||||
// so we get that in main() from argv[0]
|
||||
#if 0
|
||||
paths = NSSearchPathForDirectoriesInDomains(NSApplicationDirectory, NSUserDomainMask, YES);
|
||||
NSString *appDirectory = [paths objectAtIndex:0];
|
||||
|
||||
static char iphoneAppDirectoryFromAPI[1024];
|
||||
[appDirectory getCString: iphoneAppDirectoryFromAPI
|
||||
maxLength: sizeof( iphoneAppDirectoryFromAPI ) - 1
|
||||
encoding: NSASCIIStringEncoding ];
|
||||
#endif
|
||||
|
||||
// disable screen dimming
|
||||
[UIApplication sharedApplication].idleTimerDisabled = YES;
|
||||
|
||||
// start the flow of accelerometer events
|
||||
UIAccelerometer *accelerometer = [UIAccelerometer sharedAccelerometer];
|
||||
accelerometer.delegate = self;
|
||||
accelerometer.updateInterval = 0.01;
|
||||
|
||||
// use this mutex for coordinating touch handling between
|
||||
// the run loop thread and the game thread
|
||||
if ( pthread_mutex_init( &eventMutex, NULL ) == -1 ) {
|
||||
perror( "pthread_mutex_init" );
|
||||
}
|
||||
|
||||
// use this semaphore for signaling from the async cmd generation thread that
|
||||
// the game / draw thread can wake up
|
||||
|
||||
// sem_init is unimplemented on iPhone
|
||||
//if ( sem_init( &ticSemaphore, 0, 0 ) == -1 ) {
|
||||
// perror( "sem_init" );
|
||||
//}
|
||||
ticSemaphore = sem_open( "ticSemaphore", O_CREAT, S_IRWXU, 0 );
|
||||
if ( ticSemaphore == SEM_FAILED ) {
|
||||
perror( "sem_open" );
|
||||
}
|
||||
|
||||
// we want the main (event/async) thread to be as high a priority as possible
|
||||
// so the game/render thread will be interrupted immediately.
|
||||
// It looks like the default scheduling on iPhone is already what we want --
|
||||
// the main thread is at 1.0, and new threads are at 0.5.
|
||||
printf( "original event thread priority: %f\n", (float)[NSThread threadPriority] );
|
||||
[NSThread setThreadPriority: 1.0];
|
||||
printf( "new event thread priority: %f\n", (float)[NSThread threadPriority] );
|
||||
|
||||
|
||||
#ifdef USE_GAME_THREAD
|
||||
// the game thread will do the init and start running frames
|
||||
pthread_create( &gameThreadHandle, NULL, GameThread, NULL );
|
||||
while( !startupCompleted ) {
|
||||
// pause until all startup is completed and the game is
|
||||
// ready to receive accel and touch calls
|
||||
usleep( 1000 );
|
||||
}
|
||||
|
||||
// schedule the time for async command generation in network games
|
||||
float interval = 1.0 / 30.0f;
|
||||
[NSTimer scheduledTimerWithTimeInterval:interval
|
||||
target:self
|
||||
selector:@selector(asyncTic)
|
||||
userInfo:nil repeats:YES];
|
||||
#else
|
||||
// do all the game startup work
|
||||
iphoneStartup();
|
||||
|
||||
// schedule the time for frame updates
|
||||
float interval = 1.0 / 30.0f;
|
||||
// float interval = 1.0 / 5.0f; // a low framerate is useful for some timing tests
|
||||
animationTimer = [NSTimer scheduledTimerWithTimeInterval:interval
|
||||
target:self
|
||||
selector:@selector(runFrame)
|
||||
userInfo:nil repeats:YES];
|
||||
|
||||
// run one frame manually to avoid a grey view swap
|
||||
[self runFrame];
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
- (void)applicationWillResignActive:(UIApplication *)application {
|
||||
}
|
||||
|
||||
|
||||
- (void)applicationDidBecomeActive:(UIApplication *)application {
|
||||
}
|
||||
|
||||
- (void)applicationWillTerminate:(UIApplication *)application {
|
||||
iphoneShutdown();
|
||||
}
|
||||
|
||||
- (void)applicationDidReceiveMemoryWarning:(UIApplication *)application {
|
||||
Com_Printf( "applicationDidReceiveMemoryWarning\n" );
|
||||
}
|
||||
|
||||
|
||||
|
||||
- (void)dealloc {
|
||||
[window release];
|
||||
[glView release];
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
- (void)restartAccelerometerIfNeeded {
|
||||
|
||||
// I have no idea why this seems to happen sometimes...
|
||||
if ( SysIphoneMilliseconds() - lastAccelUpdateMsec > 1000 ) {
|
||||
static int count;
|
||||
if ( ++count < 100 ) {
|
||||
printf( "Restarting accelerometer updates.\n" );
|
||||
}
|
||||
UIAccelerometer *accelerometer = [UIAccelerometer sharedAccelerometer];
|
||||
accelerometer.delegate = self;
|
||||
accelerometer.updateInterval = 0.01;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)acceleration
|
||||
{
|
||||
float acc[4];
|
||||
acc[0] = acceleration.x;
|
||||
acc[1] = acceleration.y;
|
||||
acc[2] = acceleration.z;
|
||||
acc[3] = acceleration.timestamp;
|
||||
iphoneTiltEvent( acc );
|
||||
lastAccelUpdateMsec = SysIphoneMilliseconds();
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
|
||||
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.
|
||||
|
||||
*/
|
||||
|
||||
//
|
||||
// Prefix header for all source files of the 'doom' target in the 'doom' project
|
||||
//
|
||||
|
||||
#ifdef __OBJC__
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
#endif
|
||||
@@ -0,0 +1,200 @@
|
||||
/*
|
||||
|
||||
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.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#include "doomiphone.h"
|
||||
|
||||
//int registration_sequence;
|
||||
|
||||
//#include "iphone_gl.h"
|
||||
|
||||
|
||||
void GLCheckError(const char *message) {
|
||||
GLint err = glGetError();
|
||||
if ( err != GL_NO_ERROR ) {
|
||||
printf( "GL ERROR %d from %s\n", err, message );
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int QGLBeginStarted = 0;
|
||||
|
||||
struct Vertex {
|
||||
float xyz[3];
|
||||
float st[2];
|
||||
#ifdef VERTEX_COLOR
|
||||
GLubyte c[4];
|
||||
#endif
|
||||
};
|
||||
|
||||
#define MAX_VERTS 16384
|
||||
|
||||
typedef struct Vertex Vertex;
|
||||
Vertex immediate[ MAX_VERTS ];
|
||||
Vertex vab;
|
||||
short quad_indexes[MAX_VERTS * 3 / 2 ];
|
||||
int curr_vertex;
|
||||
GLenum curr_prim;
|
||||
|
||||
void SetImmediateModeGLVertexArrays() {
|
||||
glVertexPointer( 3, GL_FLOAT, sizeof( Vertex ), immediate[ 0 ].xyz );
|
||||
glEnableClientState( GL_VERTEX_ARRAY );
|
||||
glTexCoordPointer( 2, GL_FLOAT, sizeof( Vertex ), immediate[ 0 ].st );
|
||||
glEnableClientState( GL_TEXTURE_COORD_ARRAY );
|
||||
#ifdef VERTEX_COLOR
|
||||
glColorPointer( 4, GL_UNSIGNED_BYTE, sizeof( Vertex ), immediate[ 0 ].c );
|
||||
glEnableClientState( GL_COLOR_ARRAY );
|
||||
#endif
|
||||
}
|
||||
|
||||
void InitImmediateModeGL() {
|
||||
for ( int i = 0; i < MAX_VERTS * 3 / 2; i+=6 ) {
|
||||
int q = i / 6 * 4;
|
||||
quad_indexes[ i + 0 ] = q + 0;
|
||||
quad_indexes[ i + 1 ] = q + 1;
|
||||
quad_indexes[ i + 2 ] = q + 2;
|
||||
|
||||
quad_indexes[ i + 3 ] = q + 0;
|
||||
quad_indexes[ i + 4 ] = q + 2;
|
||||
quad_indexes[ i + 5 ] = q + 3;
|
||||
}
|
||||
|
||||
SetImmediateModeGLVertexArrays();
|
||||
}
|
||||
|
||||
void glBegin( GLenum prim ) {
|
||||
curr_vertex = 0;
|
||||
curr_prim = prim;
|
||||
}
|
||||
|
||||
void glVertex3f( GLfloat x, GLfloat y, GLfloat z ) {
|
||||
assert( curr_vertex < MAX_VERTS );
|
||||
vab.xyz[ 0 ] = x;
|
||||
vab.xyz[ 1 ] = y;
|
||||
vab.xyz[ 2 ] = z;
|
||||
immediate[ curr_vertex ] = vab;
|
||||
curr_vertex++;
|
||||
}
|
||||
void glVertex3fv( GLfloat *xyz ) {
|
||||
assert( curr_vertex < MAX_VERTS );
|
||||
vab.xyz[ 0 ] = xyz[0];
|
||||
vab.xyz[ 1 ] = xyz[1];
|
||||
vab.xyz[ 2 ] = xyz[2];
|
||||
immediate[ curr_vertex ] = vab;
|
||||
curr_vertex++;
|
||||
}
|
||||
void glVertex2f( GLfloat x, GLfloat y ) {
|
||||
assert( curr_vertex < MAX_VERTS );
|
||||
vab.xyz[ 0 ] = (float)x;
|
||||
vab.xyz[ 1 ] = (float)y;
|
||||
vab.xyz[ 2 ] = 0.0f;
|
||||
immediate[ curr_vertex ] = vab;
|
||||
curr_vertex++;
|
||||
}
|
||||
void glVertex2i( GLint x, GLint y ) {
|
||||
assert( curr_vertex < MAX_VERTS );
|
||||
vab.xyz[ 0 ] = (float)x;
|
||||
vab.xyz[ 1 ] = (float)y;
|
||||
vab.xyz[ 2 ] = 0.0f;
|
||||
immediate[ curr_vertex ] = vab;
|
||||
curr_vertex++;
|
||||
}
|
||||
|
||||
#ifdef VERTEX_COLOR
|
||||
void glColor4ub( GLubyte r, GLubyte g, GLubyte b, GLubyte a ) {
|
||||
vab.c[ 0 ] = r;
|
||||
vab.c[ 1 ] = g;
|
||||
vab.c[ 2 ] = b;
|
||||
vab.c[ 3 ] = a;
|
||||
}
|
||||
void glColor4ubv( GLubyte *rgba ) {
|
||||
vab.c[ 0 ] = rgba[0];
|
||||
vab.c[ 1 ] = rgba[1];
|
||||
vab.c[ 2 ] = rgba[2];
|
||||
vab.c[ 3 ] = rgba[3];
|
||||
}
|
||||
void glColor4f( GLfloat r, GLfloat g, GLfloat b, GLfloat a ) {
|
||||
vab.c[ 0 ] = (GLubyte) ( r * 255 );
|
||||
vab.c[ 1 ] = (GLubyte) ( g * 255 );
|
||||
vab.c[ 2 ] = (GLubyte) ( b * 255 );
|
||||
vab.c[ 3 ] = (GLubyte) ( a * 255 );
|
||||
}
|
||||
void glColor4fv( GLfloat *rgba ) {
|
||||
vab.c[ 0 ] = (GLubyte) ( rgba[0] * 255 );
|
||||
vab.c[ 1 ] = (GLubyte) ( rgba[1] * 255 );
|
||||
vab.c[ 2 ] = (GLubyte) ( rgba[2] * 255 );
|
||||
vab.c[ 3 ] = (GLubyte) ( rgba[3] * 255 );
|
||||
}
|
||||
void glColor3f( GLfloat r, GLfloat g, GLfloat b ) {
|
||||
vab.c[ 0 ] = (GLubyte) ( r * 255 );
|
||||
vab.c[ 1 ] = (GLubyte) ( g * 255 );
|
||||
vab.c[ 2 ] = (GLubyte) ( b * 255 );
|
||||
vab.c[ 3 ] = 255;
|
||||
}
|
||||
#endif
|
||||
|
||||
void glTexCoord2i( GLint s, GLint t ) {
|
||||
vab.st[ 0 ] = (float)s;
|
||||
vab.st[ 1 ] = (float)t;
|
||||
}
|
||||
void glTexCoord2f( GLfloat s, GLfloat t ) {
|
||||
vab.st[ 0 ] = s;
|
||||
vab.st[ 1 ] = t;
|
||||
}
|
||||
void glTexCoord2fv( GLfloat *st ) {
|
||||
vab.st[ 0 ] = st[0];
|
||||
vab.st[ 1 ] = st[1];
|
||||
}
|
||||
|
||||
void glEnd() {
|
||||
#if 0
|
||||
glVertexPointer( 3, GL_FLOAT, sizeof( Vertex ), immediate[ 0 ].xyz );
|
||||
glTexCoordPointer( 2, GL_FLOAT, sizeof( Vertex ), immediate[ 0 ].st );
|
||||
glColorPointer( 4, GL_UNSIGNED_BYTE, sizeof( Vertex ), immediate[ 0 ].c );
|
||||
glEnableClientState( GL_VERTEX_ARRAY );
|
||||
glEnableClientState( GL_TEXTURE_COORD_ARRAY );
|
||||
glEnableClientState( GL_COLOR_ARRAY );
|
||||
#endif
|
||||
if ( curr_prim == GL_QUADS ) {
|
||||
glDrawElements( GL_TRIANGLES, curr_vertex / 4 * 6, GL_UNSIGNED_SHORT, quad_indexes );
|
||||
} else {
|
||||
glDrawArrays( curr_prim, 0, curr_vertex );
|
||||
}
|
||||
curr_vertex = 0;
|
||||
curr_prim = 0;
|
||||
}
|
||||
|
||||
void landscapeViewport( GLint x, GLint y, GLsizei width, GLsizei height ) {
|
||||
y = 0; // !@#
|
||||
if ( revLand->value ) {
|
||||
glViewport( VID_HEIGHT - (y+height), x, height, width );
|
||||
} else {
|
||||
glViewport( y, x, height, width );
|
||||
}
|
||||
}
|
||||
|
||||
void landscapeScissor( GLint x, GLint y, GLsizei width, GLsizei height ) {
|
||||
y = 0; // !@#
|
||||
if ( revLand->value ) {
|
||||
glScissor( VID_HEIGHT - (y+height), x, height, width );
|
||||
} else {
|
||||
glScissor( y, x, height, width );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
|
||||
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 __GLES_GLUE_H__
|
||||
#define __GLES_GLUE_H__
|
||||
|
||||
#define GL_QUADS 888
|
||||
|
||||
void GLCheckError( const char *message );
|
||||
|
||||
void glBegin( GLenum prim );
|
||||
void glVertex3f( float x, float y, float z );
|
||||
void glVertex2i( GLint x, GLint y );
|
||||
void glColor4ub( GLubyte r, GLubyte g, GLubyte b, GLubyte a );
|
||||
void glColor4ubv( GLubyte *rgba );
|
||||
void glColor3f( GLfloat r, GLfloat g, GLfloat b );
|
||||
void glColor4f( GLfloat r, GLfloat g, GLfloat b, GLfloat a );
|
||||
void glColor4fv( GLfloat *rgba );
|
||||
void glTexCoord2i( GLint s, GLint t );
|
||||
void glTexCoord2f( GLfloat s, GLfloat t );
|
||||
void glEnd();
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,215 @@
|
||||
/*
|
||||
|
||||
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.
|
||||
|
||||
*/
|
||||
|
||||
#include "../doomiphone.h"
|
||||
|
||||
hud_t huds;
|
||||
|
||||
void HudDraw();
|
||||
|
||||
void HudWrite();
|
||||
|
||||
void HudRead();
|
||||
|
||||
ibutton_t *dragHud;
|
||||
int dragX, dragY;
|
||||
|
||||
void SetHudPic( ibutton_t *hp, const char *image ) {
|
||||
pkTexture_t *gl;
|
||||
gl = PK_FindTexture( image );
|
||||
assert( gl );
|
||||
hp->texture = gl;
|
||||
hp->touch = NULL; // in case one was down when it was saved
|
||||
}
|
||||
|
||||
void SetHudSpot( ibutton_t *hp, int x, int y, int dw, int dh ) {
|
||||
hp->touch = NULL; // in case one was down when it was saved
|
||||
hp->x = x - dw/2;
|
||||
hp->y = y - dh/2;
|
||||
hp->drawWidth = dw;
|
||||
hp->drawHeight = dh;
|
||||
hp->buttonFlags = 0;
|
||||
hp->scale = 1.0f;
|
||||
}
|
||||
|
||||
void HudSetTexnums() {
|
||||
SetHudPic( &huds.forwardStick, "iphone/up_down.tga" );
|
||||
SetHudPic( &huds.sideStick, "iphone/side_2_side.tga" );
|
||||
SetHudPic( &huds.turnStick, "iphone/directional_2.tga" );
|
||||
SetHudPic( &huds.turnRotor, "iphone/rotate.tga" );
|
||||
SetHudPic( &huds.fire, "iphone/fire.tga" );
|
||||
SetHudPic( &huds.menu, "iphone/menu_button.tga" );
|
||||
SetHudPic( &huds.map, "iphone/map_button.tga" );
|
||||
|
||||
SetHudSpot( &huds.weaponSelect, 240, 280, 40, 40 );
|
||||
}
|
||||
|
||||
void HudSetForScheme( int schemeNum ) {
|
||||
for ( ibutton_t *hud = (ibutton_t *)&huds ; hud != (ibutton_t *)(&huds+1) ; hud++ ) {
|
||||
hud->buttonFlags = BF_IGNORE;
|
||||
}
|
||||
static const int STICK_SIZE = 128;
|
||||
static const int HALF_STICK = 128/2;
|
||||
static const int BOTTOM = 320 - 44; // above the status bar
|
||||
SetHudSpot( &huds.weaponSelect, 240, 280, 40, 40 ); // the touch area is doubled
|
||||
|
||||
// make the forward / back sticks touch taller than they draw
|
||||
switch ( schemeNum ) {
|
||||
default:
|
||||
case 0: // turn stick
|
||||
SetHudSpot( &huds.forwardStick, HALF_STICK, BOTTOM-HALF_STICK, STICK_SIZE, STICK_SIZE );
|
||||
SetHudSpot( &huds.turnStick, HALF_STICK, BOTTOM-HALF_STICK, STICK_SIZE, STICK_SIZE );
|
||||
SetHudSpot( &huds.fire, 480-40, BOTTOM-HALF_STICK, 80, 80 );
|
||||
SetHudSpot( &huds.menu, 480-24, 24, 48, 48 );
|
||||
SetHudSpot( &huds.map, 24, 24, 48, 48 );
|
||||
break;
|
||||
case 1: // dual stick
|
||||
SetHudSpot( &huds.forwardStick, HALF_STICK, BOTTOM-HALF_STICK, STICK_SIZE, STICK_SIZE );
|
||||
SetHudSpot( &huds.sideStick, HALF_STICK, BOTTOM-HALF_STICK, STICK_SIZE, STICK_SIZE );
|
||||
SetHudSpot( &huds.turnStick, 480-HALF_STICK, BOTTOM-HALF_STICK, STICK_SIZE, STICK_SIZE );
|
||||
SetHudSpot( &huds.fire, 480-40, 40, 80, 80 );
|
||||
SetHudSpot( &huds.menu, 48+24, 24, 48, 48 );
|
||||
SetHudSpot( &huds.map, 24, 24, 48, 48 );
|
||||
break;
|
||||
case 2: // rotor
|
||||
SetHudSpot( &huds.forwardStick, HALF_STICK, BOTTOM-HALF_STICK, STICK_SIZE, STICK_SIZE );
|
||||
SetHudSpot( &huds.sideStick, HALF_STICK, BOTTOM-HALF_STICK, STICK_SIZE, STICK_SIZE );
|
||||
SetHudSpot( &huds.turnRotor, 480-HALF_STICK, BOTTOM-HALF_STICK, STICK_SIZE, STICK_SIZE );
|
||||
SetHudSpot( &huds.fire, 480-40, 40, 80, 80 );
|
||||
SetHudSpot( &huds.menu, 48+24, 24, 48, 48 );
|
||||
SetHudSpot( &huds.map, 24, 24, 48, 48 );
|
||||
break;
|
||||
}
|
||||
|
||||
// don't process these in the update hud touch loop, because they will be
|
||||
// handled with normal button calls
|
||||
huds.menu.buttonFlags |= BF_HUDBUTTON;
|
||||
huds.map.buttonFlags |= BF_HUDBUTTON;
|
||||
|
||||
// don't make the big button click sound for the fire button
|
||||
huds.fire.buttonFlags |= BF_SMALL_CLICK;
|
||||
|
||||
}
|
||||
|
||||
void SnapSticks( ibutton_t *test, const ibutton_t *to ) {
|
||||
if ( abs( test->x - to->x ) < test->drawWidth && abs( test->y - to->y ) < test->drawHeight ) {
|
||||
test->x = to->x;
|
||||
test->y = to->y;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
HudEditFrame
|
||||
|
||||
==================
|
||||
*/
|
||||
void HudEditFrame() {
|
||||
color3_t gray = { 32, 32, 32 };
|
||||
|
||||
if ( numTouches == 0 && numPrevTouches == 1 && 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];
|
||||
dragHud = NULL;
|
||||
for ( ibutton_t *hud = (ibutton_t *)&huds ; hud != (ibutton_t *)(&huds+1) ; hud++ ) {
|
||||
if ( hud->buttonFlags & BF_IGNORE ) {
|
||||
continue;
|
||||
}
|
||||
if ( x >= hud->x && x - hud->x < hud->drawWidth && y >= hud->y && y - hud->y < hud->drawHeight ) {
|
||||
dragHud = hud;
|
||||
dragX = dragHud->x - x;
|
||||
dragY = dragHud->y - y;
|
||||
Sound_StartLocalSound( "iphone/bdown_01.wav" );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( numTouches == 1 && numPrevTouches == 1 && dragHud ) {
|
||||
// adjust the position of the dragHud
|
||||
dragHud->x = touches[0][0] + dragX;
|
||||
dragHud->y = touches[0][1] + dragY;
|
||||
if ( dragHud->x < 0 ) {
|
||||
dragHud->x = 0;
|
||||
}
|
||||
if ( dragHud->x > 480 - dragHud->drawWidth ) {
|
||||
dragHud->x = 480 - dragHud->drawWidth;
|
||||
}
|
||||
if ( dragHud->y < 0 ) {
|
||||
dragHud->y = 0;
|
||||
}
|
||||
if ( dragHud->y > 320 - dragHud->drawHeight ) {
|
||||
dragHud->y = 320 - dragHud->drawHeight;
|
||||
}
|
||||
|
||||
// magnet pull a matchable axis
|
||||
if ( controlScheme->value == 0 ) {
|
||||
if ( dragHud == &huds.forwardStick ) {
|
||||
SnapSticks( &huds.turnStick, dragHud );
|
||||
}
|
||||
} else {
|
||||
if ( dragHud == &huds.forwardStick ) {
|
||||
SnapSticks( &huds.sideStick, dragHud );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// solid background color and some UI elements for context
|
||||
R_Draw_Fill( 0, 0, 480, 320, gray );
|
||||
glColor4f( 1, 1, 1, 1 );
|
||||
iphoneCenterText( 240, 20, 0.75, "Drag the controls" );
|
||||
|
||||
// draw the status bar
|
||||
extern patchnum_t stbarbg;
|
||||
if ( statusBar->value ) {
|
||||
// force doom to rebind, since we have changed the active GL_TEXTURE_2D
|
||||
last_gltexture = NULL;
|
||||
gld_DrawNumPatch(0, ST_Y, stbarbg.lumpnum, CR_DEFAULT, VPT_STRETCH);
|
||||
}
|
||||
|
||||
// draw the active items at their current locations
|
||||
for ( ibutton_t *hud = (ibutton_t *)&huds ; hud != (ibutton_t *)(&huds+1) ; hud++ ) {
|
||||
if ( !hud->texture ) {
|
||||
continue;
|
||||
}
|
||||
if ( hud->buttonFlags & BF_IGNORE ) {
|
||||
continue;
|
||||
}
|
||||
PK_StretchTexture( hud->texture, hud->x, hud->y, hud->drawWidth, hud->drawHeight );
|
||||
}
|
||||
|
||||
// draw the done button
|
||||
static ibutton_t btnDone;
|
||||
if ( !btnDone.texture ) {
|
||||
// initial setup
|
||||
SetButtonPicsAndSizes( &btnDone, "iphone/back_button.tga", "Done", 240 - 32, 160-32, 64, 64 );
|
||||
}
|
||||
if ( HandleButton( &btnDone ) ) {
|
||||
menuState = IPM_CONTROLS;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,418 @@
|
||||
/*
|
||||
* ipak.c
|
||||
* doom
|
||||
*
|
||||
* Created by John Carmack on 4/9/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.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#include "../doomiphone.h"
|
||||
|
||||
pkHeader_t *pkHeader;
|
||||
int pkSize;
|
||||
|
||||
// images and wavs have writable state, so they need separate
|
||||
// structs that also point to the source in the pak file
|
||||
pkTexture_t *pkTextures;
|
||||
pkWav_t *pkWavs;
|
||||
|
||||
void PK_LoadTexture( pkTexture_t *image );
|
||||
|
||||
/*
|
||||
==================
|
||||
PK_Init
|
||||
|
||||
==================
|
||||
*/
|
||||
void PK_Init( const char *pakFileName ) {
|
||||
printf( "PK_Init( %s )\n", pakFileName );
|
||||
|
||||
int fd = open( pakFileName, O_RDONLY );
|
||||
if ( fd == -1 ) {
|
||||
printf( "Couldn't open file\n" );
|
||||
assert( 0 );
|
||||
}
|
||||
|
||||
struct stat s;
|
||||
fstat( fd, &s );
|
||||
|
||||
pkSize = s.st_size;
|
||||
pkHeader = mmap( NULL, pkSize, PROT_READ, MAP_FILE|MAP_PRIVATE, fd, 0 );
|
||||
|
||||
// mmap keeps the file internally, we can close our descriptor
|
||||
close( fd );
|
||||
|
||||
if ( (int)pkHeader == -1 ) {
|
||||
printf( "mmap failed: %s\n", strerror( errno ) );
|
||||
assert( 0 );
|
||||
}
|
||||
|
||||
if ( pkHeader->version != PKFILE_VERSION ) {
|
||||
printf( "bad pak file version: 0x%x != 0x%x\n", pkHeader->version, PKFILE_VERSION );
|
||||
assert( 0 );
|
||||
}
|
||||
|
||||
// build the local image table
|
||||
pkTextures = malloc( sizeof( pkTextures[0] ) * pkHeader->textures.count );
|
||||
memset( pkTextures, 0, sizeof( pkTextures[0] ) * pkHeader->textures.count );
|
||||
for ( int i = 0 ; i < pkHeader->textures.count ; i++ ) {
|
||||
pkTextures[i].textureData = (pkTextureData_t *)( (byte *)pkHeader + pkHeader->textures.tableOfs + i * pkHeader->textures.structSize );
|
||||
}
|
||||
|
||||
// build the local wav table
|
||||
int startLoadingWavs = SysIphoneMicroseconds();
|
||||
pkWavs = malloc( sizeof( pkWavs[0] ) * pkHeader->wavs.count );
|
||||
memset( pkWavs, 0, sizeof( pkWavs[0] ) * pkHeader->wavs.count );
|
||||
for ( int i = 0 ; i < pkHeader->wavs.count ; i++ ) {
|
||||
pkWav_t *sfx = &pkWavs[i];
|
||||
sfx->wavData = (pkWavData_t *)( (byte *)pkHeader + pkHeader->wavs.tableOfs + i * pkHeader->wavs.structSize );
|
||||
// there is no harm in setting the OpenAl static buffer up for everything now
|
||||
alGenBuffers( 1, &sfx->alBufferNum );
|
||||
|
||||
int alFormat;
|
||||
if ( sfx->wavData->wavChannels == 1 ) {
|
||||
if ( sfx->wavData->wavChannelBytes == 1 ) {
|
||||
alFormat = AL_FORMAT_MONO8;
|
||||
} else {
|
||||
alFormat = AL_FORMAT_MONO16;
|
||||
}
|
||||
} else {
|
||||
if ( sfx->wavData->wavChannelBytes == 1 ) {
|
||||
alFormat = AL_FORMAT_STEREO8;
|
||||
} else {
|
||||
alFormat = AL_FORMAT_STEREO16;
|
||||
}
|
||||
}
|
||||
#if 0
|
||||
alBufferData( sfx->alBufferNum, alFormat, (byte *)pkHeader + sfx->wavData->wavDataOfs
|
||||
, sfx->wavData->wavChannels*sfx->wavData->wavChannelBytes*sfx->wavData->wavNumSamples
|
||||
, sfx->wavData->wavRate );
|
||||
#else
|
||||
// This should just store out a pointer, so the data won't get touched until it is actually
|
||||
// used to play a sound.
|
||||
extern void alBufferDataStatic(const ALint bid, ALenum format, ALvoid* data, ALsizei size, ALsizei freq);
|
||||
|
||||
alBufferDataStatic( sfx->alBufferNum, alFormat, (byte *)pkHeader + sfx->wavData->wavDataOfs
|
||||
, sfx->wavData->wavChannels*sfx->wavData->wavChannelBytes*sfx->wavData->wavNumSamples
|
||||
, sfx->wavData->wavRate );
|
||||
#endif
|
||||
}
|
||||
int endLoadingWavs = SysIphoneMicroseconds();
|
||||
printf( "%i usec to load wavs\n", endLoadingWavs - startLoadingWavs );
|
||||
|
||||
printf( "Mapped %i bytes of %s at 0x%p\n", pkSize, pakFileName, pkHeader );
|
||||
printf( "%4i textures\n", pkHeader->textures.count );
|
||||
printf( "%4i wavs\n", pkHeader->wavs.count );
|
||||
printf( "%4i raws\n", pkHeader->raws.count );
|
||||
#if 0
|
||||
// testing
|
||||
for ( int j = 0 ; j < 4 ; j++ ) {
|
||||
int startTime = Sys_Microseconds();
|
||||
int sum = 0;
|
||||
for ( int i = 0 ; i < pkSize ; i+=16 ) {
|
||||
sum += ((byte *)pkHeader)[i];
|
||||
}
|
||||
int endTime = Sys_Microseconds();
|
||||
printf( "%5.1f mb/s page-in speed (%i)\n", (float)pkSize / (endTime - startTime ), endTime - startTime );
|
||||
}
|
||||
|
||||
for ( int i = 0 ; i < pkHeader->numTextures ; i++ ) {
|
||||
printf( "-------------------------\n" );
|
||||
for ( int j = 0 ; j < 8 ; j++ ) {
|
||||
pkTexture_t *tex = &pkTextures[i];
|
||||
int start = Sys_Microseconds();
|
||||
PK_LoadTexture( tex );
|
||||
int middle = Sys_Microseconds();
|
||||
PK_StretchTexture( tex, 0, 0, 0, 0 );
|
||||
int middle2 = Sys_Microseconds();
|
||||
PK_StretchTexture( tex, 0, 0, 0, 0 );
|
||||
int end = Sys_Microseconds();
|
||||
printf( "%i usec load, %i usec first draw, %i usec second draw\n",
|
||||
middle - start, middle2 - middle, end - middle2 );
|
||||
|
||||
glDeleteTextures( 1, &tex->glTexNum );
|
||||
tex->glTexNum = 0;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
==================
|
||||
PK_LoadTexture
|
||||
|
||||
==================
|
||||
*/
|
||||
void PK_LoadTexture( pkTexture_t *tex ) {
|
||||
int startTime = SysIphoneMicroseconds();
|
||||
|
||||
const pkTextureData_t *imd = tex->textureData;
|
||||
|
||||
glGenTextures( 1, &tex->glTexNum );
|
||||
glBindTexture( GL_TEXTURE_2D, tex->glTexNum );
|
||||
|
||||
// load the image directly from the mapped file
|
||||
typedef struct {
|
||||
int internalFormat;
|
||||
int externalFormat;
|
||||
int type;
|
||||
int bpp;
|
||||
} formatInfo_t;
|
||||
|
||||
static formatInfo_t formatInfo[9] = {
|
||||
{ GL_RGB , GL_RGB, GL_UNSIGNED_SHORT_5_6_5, 16 },
|
||||
{ GL_RGBA, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1, 16 },
|
||||
{ GL_RGBA, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, 16 },
|
||||
{ GL_RGBA, GL_BGRA, GL_UNSIGNED_BYTE, 32 },
|
||||
{ GL_LUMINANCE_ALPHA, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, 16 },
|
||||
{ GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG, 0, 0, 4 },
|
||||
{ GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG, 0, 0, 4 },
|
||||
{ GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG, 0, 0, 2 },
|
||||
{ GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG, 0, 0, 2 },
|
||||
};
|
||||
|
||||
assert( imd->format < 9 );
|
||||
formatInfo_t *fi = &formatInfo[imd->format];
|
||||
|
||||
unsigned char *s = (byte *)pkHeader + imd->picDataOfs;
|
||||
int w = imd->uploadWidth;
|
||||
int h = imd->uploadHeight;
|
||||
// upload each mip level
|
||||
int l = 0;
|
||||
int totalSize = 0;
|
||||
while( 1 ) {
|
||||
int size = (w*h*fi->bpp)/8;
|
||||
if ( fi->type == 0 ) {
|
||||
if ( size < 32 ) {
|
||||
// minimum PVRTC size
|
||||
size = 32;
|
||||
}
|
||||
glCompressedTexImage2D( GL_TEXTURE_2D, l, fi->internalFormat, w, h, 0,
|
||||
size, s );
|
||||
} else {
|
||||
glTexImage2D( GL_TEXTURE_2D, l, fi->internalFormat, w, h, 0,
|
||||
fi->externalFormat, fi->type, s );
|
||||
}
|
||||
GLCheckError( "texture upload" );
|
||||
|
||||
totalSize += size;
|
||||
if ( ++l == imd->numLevels ) {
|
||||
break;
|
||||
}
|
||||
if ( w == 1 && h == 1 ) {
|
||||
break;
|
||||
}
|
||||
s += size;
|
||||
w >>= 1;
|
||||
if ( w == 0 ) {
|
||||
w = 1;
|
||||
}
|
||||
h >>= 1;
|
||||
if ( h == 0 ) {
|
||||
h = 1;
|
||||
}
|
||||
}
|
||||
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, imd->minFilter );
|
||||
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, imd->magFilter );
|
||||
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, imd->wrapS );
|
||||
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, imd->wrapT );
|
||||
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 2.0f );
|
||||
|
||||
int endTime = SysIphoneMicroseconds();
|
||||
|
||||
printf( "%5.1f mb/s TexImage for %s\n", (float)totalSize /
|
||||
( endTime - startTime ), imd->name.name );
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
PK_FindTexture
|
||||
|
||||
Fully creates the gl texture before returning.
|
||||
==================
|
||||
*/
|
||||
pkTexture_t *PK_FindTexture( const char *imageName ) {
|
||||
int index;
|
||||
pkTexture_t *texData = (pkTexture_t *)PK_FindType( imageName, &pkHeader->textures, &index );
|
||||
if ( !texData ) {
|
||||
return NULL;
|
||||
}
|
||||
pkTexture_t *tex = pkTextures + index;
|
||||
if ( tex->glTexNum == 0 ) {
|
||||
PK_LoadTexture( tex );
|
||||
}
|
||||
return tex;
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
PK_FindWav
|
||||
|
||||
==================
|
||||
*/
|
||||
pkWav_t *PK_FindWav( const char *soundName ) {
|
||||
int index;
|
||||
pkWavData_t *wavData = (pkWavData_t *)PK_FindType( soundName, &pkHeader->wavs, &index );
|
||||
if ( !wavData ) {
|
||||
return NULL;
|
||||
}
|
||||
pkWav_t *wav = pkWavs + index;
|
||||
|
||||
// create the OpenAL buffer
|
||||
|
||||
return wav;
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
PK_FindRaw
|
||||
|
||||
==================
|
||||
*/
|
||||
const byte *PK_FindRaw( const char *rawName, int *len ) {
|
||||
pkRawData_t *raw = (pkRawData_t *)PK_FindType( rawName, &pkHeader->raws, NULL );
|
||||
if ( !raw ) {
|
||||
if ( len ) {
|
||||
*len = -1;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if ( len ) {
|
||||
*len = raw->rawDataLen;
|
||||
}
|
||||
return (byte *)pkHeader + raw->rawDataOfs;
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
PK_HashName
|
||||
|
||||
==================
|
||||
*/
|
||||
int PK_HashName( const char *name, char canonical[MAX_PK_NAME] ) {
|
||||
int o = 0;
|
||||
int hash = 0;
|
||||
|
||||
do {
|
||||
int c = name[o];
|
||||
if ( c == 0 ) {
|
||||
break;
|
||||
}
|
||||
// backslashes to forward slashes
|
||||
if ( c == '\\' ) {
|
||||
c = '/';
|
||||
}
|
||||
// to lowercase
|
||||
c = tolower( c );
|
||||
canonical[o++] = c;
|
||||
hash = (hash << 5) - hash + c;
|
||||
} while ( o < MAX_PK_NAME-1 );
|
||||
canonical[o] = 0;
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
PK_FindType
|
||||
|
||||
==================
|
||||
*/
|
||||
const pkName_t *PK_FindType( const char *rawName, const pkType_t *type, int *indexOutput ) {
|
||||
char canonicalName[MAX_PK_NAME];
|
||||
|
||||
int hash = PK_HashName( rawName, canonicalName );
|
||||
|
||||
int hashChain = hash & (PK_HASH_CHAINS-1);
|
||||
|
||||
int index = type->hashChains[hashChain];
|
||||
while ( index != -1 ) {
|
||||
assert( index >= 0 && index < type->count );
|
||||
const pkName_t *name = (pkName_t *)((byte *)pkHeader + type->tableOfs + index * type->structSize );
|
||||
if ( name->nameHash == hash && !strcmp( canonicalName, name->name ) ) {
|
||||
// this is it
|
||||
if ( indexOutput ) {
|
||||
*indexOutput = index;
|
||||
}
|
||||
return name;
|
||||
}
|
||||
index = name->nextOnHashChain;
|
||||
}
|
||||
|
||||
// not found
|
||||
if ( indexOutput ) {
|
||||
*indexOutput = -1;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
==================
|
||||
PK_BindTexture
|
||||
|
||||
==================
|
||||
*/
|
||||
void PK_BindTexture( pkTexture_t *tex ) {
|
||||
assert( tex->glTexNum );
|
||||
glBindTexture( GL_TEXTURE_2D, tex->glTexNum );
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
PK_DrawTexture
|
||||
|
||||
==================
|
||||
*/
|
||||
void PK_DrawTexture( pkTexture_t *tex, int x, int y ) {
|
||||
PK_BindTexture( tex );
|
||||
|
||||
int w = tex->textureData->srcWidth;
|
||||
int h = tex->textureData->srcHeight;
|
||||
|
||||
glBegin( GL_QUADS );
|
||||
|
||||
glTexCoord2f( 0.0f, 0.0f ); glVertex2i( x, y );
|
||||
glTexCoord2f( tex->textureData->maxS, 0.0f ); glVertex2i( x+w, y );
|
||||
glTexCoord2f( tex->textureData->maxS, tex->textureData->maxT ); glVertex2i( x+w, y+h );
|
||||
glTexCoord2f( 0.0f, tex->textureData->maxT ); glVertex2i( x, y+h );
|
||||
|
||||
glEnd();
|
||||
}
|
||||
|
||||
void PK_StretchTexture( pkTexture_t *tex, float x, float y, float w, float h ) {
|
||||
PK_BindTexture( tex );
|
||||
|
||||
glBegin( GL_QUADS );
|
||||
|
||||
glTexCoord2f( 0.0f, 0.0f ); glVertex2i( x, y );
|
||||
glTexCoord2f( tex->textureData->maxS, 0.0f ); glVertex2i( x+w, y );
|
||||
glTexCoord2f( tex->textureData->maxS, tex->textureData->maxT ); glVertex2i( x+w, y+h );
|
||||
glTexCoord2f( 0.0f, tex->textureData->maxT ); glVertex2i( x, y+h );
|
||||
|
||||
glEnd();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,180 @@
|
||||
/*
|
||||
* ipak.h
|
||||
* General purpose data file management intended to be used
|
||||
* as a read-only memory mapped file to play nice with iPhone OS's
|
||||
* non-swapping and variable memory management.
|
||||
*
|
||||
* Created by John Carmack on 4/9/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.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
//============================================================
|
||||
//
|
||||
// In-file structures
|
||||
//
|
||||
// These stuctures are in the mapped data file, and shared
|
||||
// between the app and utility.
|
||||
//
|
||||
// Type headers are stored separately from the bulk data to minimize the
|
||||
// number of active pages.
|
||||
//
|
||||
// The full hash of the name is stored in nameHash, and nameHash&(PK_HASH_BUCKETS-1) is
|
||||
// used to chain structures of a particular type together.
|
||||
//
|
||||
//============================================================
|
||||
|
||||
#define MAX_PK_NAME 64
|
||||
typedef struct {
|
||||
int nameHash; // PK_HashName( name )
|
||||
int nextOnHashChain; // -1 = end of chain
|
||||
char name[MAX_PK_NAME]; // in canonical form: backslashes to slashes and lowercase
|
||||
} pkName_t;
|
||||
|
||||
#define PK_HASH_CHAINS 256
|
||||
typedef struct {
|
||||
int tableOfs; // // &firstStruct = (byte *)dfHeader + tableOfs
|
||||
int count;
|
||||
int structSize; // sizeof( pkWavData_t ), etc
|
||||
int hashChains[PK_HASH_CHAINS]; // -1 = end of chain
|
||||
} pkType_t;
|
||||
|
||||
// dfWavData holds everything necessary to fully create an OpenAL sample buffer
|
||||
typedef struct {
|
||||
pkName_t name;
|
||||
int wavDataOfs;
|
||||
int wavChannels; // 1 or 2
|
||||
int wavChannelBytes; // 1 or 2
|
||||
int wavRate; // 22050, etc
|
||||
int wavNumSamples; // each sample holds all the channels
|
||||
// we may want looping information here later
|
||||
} pkWavData_t;
|
||||
|
||||
// iPhone does not natively support palettized textures, but we
|
||||
// might conceivably want to support luminance and intensity textures
|
||||
// in the future.
|
||||
typedef enum {
|
||||
TF_565,
|
||||
TF_5551,
|
||||
TF_4444,
|
||||
TF_8888,
|
||||
TF_LA,
|
||||
TF_PVR4,
|
||||
TF_PVR4A,
|
||||
TF_PVR2,
|
||||
TF_PVR2A,
|
||||
} textureFormat_t;
|
||||
|
||||
// dfImageData_t holds everything necessary to fully create an OpenGL texture object
|
||||
typedef struct {
|
||||
pkName_t name;
|
||||
int picDataOfs; // the raw bits to pass to gl, mipmaps appended
|
||||
// for PVR formats, the minimum size of each level is 32 bytes
|
||||
|
||||
int format;
|
||||
int uploadWidth;
|
||||
int uploadHeight;
|
||||
int numLevels; // 1 for non mipmapped, otherwise log2( largest dimension )
|
||||
|
||||
// glTexParameters
|
||||
int wrapS;
|
||||
int wrapT;
|
||||
int minFilter;
|
||||
int magFilter;
|
||||
int aniso;
|
||||
|
||||
// The upload sizes can be larger than the source sizes for
|
||||
// non power of two sources, or for non square sources in the
|
||||
// case of PVR compression.
|
||||
int srcWidth;
|
||||
int srcHeight;
|
||||
|
||||
float maxS; // srcWidth / uploadWidth
|
||||
float maxT;
|
||||
|
||||
// Track the outlines of up to two boxes of non-transparent pixels
|
||||
// to allow optimized drawing of sprites with large empty areas.
|
||||
// The reason for two boxes is that the common lights have something
|
||||
// at the top and something at the bottom, with nothing inbetween.
|
||||
// These are inclusive bounds of the rows / columns in
|
||||
// uploadWidth / uploadHeight with non-0 alpha
|
||||
int numBounds;
|
||||
int bounds[2][2][2];
|
||||
} pkTextureData_t;
|
||||
|
||||
typedef struct {
|
||||
pkName_t name;
|
||||
int rawDataOfs; // (byte *)pkHeader + dataOfs
|
||||
int rawDataLen; // there will always be a 0 byte appended to terminate strings
|
||||
// that is not counted in this length
|
||||
} pkRawData_t;
|
||||
|
||||
#define PKFILE_VERSION 0x12340002
|
||||
typedef struct {
|
||||
int version;
|
||||
|
||||
pkType_t textures;
|
||||
pkType_t wavs;
|
||||
pkType_t raws;
|
||||
} pkHeader_t;
|
||||
|
||||
|
||||
//============================================================
|
||||
//
|
||||
// In-memory, writable structures
|
||||
//
|
||||
//============================================================
|
||||
|
||||
typedef struct {
|
||||
unsigned glTexNum;
|
||||
const pkTextureData_t *textureData;
|
||||
// we will need to add LRU links if texture caching is needed
|
||||
} pkTexture_t;
|
||||
|
||||
typedef struct {
|
||||
unsigned alBufferNum; // created with the staticBuffer extension directly in the mapped memory
|
||||
const pkWavData_t *wavData;
|
||||
} pkWav_t;
|
||||
|
||||
void PK_Init( const char *pakFileName );
|
||||
const pkName_t *PK_FindType( const char *rawName, const pkType_t *type, int *index );
|
||||
const byte * PK_FindRaw( const char *rawName, int *len ); // len can be NULL if you don't need it
|
||||
pkTexture_t * PK_FindTexture( const char *imageName );
|
||||
pkWav_t * PK_FindWav( const char *soundName );
|
||||
|
||||
// The name will be converted to canonical name (backslashes converted to slashes and lowercase)
|
||||
// before generating a hash.
|
||||
int PK_HashName( const char *name, char canonical[MAX_PK_NAME] );
|
||||
|
||||
void PK_BindTexture( pkTexture_t *tex );
|
||||
void PK_DrawTexture( pkTexture_t *tex, int x, int y );
|
||||
void PK_StretchTexture( pkTexture_t *tex, float x, float y, float w, float h );
|
||||
|
||||
extern pkHeader_t * pkHeader;
|
||||
extern int pkSize;
|
||||
|
||||
// images and wavs have writable state, so they need separate
|
||||
// structs that also point to the source in the pak file
|
||||
extern pkTexture_t *pkTextures;
|
||||
extern pkWav_t * pkWavs;
|
||||
|
||||
@@ -0,0 +1,993 @@
|
||||
/*
|
||||
* iphone_async.c
|
||||
* doom
|
||||
*
|
||||
* Created by John Carmack on 7/2/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.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#include "../doomiphone.h"
|
||||
|
||||
typedef struct {
|
||||
int msecFromLast;
|
||||
int msecToExecute;
|
||||
int sent;
|
||||
int received;
|
||||
int latency;
|
||||
} asyncStats_t;
|
||||
|
||||
#define MAX_ASYNC_LOGS 256
|
||||
static asyncStats_t asyncStats[MAX_ASYNC_LOGS];
|
||||
int asyncTicNum;
|
||||
|
||||
// we save this for the packet acknowledge, and also for debugging
|
||||
static packetServer_t lastServerPacket;
|
||||
|
||||
/*
|
||||
==================
|
||||
ShowNet
|
||||
|
||||
Graph packet receives and transmits
|
||||
==================
|
||||
*/
|
||||
void ShowNet() {
|
||||
if ( !showNet->value ) {
|
||||
return;
|
||||
}
|
||||
color4_t red = { 255, 0, 0, 255 };
|
||||
color4_t green = { 0, 255, 0, 255 };
|
||||
color4_t blue = { 0, 0, 255, 255 };
|
||||
|
||||
int now = asyncTicNum; // latch it in case it changes
|
||||
|
||||
for ( int i = 1 ; i < 30 ; i++ ) {
|
||||
asyncStats_t *lt = &asyncStats[(now - i ) & (MAX_ASYNC_LOGS-1)];
|
||||
R_Draw_Fill( 0, i * 4, lt->sent * 10, 2, red );
|
||||
R_Draw_Fill( 100, i * 4, lt->received * 10, 2, green );
|
||||
R_Draw_Fill( 200, i * 4, lt->latency * 10, 2, blue );
|
||||
}
|
||||
}
|
||||
|
||||
void ShowMiniNet() {
|
||||
if ( !miniNet->value ) {
|
||||
return;
|
||||
}
|
||||
color4_t red = { 255, 0, 0, 255 };
|
||||
color4_t green = { 0, 255, 0, 255 };
|
||||
color4_t blue = { 0, 0, 255, 255 };
|
||||
|
||||
int now = asyncTicNum; // latch it in case it changes
|
||||
now--;
|
||||
|
||||
int x = huds.menu.x;
|
||||
int y = huds.menu.y;
|
||||
|
||||
for ( int i = 0 ; i < 10 ; i++ ) {
|
||||
asyncStats_t *lt = &asyncStats[(now - i ) & (MAX_ASYNC_LOGS-1)];
|
||||
R_Draw_Fill( x, y+i * 4, lt->sent * 4, 2, red );
|
||||
R_Draw_Fill( x+20, y+i * 4, lt->received * 4, 2, green );
|
||||
R_Draw_Fill( x+40, y+i * 4, lt->latency * 4, 2, blue );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
UpdatePeerTiming
|
||||
|
||||
Calculates one way latency based on local and remote times
|
||||
==================
|
||||
*/
|
||||
void UpdatePeerTiming( netPeer_t *peer, int remoteMilliseconds ) {
|
||||
peer->lastPacketAsyncTic == asyncTicNum;
|
||||
peer->lastPacketTime = SysIphoneMilliseconds();
|
||||
peer->lastTimeDelta = abs( remoteMilliseconds - peer->lastPacketTime );
|
||||
if ( peer->lowestTimeDelta == 0 || peer->lastTimeDelta < peer->lowestTimeDelta ) {
|
||||
peer->lowestTimeDelta = peer->lastTimeDelta;
|
||||
}
|
||||
peer->oneWayLatency = peer->lastTimeDelta - peer->lowestTimeDelta;
|
||||
if ( peer->oneWayLatency < 0 ) {
|
||||
// this can happen if we context switched at a bad time
|
||||
peer->lowestTimeDelta = peer->lastTimeDelta;
|
||||
peer->oneWayLatency = 0;
|
||||
}
|
||||
// printf( "OWL:%i timeDelta:%i lowest:%i\n", peer->oneWayLatency,
|
||||
// peer->lastTimeDelta, peer->lowestTimeDelta );
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
iphoneProcessPacket
|
||||
|
||||
A packet has been received over WiFi or bluetooth
|
||||
==================
|
||||
*/
|
||||
void iphoneProcessPacket( const struct sockaddr *from, const void *data, int len ) {
|
||||
if ( len < 4 ) {
|
||||
printf( "discarding packet because len = %i.\n", len );
|
||||
return;
|
||||
}
|
||||
int packetID = *(int *)data;
|
||||
|
||||
if ( !netgame ) {
|
||||
// setup and join are only processed while in the menu system
|
||||
|
||||
if ( packetID == PACKET_VERSION_SETUP ) {
|
||||
if ( localGameID == setupPacket.gameID ) {
|
||||
// if we are sending packets, always ignore other setup packets
|
||||
printf( "discarding setup packet because we are the server\n" );
|
||||
return;
|
||||
}
|
||||
setupPacketFrameNum = iphoneFrameNum;
|
||||
|
||||
// save this packet
|
||||
// printf( "valid setup packet\n" );
|
||||
setupPacket = *(packetSetup_t *)data;
|
||||
return;
|
||||
}
|
||||
|
||||
if ( packetID == PACKET_VERSION_JOIN ) {
|
||||
// we should only process join packets if we are in the multiplayer
|
||||
// menu and running the current game
|
||||
if ( menuState != IPM_MULTIPLAYER ) {
|
||||
printf( "discarding join packet because not in IPM_MULTIPLAYER\n" );
|
||||
return;
|
||||
}
|
||||
if ( setupPacket.gameID != localGameID ) {
|
||||
printf( "discarding join packet because we aren't the server\n" );
|
||||
return;
|
||||
}
|
||||
|
||||
packetJoin_t *pj = (packetJoin_t *)data;
|
||||
if ( pj->playerID == 0 ) {
|
||||
// should never happen
|
||||
printf( "discarding join packet because playerID is 0\n" );
|
||||
return;
|
||||
}
|
||||
// add this player
|
||||
int i;
|
||||
for ( i = 0 ; i < MAXPLAYERS ; i++ ) {
|
||||
if ( setupPacket.playerID[i] == pj->playerID ) {
|
||||
netPlayers[i].peer.lastPacketTime = SysIphoneMilliseconds();
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ( i == MAXPLAYERS ) {
|
||||
// not in yet, add if possible
|
||||
for ( i = 0 ; i < MAXPLAYERS ; i++ ) {
|
||||
if ( setupPacket.playerID[i] == 0 ) {
|
||||
setupPacket.playerID[i] = pj->playerID;
|
||||
netPlayers[i].peer.address = *from;
|
||||
netPlayers[i].peer.lastPacketTime = SysIphoneMilliseconds();
|
||||
break;
|
||||
}
|
||||
}
|
||||
// if all players are active, the new join gets ignored
|
||||
}
|
||||
// printf( "valid join packet\n" );
|
||||
return;
|
||||
}
|
||||
|
||||
// no other packets are processed unless we are in the game
|
||||
printf( "discarding packet with id 0x%x when not in netgame\n", packetID );
|
||||
return;
|
||||
}
|
||||
|
||||
// the following are only for running games
|
||||
|
||||
if ( consoleplayer == 0 ) {
|
||||
// we are the server, and should only receive packetClient_t
|
||||
if ( packetID != PACKET_VERSION_CLIENT ) {
|
||||
static boolean typeErrorPrinted;
|
||||
if ( !typeErrorPrinted ) {
|
||||
typeErrorPrinted = true;
|
||||
printf( "Packet received with type 0x%x instead of 0x%x\n", packetID, PACKET_VERSION_CLIENT );
|
||||
}
|
||||
return;
|
||||
}
|
||||
packetClient_t *pc = (packetClient_t *)data;
|
||||
if ( len != sizeof( *pc ) ) {
|
||||
// this should always be an exact length match
|
||||
return;
|
||||
}
|
||||
|
||||
if ( pc->gameID != gameID ) {
|
||||
static boolean gameErrorPrinted;
|
||||
if ( !gameErrorPrinted ) {
|
||||
gameErrorPrinted = true;
|
||||
printf( "Packet received with gameID 0x%x instead of 0x%x\n", pc->gameID, gameID );
|
||||
}
|
||||
return;
|
||||
}
|
||||
assert( pc->consoleplayer > 0 && pc->consoleplayer < MAXPLAYERS );
|
||||
|
||||
netPlayer_t *np = &netPlayers[pc->consoleplayer];
|
||||
if ( np->pc.packetSequence >= pc->packetSequence ) {
|
||||
printf( "Out of order or duplicated packet from player %i\n", pc->consoleplayer );
|
||||
return;
|
||||
}
|
||||
np->peer.currentPingTics = packetSequence - pc->packetAcknowledge;
|
||||
if ( np->pc.packetSequence != pc->packetSequence - 1 ) {
|
||||
printf( "Dropped %i packets from player %i\n", pc->packetSequence - 1 - np->pc.packetSequence,
|
||||
pc->consoleplayer );
|
||||
}
|
||||
|
||||
// good packet from client
|
||||
np->pc = *pc;
|
||||
UpdatePeerTiming( &np->peer, np->pc.milliseconds );
|
||||
} else {
|
||||
// we are a client, and should only receive server packets
|
||||
if ( packetID != PACKET_VERSION_SERVER ) {
|
||||
static boolean typeErrorPrinted;
|
||||
if ( !typeErrorPrinted ) {
|
||||
typeErrorPrinted = true;
|
||||
printf( "Packet received with type 0x%x instead of 0x%x\n", packetID, PACKET_VERSION_CLIENT );
|
||||
}
|
||||
return;
|
||||
}
|
||||
packetServer_t *ps = (packetServer_t *)data;
|
||||
if ( len > sizeof( *ps ) ) {
|
||||
// packets will usually have less ticcmd_t, but never more
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ps->gameID != gameID ) {
|
||||
static boolean gameErrorPrinted;
|
||||
if ( !gameErrorPrinted ) {
|
||||
gameErrorPrinted = true;
|
||||
printf( "Packet received with gameID 0x%x instead of 0x%x\n", ps->gameID, gameID );
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ps->packetSequence <= lastServerPacket.packetSequence ) {
|
||||
printf( "Out of order or duplicated packet from server: %i <= %i\n",
|
||||
ps->packetSequence , lastServerPacket.packetSequence );
|
||||
return;
|
||||
}
|
||||
int drop = ps->packetSequence - (lastServerPacket.packetSequence + 1);
|
||||
if ( drop > 0 ) {
|
||||
printf( "Dropped %i packets from server\n", drop );
|
||||
}
|
||||
|
||||
// good packet from server
|
||||
memcpy( &lastServerPacket, ps, len );
|
||||
UpdatePeerTiming( &netServer, ps->milliseconds );
|
||||
netServer.currentPingTics = packetSequence - ps->packetAcknowledge;
|
||||
|
||||
// It is possible to have a client run a tic that hasn't been run yet on the game
|
||||
// server, since the server can be generating cmds and sending packets while
|
||||
// its game frame is hitched for an image load, so this is not an error condition.
|
||||
// assert( ps->gametic >= gametic );
|
||||
|
||||
// this should never happen
|
||||
assert( ps->maketic >= maketic );
|
||||
|
||||
// if a ticcmd_t that we need has permanently rolled off the end, we are hosed.
|
||||
// This shouldn't happen, since we don't create commands if all the clients
|
||||
// haven't processed most of the ones already sent.
|
||||
if ( ps->maketic - gametic >= BACKUPTICS ) {
|
||||
printf( "BACKUPTICS exceeded: ps->maketic %i, gametic %i\n",
|
||||
ps->maketic, gametic );
|
||||
netGameFailure = NF_INTERRUPTED;
|
||||
}
|
||||
|
||||
// move over the new commands
|
||||
// it is possible that some early frames of these are redundant, due
|
||||
// to packets crossing in flight.
|
||||
ticcmd_t *cmd_p = ps->netcmds;
|
||||
for ( int i = ps->starttic ; i < ps->maketic ; i++ ) {
|
||||
for ( int j = 0 ; j < MAXPLAYERS ; j++ ) {
|
||||
if ( playeringame[j] ) {
|
||||
netcmds[j][i&BACKUPTICMASK] = *cmd_p++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// copy this after the cmds have been updated
|
||||
maketic = ps->maketic;
|
||||
|
||||
// check consistancy for all in-game players on the most
|
||||
// recent gametic that the server knew this client had run
|
||||
int checkTic = ps->consistancyTic;
|
||||
assert( checkTic > gametic - BACKUPTICS ); // if older than this, we have lost the data
|
||||
checkTic &= BACKUPTICMASK;
|
||||
for ( int i = 0 ; i < MAXPLAYERS ; i++ ) {
|
||||
if ( playeringame[i] ) {
|
||||
if ( ps->consistancy[i] != consistancy[i][checkTic] ) {
|
||||
printf( "ConsistancyFailure for player %i on consistancyTic %i\n",
|
||||
i, ps->consistancyTic );
|
||||
netGameFailure = NF_CONSISTANCY;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
SendSetupPacketIfNecessary
|
||||
|
||||
the server sends out a setup packet to each joined client so they
|
||||
can see the game options needed to start the game.
|
||||
==================
|
||||
*/
|
||||
void SendSetupPacketIfNecessary() {
|
||||
if ( setupPacket.gameID != localGameID ) {
|
||||
// we aren't the server
|
||||
return;
|
||||
}
|
||||
|
||||
if ( gametic >= 2 ) {
|
||||
// everyone has already started, so they don't need more setup packets
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
setupPacket.sendCount++;
|
||||
|
||||
// player 0 is always the server, no need to send to ourselves
|
||||
for ( int i = 1 ; i < MAXPLAYERS ; i++ ) {
|
||||
if ( setupPacket.playerID[i] == 0 ) {
|
||||
continue;
|
||||
}
|
||||
int r = sendto( gameSocket, &setupPacket, sizeof( setupPacket ), 0,
|
||||
&netPlayers[i].peer.address, sizeof( netPlayers[i].peer.address ) );
|
||||
if ( r == -1 ) {
|
||||
Com_Printf( "UDP sendTo failed: %s\n", strerror( errno ) );
|
||||
close( gameSocket );
|
||||
gameSocket = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
==================
|
||||
DeadBandAdjust
|
||||
|
||||
Compresses the 0.0 - 1.0 range into deadband - 1.0
|
||||
==================
|
||||
*/
|
||||
float DeadBandAdjust( float f, float deadBand ) {
|
||||
if ( f < 0 ) {
|
||||
return -DeadBandAdjust( -f, deadBand );
|
||||
}
|
||||
if ( f > 1.0 ) {
|
||||
return 1.0;
|
||||
}
|
||||
if ( f < deadBand ) {
|
||||
return 0;
|
||||
}
|
||||
return (f-deadBand) / (1.0 - deadBand);
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
AxisHit
|
||||
|
||||
Returns a -1 to 1 range
|
||||
|
||||
If activeFraction is less than 1.0, the range will clamp
|
||||
to the limits before the edge of the box is hit.
|
||||
==================
|
||||
*/
|
||||
float AxisHit( ibutton_t *hud ) {
|
||||
// will be set true if -1 or 1
|
||||
hud->drawAsLimit = false;
|
||||
|
||||
if ( hud->buttonFlags & BF_IGNORE ) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
touch_t *t = hud->touch;
|
||||
if ( !t ) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int centerX, centerY;
|
||||
|
||||
if ( centerSticks->value ) {
|
||||
// center on each touch
|
||||
centerX = hud->downX;
|
||||
centerY = hud->downY;
|
||||
} else {
|
||||
centerX = hud->x + hud->drawWidth / 2;
|
||||
centerY = hud->y + hud->drawHeight / 2;
|
||||
}
|
||||
|
||||
float w = hud->drawWidth * 0.5 * hud->scale;
|
||||
float h = hud->drawHeight * 0.5 * hud->scale;
|
||||
int x = t->x - centerX;
|
||||
int y = t->y - centerY;
|
||||
float f;
|
||||
int isXaxis = ( hud != &huds.forwardStick );
|
||||
if ( isXaxis ) {
|
||||
f = (float)x / w;
|
||||
} else {
|
||||
f = (float)y / h;
|
||||
}
|
||||
float deadBand = stickDeadBand->value;
|
||||
if ( hud == &huds.turnStick ) {
|
||||
deadBand = 0;
|
||||
}
|
||||
if ( f > deadBand ) {
|
||||
f -= deadBand;
|
||||
} else if ( f < -deadBand ) {
|
||||
f += deadBand;
|
||||
} else {
|
||||
// inside the deadband
|
||||
return 0;
|
||||
}
|
||||
|
||||
// adjust so you can hit the limit even if the control is drawn at the very edge
|
||||
// of the screen
|
||||
f /= (0.95-deadBand);
|
||||
if ( f > 1.0f ) {
|
||||
f = 1.0f;
|
||||
hud->drawAsLimit = true;
|
||||
} else if ( f < -1.0f ) {
|
||||
f = -1.0f;
|
||||
hud->drawAsLimit = true;
|
||||
}
|
||||
|
||||
if ( hud == &huds.turnStick && rampTurn->value ) {
|
||||
// do "gamma corrected" movement, so changes are always proportional
|
||||
if ( f > 0 ) {
|
||||
f = 0.01 * pow( 1.047, f * 100 );
|
||||
} else {
|
||||
f = -0.01 * pow( 1.047, f * -100 );
|
||||
}
|
||||
}
|
||||
return f;
|
||||
}
|
||||
|
||||
|
||||
static const float NOT_TOUCHED_STATE = 99999.0f;
|
||||
|
||||
float RotorControl( ibutton_t *hud ) {
|
||||
if ( hud->buttonFlags & BF_IGNORE ) {
|
||||
return 0;
|
||||
}
|
||||
touch_t *t = hud->touch;
|
||||
if ( !t ) {
|
||||
// no touches in the control
|
||||
hud->touchState = NOT_TOUCHED_STATE;
|
||||
return 0;
|
||||
}
|
||||
float delta[2];
|
||||
|
||||
int centerX = hud->x + hud->drawWidth / 2;
|
||||
int centerY = hud->y + hud->drawHeight / 2;
|
||||
|
||||
delta[0] = t->x - centerX;
|
||||
delta[1] = t->y - centerY;
|
||||
|
||||
float rotorAngle = atan2( delta[1], delta[0] );
|
||||
if ( hud->touchState == NOT_TOUCHED_STATE ) {
|
||||
// just touched, haven't moved yet
|
||||
hud->touchState = rotorAngle;
|
||||
return 0;
|
||||
}
|
||||
float deltaAngle = rotorAngle - hud->touchState;
|
||||
// handle the wrap around cases
|
||||
if ( deltaAngle >= M_PI ) {
|
||||
deltaAngle = deltaAngle - 2*M_PI;
|
||||
} else if ( deltaAngle <= -M_PI ) {
|
||||
deltaAngle = 2*M_PI + deltaAngle;
|
||||
}
|
||||
hud->touchState = rotorAngle;
|
||||
hud->drawState += deltaAngle;
|
||||
return deltaAngle / (2*M_PI);
|
||||
}
|
||||
|
||||
#define TURBOTHRESHOLD 0x32
|
||||
static int ClampMove( int v ) {
|
||||
if ( v > TURBOTHRESHOLD ) {
|
||||
return TURBOTHRESHOLD;
|
||||
}
|
||||
if ( v < -TURBOTHRESHOLD ) {
|
||||
return -TURBOTHRESHOLD;
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
==================
|
||||
iphoneBuildTiccmd
|
||||
|
||||
Use touch and tilt controls to set up a doom ticcmd_t
|
||||
==================
|
||||
*/
|
||||
static void iphoneBuildTiccmd(ticcmd_t* cmd) {
|
||||
memset(cmd,0,sizeof*cmd);
|
||||
// cmd->consistancy = consistancy[consoleplayer][maketic & BACKUPTICMASK];
|
||||
|
||||
if ( menuState != IPM_GAME ) {
|
||||
// if in the menus, always generate an empty event
|
||||
return;
|
||||
}
|
||||
|
||||
// the respawn button triggers a use
|
||||
if ( respawnActive ) {
|
||||
cmd->buttons |= BT_USE;
|
||||
respawnActive = false;
|
||||
}
|
||||
|
||||
if ( gamestate != GS_LEVEL ) {
|
||||
// at intermissions, all taps equal attack
|
||||
// FIXME: better latched value
|
||||
if ( numTouches == numPrevTouches + 1 ) {
|
||||
cmd->buttons |= BT_ATTACK;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// don't allow movement control use during automap
|
||||
if ( automapmode & am_active ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// don't built a tic when dead, other than the respawn use
|
||||
if ( players[consoleplayer].playerstate == PST_DEAD ) {
|
||||
return;
|
||||
}
|
||||
|
||||
//------------------------
|
||||
// No controls during weapon-select screen
|
||||
//------------------------
|
||||
boolean weaponCycle = false;
|
||||
if ( drawWeaponSelect ) {
|
||||
// if the weaponSelect overlay is up, continue tracking held touches
|
||||
// until the are released
|
||||
for ( ibutton_t *hud = (ibutton_t *)&huds ; hud != (ibutton_t *)(&huds+1) ; hud++ ) {
|
||||
if ( hud->touch || hud == &huds.weaponSelect ) {
|
||||
UpdateHudTouch( hud );
|
||||
}
|
||||
}
|
||||
|
||||
// Re-tapping in the weapon select area will cycle to the next weapon.
|
||||
// The action happens on initial touch.
|
||||
touch_t *t = huds.weaponSelect.touch;
|
||||
if ( t && t->down && t->stateCount == 1 ) {
|
||||
drawWeaponSelect = false;
|
||||
t->stateCount++; // ensure it won't bring it back up
|
||||
weaponCycle = true;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------
|
||||
// gameplay controls
|
||||
//------------------------
|
||||
|
||||
// update all the hud touch states
|
||||
if ( menuState == IPM_GAME ) {
|
||||
UpdateHudTouch( &huds.forwardStick );
|
||||
UpdateHudTouch( &huds.sideStick );
|
||||
UpdateHudTouch( &huds.turnStick );
|
||||
UpdateHudTouch( &huds.turnRotor );
|
||||
UpdateHudTouch( &huds.weaponSelect );
|
||||
}
|
||||
// tap in the lower center for weapon switch
|
||||
touch_t *t = huds.weaponSelect.touch;
|
||||
if ( t && t->down && t->stateCount == 1 ) {
|
||||
drawWeaponSelect = true;
|
||||
}
|
||||
|
||||
// hack to let a single touch control both hud elements on combo sticks
|
||||
// This is dependent on the order in the structure, and probably not a good
|
||||
// way to do things.
|
||||
if ( huds.sideStick.x == huds.forwardStick.x && huds.sideStick.y == huds.forwardStick.y ) {
|
||||
huds.sideStick.touch = huds.forwardStick.touch;
|
||||
huds.sideStick.downX = huds.forwardStick.downX;
|
||||
huds.sideStick.downY = huds.forwardStick.downY;
|
||||
}
|
||||
if ( huds.turnStick.x == huds.forwardStick.x && huds.turnStick.y == huds.forwardStick.y ) {
|
||||
huds.turnStick.touch = huds.forwardStick.touch;
|
||||
huds.turnStick.downX = huds.forwardStick.downX;
|
||||
huds.turnStick.downY = huds.forwardStick.downY;
|
||||
}
|
||||
|
||||
// the fire button doesn't grab touches
|
||||
{
|
||||
int x = huds.fire.x - ( huds.fire.drawWidth >> 1 );
|
||||
int y = huds.fire.y - ( huds.fire.drawHeight >> 1 );
|
||||
int w = huds.fire.drawWidth << 1;
|
||||
int h = huds.fire.drawHeight << 1;
|
||||
if ( AnyTouchInBounds( x, y, w, h ) ) {
|
||||
cmd->buttons |= BT_ATTACK;
|
||||
huds.fire.buttonFlags |= BF_DRAW_ACTIVE; // draw with color
|
||||
} else {
|
||||
huds.fire.buttonFlags &= ~BF_DRAW_ACTIVE;
|
||||
}
|
||||
}
|
||||
int forwardmove;
|
||||
int sidemove;
|
||||
|
||||
// the edge of the drawn control should give the maximum
|
||||
// legal doom movement speed
|
||||
huds.forwardStick.scale = stickMove->value / 128.0f;
|
||||
huds.sideStick.scale = stickMove->value / 128.0f;
|
||||
|
||||
forwardmove = -TURBOTHRESHOLD * AxisHit( &huds.forwardStick );
|
||||
sidemove = TURBOTHRESHOLD * AxisHit( &huds.sideStick );
|
||||
|
||||
huds.turnStick.scale = stickTurn->value / 128.0f;
|
||||
cmd->angleturn = -1500.0f * AxisHit( &huds.turnStick );
|
||||
|
||||
// rotary wheel
|
||||
cmd->angleturn -= rotorTurn->value * RotorControl( &huds.turnRotor );
|
||||
|
||||
// accelerometer tilting
|
||||
sidemove += tiltMove->value * DeadBandAdjust( tilt, tiltDeadBand->value );
|
||||
cmd->angleturn -= tiltTurn->value * DeadBandAdjust( tilt, tiltDeadBand->value );
|
||||
|
||||
// clamp movements
|
||||
cmd->forwardmove = ClampMove( forwardmove );
|
||||
cmd->sidemove = ClampMove( sidemove );
|
||||
|
||||
// tap in the upper center for use
|
||||
if ( TouchPressed( 140, 0, 240, 200 ) ) {
|
||||
cmd->buttons |= BT_USE;
|
||||
}
|
||||
|
||||
// auto-use if the game thread found a usable line in front of the player
|
||||
if ( autoUse->value && autoUseActive ) {
|
||||
if ( cmd->buttons & BT_USE ) {
|
||||
// Allow a tap to briefly cancel the auto-use, which works around
|
||||
// some issues with incorrectly started auto-uses preventing
|
||||
// a real door from opening.
|
||||
cmd->buttons &= ~BT_USE;
|
||||
} else {
|
||||
cmd->buttons |= BT_USE;
|
||||
}
|
||||
}
|
||||
|
||||
if ( weaponSelected != -1 ) {
|
||||
cmd->buttons |= BT_CHANGE;
|
||||
cmd->buttons |= weaponSelected<<BT_WEAPONSHIFT;
|
||||
weaponSelected = -1;
|
||||
} else {
|
||||
// auto-cycle weapons when firing on empty
|
||||
if ( players[consoleplayer].attackdown && !P_CheckAmmo(&players[consoleplayer]) ) {
|
||||
weaponCycle = true;
|
||||
}
|
||||
|
||||
// weapon switch
|
||||
int newweapon = wp_nochange;
|
||||
if ( weaponCycle ) {
|
||||
// witch to next weapon when out of ammo
|
||||
newweapon = P_SwitchWeapon(&players[consoleplayer]);
|
||||
}
|
||||
|
||||
if (newweapon != wp_nochange) {
|
||||
cmd->buttons |= BT_CHANGE;
|
||||
cmd->buttons |= newweapon<<BT_WEAPONSHIFT;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
ShouldSendPacket
|
||||
|
||||
WiFi can suffer from bad "pileup" because of the link-level
|
||||
retransmit behavior. What we would dearly love is an ioctl or
|
||||
something that returned a value of how many packets are backed
|
||||
up in the transmit queue, but that would be terribly driver
|
||||
specific, and we are unlikely to ever see it.
|
||||
|
||||
We are going to infer that there is a pileup by looking at the
|
||||
round trip latency. If we ever support internet connections with
|
||||
real latencies that are this large, we will have to use something
|
||||
else.
|
||||
==================
|
||||
*/
|
||||
// while 1 is usual, some combinations of AsyncTic phases will give 2 commonly,
|
||||
// and we don't want to throttle back in those cases.
|
||||
static const int okPacketLatency = 4;
|
||||
static const int okOneWayLatency = 70;
|
||||
static boolean ShouldSendPacket( netPeer_t *peer, int packetLatency ) {
|
||||
if ( throttle->value == 0 ) {
|
||||
// disabled completely, always send
|
||||
return true;
|
||||
}
|
||||
|
||||
if ( peer == &netServer ) {
|
||||
if ( throttle->value == 2 ) {
|
||||
return false; // don't send client messages at all
|
||||
}
|
||||
} else {
|
||||
if ( throttle->value == 3 ) { // don't send server messages at all
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// immediately fire back a packet if it looks like we just got one through
|
||||
// clearly
|
||||
if ( peer->lastPacketAsyncTic == asyncTicNum && peer->oneWayLatency < okOneWayLatency ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ( packetLatency <= okPacketLatency ) {
|
||||
return true;
|
||||
}
|
||||
// limit from 1 to 4, in worst case only transmit a packet
|
||||
// every 16 frames, or about half a second
|
||||
packetLatency -= okPacketLatency;
|
||||
if ( packetLatency > 4 ) {
|
||||
packetLatency = 4;
|
||||
}
|
||||
|
||||
if ( asyncTicNum & ((1<<packetLatency)-1) ) {
|
||||
return false; // inhibit transmit this frame
|
||||
}
|
||||
|
||||
printf( "Sending on packetLatency %i\n", packetLatency );
|
||||
|
||||
return true; // we are throttling back, but transmit this frame
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
==================
|
||||
iphoneAsyncTic
|
||||
|
||||
This is called by a 30hz scheduled timeer in the main application thread.
|
||||
Commands are generated and sent to the packet server
|
||||
on this regular basis, regardless of the frame rate held by iphoneFrame().
|
||||
This thread should be higher priority, so it always interrupts the game
|
||||
thread.
|
||||
|
||||
It might be nice to run the game tics here, but the rendering code is not
|
||||
thread safe with the game, so it isn't an option.
|
||||
==================
|
||||
*/
|
||||
void iphoneAsyncTic() {
|
||||
// log our timing accuracy (seems to be within a few msec -- not bad)
|
||||
static int prev;
|
||||
int now = SysIphoneMilliseconds();
|
||||
asyncStats_t *stats = &asyncStats[asyncTicNum&(MAX_ASYNC_LOGS-1)];
|
||||
asyncTicNum++;
|
||||
|
||||
memset( stats, 0, sizeof( *stats ) );
|
||||
stats->msecFromLast = now - prev;
|
||||
stats->msecToExecute = 0;
|
||||
|
||||
prev = now;
|
||||
|
||||
// listen for changes to available servers
|
||||
ProcessDNSMessages();
|
||||
|
||||
// send out the setup packets if we are just starting the game
|
||||
SendSetupPacketIfNecessary();
|
||||
|
||||
// don't generate any commands while loading levels
|
||||
if ( iphoneFrameNum == levelLoadFrameNum ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// don't let the game thread mess with touches during the async tic execution
|
||||
pthread_mutex_lock( &eventMutex );
|
||||
|
||||
int afterLock = SysIphoneMilliseconds();
|
||||
|
||||
// latch the current touches for processing
|
||||
for ( int i = 0 ; i < MAX_TOUCHES ; i++ ) {
|
||||
touch_t *t = &sysTouches[i];
|
||||
|
||||
gameTouches[i] = *t;
|
||||
|
||||
// handle the special case of a touch that went down and up
|
||||
// inside a single frame
|
||||
if ( t->stateCount == -1 ) {
|
||||
gameTouches[i].stateCount = 1;
|
||||
t->stateCount = 0;
|
||||
t->down = false;
|
||||
} else {
|
||||
t->stateCount++;
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------
|
||||
// read network packets
|
||||
//
|
||||
// we may receive multiple packets in one frame due to timer skew
|
||||
// even with only one other player
|
||||
//---------------------------------
|
||||
if ( !netGameFailure ) {
|
||||
byte packet[1500];
|
||||
|
||||
while( gameSocket > 0 ) {
|
||||
struct sockaddr from;
|
||||
unsigned senderLen = sizeof( from );
|
||||
int r = recvfrom( gameSocket, &packet, sizeof( packet ), 0, &from, &senderLen );
|
||||
if ( r == -1 ) {
|
||||
if ( errno != EAGAIN ) {
|
||||
perror( "recvfrom" );
|
||||
}
|
||||
break;
|
||||
}
|
||||
stats->received++;
|
||||
iphoneProcessPacket( &from, packet, r );
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------
|
||||
// Create local user command
|
||||
//
|
||||
// We always create one, but it might not wind up being used for a game
|
||||
// tic if it doesn't make it to the server at the right time.
|
||||
//---------------------------------
|
||||
ticcmd_t cmd;
|
||||
iphoneBuildTiccmd( &cmd );
|
||||
|
||||
//---------------------------------
|
||||
// If we are a client, send our command to the server
|
||||
//---------------------------------
|
||||
if ( consoleplayer != 0 ) {
|
||||
if ( gameID != 0 && netgame && !netGameFailure ) {
|
||||
stats->latency = packetSequence - lastServerPacket.packetAcknowledge;
|
||||
if ( ShouldSendPacket( &netServer, packetSequence - lastServerPacket.packetAcknowledge ) ) {
|
||||
packetClient_t cp;
|
||||
memset( &cp, 0, sizeof( cp ) );
|
||||
cp.packetType = PACKET_VERSION_CLIENT;
|
||||
cp.gameID = gameID;
|
||||
cp.packetAcknowledge = lastServerPacket.packetSequence;
|
||||
cp.milliseconds = SysIphoneMilliseconds();
|
||||
cp.packetSequence = packetSequence++;
|
||||
cp.consoleplayer = consoleplayer;
|
||||
cp.gametic = gametic;
|
||||
cp.cmd = cmd;
|
||||
int r = sendto( gameSocket, &cp, sizeof( cp ), 0, &netServer.address, sizeof( netServer.address ) );
|
||||
stats->sent++;
|
||||
if ( r == -1 ) {
|
||||
printf( "UDP sendTo failed: %s\n", strerror( errno ) );
|
||||
close( gameSocket );
|
||||
gameSocket = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
// take our command directly
|
||||
netPlayers[0].pc.cmd = cmd;
|
||||
netPlayers[0].pc.gametic = gametic;
|
||||
netPlayers[0].peer.lastPacketTime = now;
|
||||
|
||||
|
||||
//---------------------------------
|
||||
// Decide if we want to latch the current commands for execution by the game
|
||||
//
|
||||
//---------------------------------
|
||||
int ticIndex = maketic & BACKUPTICMASK;
|
||||
|
||||
int worstTic = gametic;
|
||||
for ( int i = 0 ; i < MAXPLAYERS ; i++ ) {
|
||||
if ( playeringame[i] ) {
|
||||
netcmds[i][ticIndex] = netPlayers[i].pc.cmd;
|
||||
if ( netPlayers[i].pc.gametic < worstTic ) {
|
||||
worstTic = netPlayers[i].pc.gametic;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// only let the server get a few tics ahead of any client, so if
|
||||
// anyone is having significant net delivery problems, everyone will
|
||||
// stall instead of losing the player. If this is too small, then
|
||||
// every little hitch that any player gets will cause everyone to hitch.
|
||||
if ( maketic - worstTic < netBuffer->value ) {
|
||||
maketic++;
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------
|
||||
// Build server packets to send to clients
|
||||
//
|
||||
// Always send out the current command set over the network
|
||||
// even if we didn't create a new command, in case we are just
|
||||
// recovering from a lot of dropped packets.
|
||||
//---------------------------------
|
||||
if ( netgame && !netGameFailure ) {
|
||||
// since we are sampling a shared wireless network, any of the player's
|
||||
// latencies should be a good enough metric
|
||||
stats->latency = packetSequence - netPlayers[1].pc.packetAcknowledge;
|
||||
|
||||
if ( ShouldSendPacket( &netPlayers[1].peer, stats->latency ) ) {
|
||||
packetServer_t gp;
|
||||
memset( &gp, 0, sizeof( gp ) );
|
||||
gp.packetType = PACKET_VERSION_SERVER;
|
||||
gp.gameID = gameID;
|
||||
gp.packetSequence = packetSequence++;
|
||||
gp.maketic = maketic;
|
||||
memcpy( gp.netcmds, netcmds, sizeof( gp.netcmds ) );
|
||||
|
||||
//---------------------------------
|
||||
// Send network packets to the clients
|
||||
//---------------------------------
|
||||
for ( int i = 1 ; i < MAXPLAYERS ; i++ ) {
|
||||
if ( !playeringame[i] ) {
|
||||
continue;
|
||||
}
|
||||
if ( gameSocket <= 0 ) {
|
||||
continue;
|
||||
}
|
||||
netPlayer_t *np = &netPlayers[i];
|
||||
|
||||
// only send over the ticcmd that this client needs
|
||||
gp.starttic = np->pc.gametic;
|
||||
ticcmd_t *cmd_p = gp.netcmds;
|
||||
for ( int i = gp.starttic ; i < gp.maketic ; i++ ) {
|
||||
for ( int j = 0 ; j < MAXPLAYERS ; j++ ) {
|
||||
if ( playeringame[j] ) {
|
||||
*cmd_p++ = netcmds[j][i&BACKUPTICMASK];
|
||||
}
|
||||
}
|
||||
}
|
||||
int packetSize = (byte *)cmd_p - (byte *)&gp;
|
||||
|
||||
// use the most recent tic that both the client and
|
||||
// server have run
|
||||
gp.consistancyTic = np->pc.gametic < gametic ? np->pc.gametic : gametic;
|
||||
gp.consistancyTic--;
|
||||
|
||||
for ( int j = 0 ; j < MAXPLAYERS ; j++ ) {
|
||||
gp.consistancy[j] = consistancy[j][gp.consistancyTic&BACKUPTICMASK];
|
||||
}
|
||||
|
||||
gp.packetAcknowledge = np->pc.packetSequence;
|
||||
gp.milliseconds = SysIphoneMilliseconds();
|
||||
|
||||
// transmit the packet
|
||||
stats->sent++;
|
||||
int r = sendto( gameSocket, &gp, packetSize, 0, &np->peer.address, sizeof( np->peer.address ) );
|
||||
if ( r == -1 ) {
|
||||
printf( "UDP sendTo failed: %s\n", strerror( errno ) );
|
||||
close( gameSocket );
|
||||
gameSocket = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stats->msecToExecute = SysIphoneMilliseconds() - now;
|
||||
if ( stats->msecToExecute > 6 ) {
|
||||
printf( "long asyncTic %i: %i msec (%i in lock), %i packets\n", asyncTicNum - 1, stats->msecToExecute,
|
||||
afterLock - now, stats->received );
|
||||
}
|
||||
|
||||
// the game thread can now swap touches
|
||||
pthread_mutex_unlock( &eventMutex );
|
||||
|
||||
// signal the main thread that is probably blocked on this semaphore
|
||||
if ( sem_post( ticSemaphore ) == -1 ) {
|
||||
perror( "sem_post");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,558 @@
|
||||
/*
|
||||
|
||||
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.
|
||||
|
||||
*/
|
||||
|
||||
// this is the version number displayed on the menu screen
|
||||
#define DOOM_IPHONE_VERSION 0.9
|
||||
|
||||
// if defined, the game runs in a separate thread from the app event loop
|
||||
#define USE_GAME_THREAD
|
||||
|
||||
typedef enum menuState {
|
||||
IPM_GAME,
|
||||
IPM_MAIN,
|
||||
IPM_MAPS,
|
||||
IPM_MULTIPLAYER,
|
||||
IPM_CONTROLS,
|
||||
IPM_OPTIONS,
|
||||
IPM_HUDEDIT,
|
||||
IPM_PACKET_TEST
|
||||
} menuState_t;
|
||||
|
||||
extern menuState_t menuState;
|
||||
|
||||
void iphoneDrawMenus();
|
||||
|
||||
#define VID_WIDTH 480
|
||||
#define VID_HEIGHT 320
|
||||
|
||||
#define MAX_SKILLS 5
|
||||
#define MAX_MAPS 200
|
||||
|
||||
#define MF_TRIED 1
|
||||
#define MF_COMPLETED 2
|
||||
#define MF_KILLS 4
|
||||
#define MF_SECRETS 8
|
||||
#define MF_TREASURE 16
|
||||
#define MF_TIME 32
|
||||
|
||||
// we want to track mapStats for downloaded content, so we
|
||||
// won't have a known number of these
|
||||
typedef struct {
|
||||
int dataset;
|
||||
int episode;
|
||||
int map;
|
||||
|
||||
int completionFlags[MAX_SKILLS];
|
||||
} mapStats_t;
|
||||
|
||||
// the level select screen returns this
|
||||
typedef struct {
|
||||
int dataset;
|
||||
int episode;
|
||||
int map;
|
||||
int skill;
|
||||
} mapStart_t;
|
||||
|
||||
// this structure is saved out at the head of the binary save file,
|
||||
// and allows all the menus to work without having to load a game save
|
||||
typedef struct {
|
||||
mapStart_t map; // this is the map currently being run
|
||||
|
||||
int saveGameIsValid; // when 0, resume game will just be a new game
|
||||
|
||||
// if someone downloads more than MAX_MAPS, they won't get stat tracking on them.
|
||||
int numMapStats;
|
||||
mapStats_t mapStats[MAX_MAPS];
|
||||
} playState_t;
|
||||
|
||||
extern playState_t playState;
|
||||
|
||||
extern boolean levelHasBeenLoaded; // determines if "resume game" does a loadGame and exiting does a saveGame
|
||||
|
||||
extern pkTexture_t *arialFontTexture;
|
||||
|
||||
// set to 1 when app is exiting to cause game thread to do a save game,
|
||||
// which would not be safe to do from the event thread
|
||||
extern volatile int saveOnExitState;
|
||||
|
||||
extern int asyncTicNum; // 30hz
|
||||
extern int iphoneFrameNum; // frame rate dependent, max of 30hz
|
||||
extern int levelLoadFrameNum;
|
||||
extern int consoleActive;
|
||||
extern boolean iphoneTimeDemo;
|
||||
extern int timeDemoStart;
|
||||
extern char timeDemoResultString[80];
|
||||
|
||||
extern cvar_t *skill;
|
||||
extern cvar_t *episode;
|
||||
extern cvar_t *controlScheme;
|
||||
extern cvar_t *stickMove;
|
||||
extern cvar_t *stickTurn;
|
||||
extern cvar_t *rotorTurn;
|
||||
extern cvar_t *stickDeadBand;
|
||||
extern cvar_t *tiltTurn;
|
||||
extern cvar_t *tiltMove;
|
||||
extern cvar_t *tiltDeadBand;
|
||||
extern cvar_t *tiltAverages;
|
||||
extern cvar_t *music;
|
||||
extern cvar_t *miniNet;
|
||||
extern cvar_t *showTilt;
|
||||
extern cvar_t *showTime;
|
||||
extern cvar_t *showNet;
|
||||
extern cvar_t *showSound;
|
||||
extern cvar_t *cropSprites;
|
||||
extern cvar_t *revLand;
|
||||
extern cvar_t *mapScale;
|
||||
extern cvar_t *drawControls;
|
||||
extern cvar_t *autoUse;
|
||||
extern cvar_t *statusBar;
|
||||
extern cvar_t *touchClick;
|
||||
extern cvar_t *messages;
|
||||
extern cvar_t *timeLimit;
|
||||
extern cvar_t *fragLimit;
|
||||
extern cvar_t *mpDeathmatch;
|
||||
extern cvar_t *mpDataset;
|
||||
extern cvar_t *mpSkill;
|
||||
extern cvar_t *mpEpisode;
|
||||
extern cvar_t *mpMap;
|
||||
extern cvar_t *glfinish;
|
||||
extern cvar_t *mapSelectY;
|
||||
extern cvar_t *throttle;
|
||||
extern cvar_t *centerSticks;
|
||||
extern cvar_t *rampTurn;
|
||||
extern cvar_t *netBuffer;
|
||||
|
||||
extern int numTouches;
|
||||
extern int touches[5][2]; // [0] = x, [1] = y in landscape mode, raster order with y = 0 at top
|
||||
// so we can detect button releases
|
||||
extern int numPrevTouches;
|
||||
extern int prevTouches[5][2];
|
||||
|
||||
extern float tilt; // -1.0 to 1.0
|
||||
extern float tiltPitch;
|
||||
|
||||
extern boolean drawWeaponSelect; // true when the weapon select overlay is up
|
||||
extern int weaponSelected; // -1 for no change
|
||||
|
||||
typedef unsigned char color4_t[4];
|
||||
typedef unsigned char color3_t[3];
|
||||
|
||||
// networking
|
||||
enum {
|
||||
PACKET_VERSION_BASE = 0x24350010,
|
||||
PACKET_VERSION_SETUP,
|
||||
PACKET_VERSION_JOIN,
|
||||
PACKET_VERSION_CLIENT,
|
||||
PACKET_VERSION_SERVER
|
||||
} packetType_t;
|
||||
|
||||
#define DOOM_PORT 14666 // setup packets will go to DOOM_PORT+1
|
||||
|
||||
// the server sends out a setup packet by broadcast, and also directly addressed
|
||||
// to each client that has joined the game because broadcast packets have truly
|
||||
// crappy delivery characteristics over WiFi
|
||||
typedef struct {
|
||||
int packetType;
|
||||
int gameID; // every change to anything in packetSetup_t must change gameID
|
||||
int startGame; // when this is set, start running the game
|
||||
|
||||
int sendCount; // just for packet drop tests
|
||||
|
||||
mapStart_t map;
|
||||
|
||||
int deathmatch;
|
||||
int fraglimit;
|
||||
int timelimit;
|
||||
|
||||
int playerID[MAXPLAYERS]; // 0 = not in game
|
||||
} packetSetup_t;
|
||||
|
||||
// If we have received a recent setup packet before hitting the multiplayer
|
||||
// button, we will send a join packet to that server. Otherwise, we will
|
||||
// start acting as a new server.
|
||||
typedef struct {
|
||||
int packetType;
|
||||
|
||||
// this should match the packetSetup.gameID
|
||||
int gameID;
|
||||
|
||||
int playerID;
|
||||
} packetJoin_t;
|
||||
|
||||
typedef struct {
|
||||
int packetType;
|
||||
|
||||
// gameID is determined randomly during setup, any packet that doesn't
|
||||
// match is discarded
|
||||
int gameID;
|
||||
|
||||
// this could be used to tell when we are dropping client packets
|
||||
// but it isn't critical
|
||||
int packetSequence;
|
||||
|
||||
// used to show current round trip latency
|
||||
int packetAcknowledge;
|
||||
|
||||
// the client's clock at the time the packet was sent, used
|
||||
// to track one-way latency
|
||||
int milliseconds;
|
||||
|
||||
// the server could match this up based on ip alone, but it is nice to have
|
||||
int consoleplayer;
|
||||
|
||||
// the last tic that the client has run
|
||||
int gametic;
|
||||
|
||||
// some commands will get missed over the network
|
||||
ticcmd_t cmd;
|
||||
} packetClient_t;
|
||||
|
||||
typedef struct {
|
||||
int packetType;
|
||||
|
||||
// gameID is determined randomly during setup, any packet that doesn't
|
||||
// match is discarded
|
||||
int gameID;
|
||||
|
||||
// used to detect packet drops
|
||||
int packetSequence;
|
||||
|
||||
// used to show current round trip latency
|
||||
int packetAcknowledge;
|
||||
|
||||
// the server's clock at the time the packet was sent, used
|
||||
// to track one-way latency
|
||||
int milliseconds;
|
||||
|
||||
// consistancyTic will be the last acknowledged gametic for this
|
||||
// particular client
|
||||
int consistancyTic;
|
||||
|
||||
// constancy is used to see if somehow the game running on the
|
||||
// client has diverged from the one running on the server,
|
||||
// which is an unrecoverable error
|
||||
short consistancy[MAXPLAYERS];
|
||||
|
||||
// this will be the last pc.gametic from the player
|
||||
int starttic;
|
||||
|
||||
// netcmds[][(maketic-1)&BACKUPTICMASK] is the most recent
|
||||
int maketic;
|
||||
|
||||
// only the [playersInGame*(maketic-starttic)] will be transmitted
|
||||
ticcmd_t netcmds[MAXPLAYERS*BACKUPTICS];
|
||||
} packetServer_t;
|
||||
|
||||
extern int gameSocket;
|
||||
extern struct sockaddr_in gameSocketAddress;
|
||||
|
||||
extern int playerID;
|
||||
extern int gameID;
|
||||
extern int localGameID;
|
||||
extern int packetSequence;
|
||||
|
||||
// Only one game can be set up at a time on a given wireless segment, although
|
||||
// several independent games can be played.
|
||||
// If a valid setupPacket has arrived in the last second, that will be the
|
||||
// displayed game, otherwise the local system starts sending out setupPackets.
|
||||
extern packetSetup_t setupPacket;
|
||||
extern int setupPacketFrameNum;
|
||||
extern int localGameID; // change every time we take over as the sender of setupPackets
|
||||
|
||||
// set after each game tic if a usable line is in front of the player
|
||||
extern boolean autoUseActive;
|
||||
extern boolean respawnActive;
|
||||
|
||||
typedef enum {
|
||||
NF_NONE,
|
||||
NF_CONSISTANCY,
|
||||
NF_INTERRUPTED
|
||||
} netFail_t;
|
||||
extern netFail_t netGameFailure; // set by asyncThread
|
||||
|
||||
typedef struct {
|
||||
int interfaceIndex; // we must use the right socket to send packets
|
||||
struct sockaddr address;
|
||||
int oneWayLatency; // will always have 30+ msec of jitter
|
||||
int lastPacketAsyncTic; // to easily tell if it just arrived
|
||||
int lastPacketTime; // local milliseconds of last receive
|
||||
int lastTimeDelta; // packet milliseconds - local milliseconds
|
||||
int lowestTimeDelta; // min'd with lastTimeDelta each arrival
|
||||
int currentPingTics; // packetSequence - last packetAcknowledge
|
||||
} netPeer_t;
|
||||
|
||||
typedef struct {
|
||||
netPeer_t peer;
|
||||
packetClient_t pc; // most recent packet received
|
||||
|
||||
// TODO: There would be some benefit to ensuring that no edge transitions on
|
||||
// buttons are missed due to clock/net jitter.
|
||||
} netPlayer_t;
|
||||
|
||||
// all received packets, whether bluetooth or WiFi, go through here
|
||||
void iphoneProcessPacket( const struct sockaddr *from, const void *data, int len );
|
||||
|
||||
extern netPeer_t netServer;
|
||||
extern netPlayer_t netPlayers[MAXPLAYERS];
|
||||
extern sem_t * ticSemaphore;
|
||||
|
||||
typedef struct {
|
||||
int numGameTics;
|
||||
int numPingTics;
|
||||
int enterFrame;
|
||||
int afterSleep;
|
||||
int beforeSwap;
|
||||
int afterSwap;
|
||||
} logTime_t;
|
||||
#define MAX_LOGGED_TIMES 512
|
||||
extern logTime_t loggedTimes[MAX_LOGGED_TIMES]; // indexed by iphoneFrameNum
|
||||
|
||||
void LoadWallTexture( int wallPicNum );
|
||||
|
||||
float StringFontWidth( const char *str );
|
||||
int TouchDown( int x, int y, int w, int h );
|
||||
int TouchReleased( int x, int y, int w, int h );
|
||||
int TouchPressed( int x, int y, int w, int h );
|
||||
|
||||
// y is the baseline for font drawing
|
||||
float iphoneDrawText( float x, float y, float scale, const char *str );
|
||||
float iphoneCenterText( float x, float y, float scale, const char *str );
|
||||
|
||||
void StartGame();
|
||||
void iphoneOpenAutomap();
|
||||
void iphoneDrawNotifyText();
|
||||
void iphoneSet2D( void );
|
||||
|
||||
void R_Draw_Fill( int x, int y, int w, int h, color3_t c );
|
||||
void R_Draw_Blend( int x, int y, int w, int h, color4_t c );
|
||||
|
||||
void InitImmediateModeGL();
|
||||
int iphoneRotateForLandscape();
|
||||
void iphoneCheckForLandscapeReverse();
|
||||
|
||||
void iphonePacifierUpdate();
|
||||
void iphoneDrawScreen();
|
||||
|
||||
extern int damageflash;
|
||||
extern int bonusFrameNum;
|
||||
extern int attackDirTime[2];
|
||||
|
||||
#define BF_IGNORE 1 // don't draw or process touches
|
||||
#define BF_INACTIVE 2 // draw, but no touch processing at all
|
||||
#define BF_GLOW 4 // animated overbright glow
|
||||
#define BF_DIMMED 8 // draw darker, but still selectable
|
||||
#define BF_CENTERTEXT 16 // text in middle of button, not underneath
|
||||
#define BF_TRANSPARENT 32 // blend translucent
|
||||
#define BF_HUDBUTTON 64 // don't process in UpdateHudTouch
|
||||
#define BF_DRAW_ACTIVE 128 // for fire button
|
||||
#define BF_SMALL_CLICK 256 // for fire button
|
||||
|
||||
typedef struct {
|
||||
int x, y;
|
||||
int drawWidth, drawHeight;
|
||||
pkTexture_t *texture;
|
||||
|
||||
const char *title;
|
||||
struct touch_s *touch;
|
||||
float scale; // ramps up and down after touches
|
||||
int frameNum; // reset scale if not checked on previous frame
|
||||
int buttonFlags;
|
||||
boolean twoFingerPress; // if a second finger came down before a release for timedemo / etc
|
||||
boolean pressed; // true when a touch goes down in it
|
||||
|
||||
// stuff for hud controls
|
||||
boolean drawAsLimit; // color tint when further movement won't do anything
|
||||
float touchState; // rotor angle
|
||||
float drawState; // offsets for rotors
|
||||
int downX, downY; // initial touch went down here
|
||||
} ibutton_t;
|
||||
|
||||
typedef struct {
|
||||
ibutton_t forwardStick;
|
||||
ibutton_t sideStick;
|
||||
ibutton_t turnStick;
|
||||
ibutton_t turnRotor;
|
||||
ibutton_t fire;
|
||||
ibutton_t menu;
|
||||
ibutton_t map;
|
||||
ibutton_t weaponSelect;
|
||||
} hud_t;
|
||||
|
||||
extern hud_t huds;
|
||||
|
||||
void HudSetForScheme( int schemeNum );
|
||||
void HudSetTexnums();
|
||||
void HudEditFrame();
|
||||
|
||||
boolean StartNetGame();
|
||||
|
||||
int BackButton();
|
||||
void ResumeGame();
|
||||
|
||||
//---------------------------------------
|
||||
// Touch and button
|
||||
//---------------------------------------
|
||||
|
||||
typedef struct touch_s {
|
||||
boolean down;
|
||||
int x, y;
|
||||
// int prevX, prevY; // will be set to x, y on first touch, copied after each game frame
|
||||
int stateCount; // set to 1 on first event that state changes, incremented each game frame (-1 is a special tapped-and-released code)
|
||||
void *controlOwner;
|
||||
} touch_t;
|
||||
|
||||
#define MAX_TOUCHES 5
|
||||
extern touch_t sysTouches[MAX_TOUCHES];
|
||||
extern touch_t gameTouches[MAX_TOUCHES];
|
||||
extern pthread_mutex_t eventMutex; // used to sync between game and event threads
|
||||
|
||||
touch_t *TouchInBounds( int x, int y, int w, int h );
|
||||
touch_t *AnyTouchInBounds( int x, int y, int w, int h );
|
||||
touch_t *UpdateHudTouch( ibutton_t *hud );
|
||||
|
||||
bool NewTextButton( ibutton_t *b, const char *title, int x, int y, int w, int h );
|
||||
void SetButtonPics( ibutton_t *button, const char *picName, const char *title, int x, int y );
|
||||
void SetButtonPicsAndSizes( ibutton_t *button, const char *picBase, const char *title, int x, int y, int w, int h );
|
||||
boolean HandleButton( ibutton_t *button );
|
||||
|
||||
//---------------------------------------
|
||||
// Doom stuff we use directly
|
||||
//---------------------------------------
|
||||
void G_DoSaveGame (boolean menu);
|
||||
extern short consistancy[MAXPLAYERS][BACKUPTICS];
|
||||
extern boolean levelTimer;
|
||||
extern int levelTimeCount;
|
||||
extern boolean levelFragLimit;
|
||||
extern int levelFragLimitCount;
|
||||
|
||||
//---------------------------------------
|
||||
// iphone_sound.c
|
||||
//---------------------------------------
|
||||
|
||||
void Sound_Init( void );
|
||||
void Sound_StartLocalSound( const char *sound );
|
||||
void Sound_StartLocalSoundAtVolume( const char *sound, float volume );
|
||||
|
||||
void ShowSound();
|
||||
|
||||
//---------------------------------------
|
||||
// iphone_net.c
|
||||
//---------------------------------------
|
||||
|
||||
// dump all the interfaces and ip addresses for debugging
|
||||
void ReportNetworkInterfaces();
|
||||
|
||||
// open a UDP socket, pass "en0" for wifi
|
||||
int UDPSocket( const char *interfaceName, int portnum );
|
||||
|
||||
// return false if the multiplayer button should be disabled
|
||||
boolean NetworkAvailable();
|
||||
|
||||
// this can be called every frame in the menu to highlight
|
||||
// the multiplayer icon when a server is already up
|
||||
boolean NetworkServerAvailable();
|
||||
|
||||
// returns "WiFi", "BlueTooth", or "" for display on the
|
||||
// main menu multiplayer icon
|
||||
const char *NetworkServerTransport();
|
||||
|
||||
// this queries DNS for the actual address
|
||||
boolean ResolveNetworkServer( struct sockaddr *addr );
|
||||
|
||||
// If we are starting a server instead of joining one, make
|
||||
// us available as a bonjour service until we start the game
|
||||
// or back out of the multiplayer menu. Returns false if
|
||||
// someone else grabbed it just before we could.
|
||||
boolean RegisterGameService();
|
||||
void TerminateGameService();
|
||||
|
||||
// called by AsyncTic() to check for server state changes,
|
||||
// registers for service browsing on first call.
|
||||
void ProcessDNSMessages();
|
||||
|
||||
// draw a graph of packets sent and received
|
||||
void ShowNet();
|
||||
void ShowMiniNet();
|
||||
|
||||
//---------------------------------------
|
||||
// iphone_mapSelect.c
|
||||
//---------------------------------------
|
||||
|
||||
// returns false if nothing was selected
|
||||
// if map->map is -1, the back button was hit instead of choosing a level
|
||||
boolean iphoneMapSelectMenu( mapStart_t *map );
|
||||
|
||||
mapStats_t *FindMapStats( int dataset, int episode, int map, boolean create );
|
||||
const char *FindMapName( int dataset, int episode, int map );
|
||||
|
||||
//---------------------------------------
|
||||
// iphone_start.c
|
||||
//
|
||||
// game harness routines
|
||||
//---------------------------------------
|
||||
void ResumeGame();
|
||||
boolean StartNetGame();
|
||||
void StartSaveGame();
|
||||
void StartSinglePlayerGame( mapStart_t map );
|
||||
void StartDemoGame( boolean timeDemoMode );
|
||||
|
||||
//---------------------------------------
|
||||
// interfaces from the original game code
|
||||
//---------------------------------------
|
||||
void iphoneSetNotifyText( const char *str, ... );
|
||||
void iphoneIntermission( wbstartstruct_t* wbstartstruct );
|
||||
void iphoneStartLevel();
|
||||
void iphoneStartMusic();
|
||||
void iphoneStopMusic();
|
||||
void iphonePlayMusic( const char *name );
|
||||
void iphonePauseMusic();
|
||||
void iphoneResumeMusic();
|
||||
|
||||
//---------------------------------------
|
||||
// interfaces to Objective-C land
|
||||
//---------------------------------------
|
||||
|
||||
// The event thread will fill this after hitting enter
|
||||
// on the console. The game thread should check it,
|
||||
// execute it, and clear it under mutex.
|
||||
extern char consoleCommand[1024];
|
||||
|
||||
void SysIPhoneSwapBuffers();
|
||||
void SysIPhoneVibrate();
|
||||
void SysIPhoneOpenURL( const char *url );
|
||||
void SysIPhoneSetUIKitOrientation( int isLandscapeRight );
|
||||
const char * SysIPhoneGetConsoleTextField();
|
||||
void SysIPhoneSetConsoleTextField(const char *);
|
||||
void SysIPhoneInitAudioSession();
|
||||
int SysIPhoneOtherAudioIsPlaying();
|
||||
int SysIphoneMilliseconds();
|
||||
int SysIphoneMicroseconds();
|
||||
const char * SysIphoneGetAppDir();
|
||||
const char * SysIphoneGetDocDir();
|
||||
|
||||
//---------------------------------------
|
||||
// interfaces from Objective-C land
|
||||
//---------------------------------------
|
||||
void iphoneStartup();
|
||||
void iphoneShutdown();
|
||||
void iphoneFrame();
|
||||
void iphoneAsyncTic();
|
||||
void iphoneTiltEvent( float *tilts );
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* iphone_email.h
|
||||
* Doom
|
||||
*
|
||||
* Created by Greg Hodges on 10/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.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
|
||||
//Appends the console buffer while replacings non-URLscheme compatible text
|
||||
void AppendConsoleBuffer(const char *buf);
|
||||
|
||||
//Emails the console buffer to id software
|
||||
void EmailConsole();
|
||||
@@ -0,0 +1,235 @@
|
||||
/*
|
||||
* iphone_email.c
|
||||
* Doom
|
||||
*
|
||||
* Created by Greg Hodges on 10/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.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#include "iphone_email.h"
|
||||
#include <time.h>
|
||||
|
||||
|
||||
char *consoleBuffer = NULL; //buffer for the console output
|
||||
int size = 0;
|
||||
|
||||
/*
|
||||
* ReplaceAt()
|
||||
* Replaces the char at location with the insertString
|
||||
*/
|
||||
void ReplaceAt( char *oldString, const char *insertString, int location)
|
||||
{
|
||||
int length = strlen(oldString);
|
||||
int chunkLength = strlen(insertString);
|
||||
|
||||
char *newString = malloc(length + chunkLength + 1);//the 1 includes space for the null terminating character
|
||||
|
||||
#if 0
|
||||
//strcpy(newString, old);
|
||||
strncpy(newString, oldString, location);
|
||||
newString[location] = '\0';
|
||||
strcat(newString, insertString);
|
||||
strcpy(&newString[location+chunkLength], &oldString[location + 1]);
|
||||
|
||||
free(oldString);
|
||||
oldString = newString;
|
||||
#endif
|
||||
|
||||
//copy the front part
|
||||
char frontPart[location+1];
|
||||
strncpy(frontPart, oldString, location);
|
||||
frontPart[location] = '\0';
|
||||
printf("\nfrontPart: %s", frontPart);
|
||||
|
||||
//copy the back part
|
||||
char backPart[length - location];
|
||||
strcpy(backPart, &oldString[location+1]);
|
||||
backPart[length - location - 1] = '\0';
|
||||
printf("\nbackPart: %s\n\n", backPart);
|
||||
|
||||
//put it all together in the new string
|
||||
newString[0] = '\0';
|
||||
strcat(newString, frontPart);
|
||||
strcat(newString, insertString);
|
||||
strcat(newString, backPart);
|
||||
|
||||
//delete old string
|
||||
free(oldString);
|
||||
|
||||
//replace old string
|
||||
oldString = newString;
|
||||
}
|
||||
|
||||
/*
|
||||
* AppendBuffer()
|
||||
* Directly appends the console buffer
|
||||
*/
|
||||
void AppendBuffer(const char *buf)
|
||||
{
|
||||
int length = strlen(buf) + 1; //strlen doesn't include the null terminating character
|
||||
char *temp = malloc(length);
|
||||
strcpy(temp, buf);
|
||||
|
||||
|
||||
for (int i = 0; i < length; ++i)
|
||||
{
|
||||
if (temp[i] == ' ' || temp[i] == '\n' || temp[i] == '=' )
|
||||
temp[i] = '_';
|
||||
}
|
||||
|
||||
#if 0
|
||||
int i = 0;
|
||||
while (temp[i] != '\0')
|
||||
{
|
||||
if (temp[i] == ' ')
|
||||
ReplaceAt(temp, "_testString_", i);
|
||||
|
||||
++i;
|
||||
}
|
||||
length = strlen(temp) + 1;
|
||||
#endif
|
||||
|
||||
//copy the old & new string into a buffer
|
||||
char *newBuf = malloc(size + length);
|
||||
if (consoleBuffer)
|
||||
{
|
||||
strcpy(newBuf, consoleBuffer);
|
||||
}
|
||||
strcpy(&newBuf[size], temp);
|
||||
|
||||
|
||||
//delete the old string and have it point to the new one
|
||||
free(consoleBuffer);
|
||||
consoleBuffer = newBuf;
|
||||
size = strlen(consoleBuffer);
|
||||
|
||||
|
||||
//delete the temp string
|
||||
free(temp);
|
||||
}
|
||||
|
||||
/*
|
||||
* AppendChunk()
|
||||
* Just append a chunk of the incoming string from start to i
|
||||
*/
|
||||
void AppendChunk(const char *buf, int start, int i)
|
||||
{
|
||||
int size = i+1 - start;
|
||||
char chunk[size];
|
||||
chunk[0] = '\0';
|
||||
strncpy(chunk, &buf[start], size-1);
|
||||
chunk[size-1] = '\0';
|
||||
AppendBuffer(chunk);
|
||||
}
|
||||
|
||||
/*
|
||||
* AppendConsoleBuffer()
|
||||
* Appends the console buffer while replacings non-URLscheme compatible text
|
||||
*/
|
||||
void AppendConsoleBuffer(const char *buf)
|
||||
{
|
||||
int length = strlen(buf) + 1; //strlen doesn't include the null terminating character
|
||||
char *temp = malloc(length);
|
||||
strcpy(temp, buf);
|
||||
|
||||
|
||||
int start = 0;
|
||||
int i = 0;
|
||||
for (i = 0; i < length; ++i)
|
||||
{
|
||||
//replace space and = with _
|
||||
//spaces and = tend to mess up the URL scheme
|
||||
if (temp[i] == ' ' )
|
||||
{
|
||||
temp[i] = '_';
|
||||
AppendChunk(temp, start, i); ++i; start = i;
|
||||
AppendBuffer("\%20"); //line space
|
||||
}
|
||||
|
||||
if (temp[i] == '=' )
|
||||
{
|
||||
temp[i] = '_';
|
||||
AppendChunk(temp, start, i); ++i; start = i;
|
||||
AppendBuffer("\%3D"); //=
|
||||
}
|
||||
|
||||
if (temp[i] == '\n')
|
||||
{
|
||||
temp[i] = '_';
|
||||
AppendChunk(temp, start, i); ++i; start = i;
|
||||
AppendBuffer("\%0A"); //line return
|
||||
}
|
||||
}
|
||||
|
||||
//append the last of the chunk
|
||||
i = length - 1;
|
||||
if (start < i)
|
||||
{
|
||||
AppendChunk(temp, start, i);
|
||||
}
|
||||
|
||||
// AppendString("\%0A"); //line return
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
*
|
||||
* Email the console buffer to id
|
||||
*
|
||||
*/
|
||||
void EmailConsole()
|
||||
{
|
||||
if (!consoleBuffer)
|
||||
return;
|
||||
|
||||
//copy everything from consoleBuffer into a char*
|
||||
char *buffer = malloc(1024+size+1);
|
||||
|
||||
#if 0
|
||||
time_t rawtime;
|
||||
struct tm * timeinfo;
|
||||
time ( &rawtime );
|
||||
timeinfo = gmtime ( &rawtime );
|
||||
char dateBuffer[128];
|
||||
sprintf(dateBuffer, asctime(timeinfo));
|
||||
for (int i = 0; i < strlen(dateBuffer); ++i)
|
||||
{
|
||||
if (dateBuffer[i] == ' ' || dateBuffer[i] == ':')
|
||||
dateBuffer[i] = '_';
|
||||
}
|
||||
printf("formatted time: %s\n", dateBuffer);
|
||||
|
||||
sprintf( buffer, "mailto:iphonesupport@idsoftware.com?subject=DoomClassicReport%s&body=%s", dateBuffer, consoleBuffer);
|
||||
#else
|
||||
sprintf( buffer, "mailto:iphonesupport@idsoftware.com?subject=DoomClassicReport&body=%s", consoleBuffer);
|
||||
#endif
|
||||
|
||||
//call the mail app
|
||||
NSURL *url = [[NSURL alloc] initWithString:[[NSString alloc] initWithCString:buffer]];
|
||||
[[UIApplication sharedApplication] openURL:url];
|
||||
|
||||
free(buffer);
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,522 @@
|
||||
/*
|
||||
|
||||
Copyright (C) 2004-2005 Michael Liebscher <johnnycanuck@users.sourceforge.net>
|
||||
Copyright (C) 1997-2001 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.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
|
||||
doom
|
||||
----
|
||||
modifications to config.h
|
||||
don't add the d_ipxgate.c file
|
||||
don't add d_server.c file
|
||||
don't add mmus2mid.[ch] files
|
||||
don't add w_memcache.c, use w_mmap.c instead
|
||||
added new SDL_opengl.h, changed code files from <SDL_opengl.h> to "SDL_opengl.h"
|
||||
commented out #include <SDL.h>
|
||||
|
||||
Commented out the static on D_DoomMainSetup();
|
||||
Add define HAVE_CONFIG_H to the target settings
|
||||
Add define _DEBUG
|
||||
|
||||
#if around uses of GL_TEXTURE_RESIDENT in gl_texture.c
|
||||
// JDC: not in GLES, not needed since it is the default condition glDisable(GL_POLYGON_SMOOTH);
|
||||
|
||||
// JDC #define USE_VERTEX_ARRAYS in gl_main.c
|
||||
|
||||
add the iphoneRotateForLandscape calls
|
||||
|
||||
static JDC removed short consistancy[MAXPLAYERS][BACKUPTICS];
|
||||
|
||||
// JDC glDisableClientState(GL_TEXTURE_COORD_ARRAY);
|
||||
// JDC glDisableClientState(GL_VERTEX_ARRAY);
|
||||
#ifdef IPHONE // JDC
|
||||
#define MAX_SCREENWIDTH 480
|
||||
#define MAX_SCREENHEIGHT 320
|
||||
#else
|
||||
|
||||
|
||||
code notes
|
||||
----------
|
||||
all the fixed point mess
|
||||
goofy polar clipping
|
||||
path traverse should have used a BSP tree
|
||||
|
||||
rename vldoor_e close and open enums to not conflict with unistd
|
||||
|
||||
opengl_error_break
|
||||
|
||||
|
||||
WiFi internet play
|
||||
---------
|
||||
voip instead of key chat
|
||||
|
||||
extra asset work
|
||||
----------------------
|
||||
perfect weapon scales
|
||||
demo of each level?
|
||||
additive art separation -- attacks, buttons, etc
|
||||
|
||||
tuned option
|
||||
------------
|
||||
last kill / last secret / last item messages
|
||||
more achievements in general
|
||||
better item pickup sounds
|
||||
scale blood spray with distance
|
||||
easier item pickup
|
||||
crosshair
|
||||
better movement clipping to walls
|
||||
better key pickup sound
|
||||
textured automap
|
||||
better low health warning
|
||||
better feedback on where bullets hit
|
||||
better bob cycle when going down stairs
|
||||
map item should also show all monsters and items to make it cool
|
||||
barrels explode easier
|
||||
wave FOV when berzerk
|
||||
pistol shots are useless in deathmatch
|
||||
show opponent / enemy health when attacking them?
|
||||
|
||||
|
||||
cpu optimizations
|
||||
-------------
|
||||
convert to 16 bit vertex data
|
||||
void gld_BindPatch(GLTexture *gltexture, int cm) is expensive
|
||||
fixedMul / fixedDiv in asm (negligable performance gain)
|
||||
atlas all of the non-character items (and bodies?)
|
||||
remove the pitch changes from sound?
|
||||
|
||||
gpu optimizations
|
||||
------------------
|
||||
use fog instead of full screen blend for damage/pickup flash (3 msec on IP3G)
|
||||
pvrtc walls and floors
|
||||
|
||||
art needed
|
||||
----------
|
||||
new map / menu button
|
||||
awards on medals
|
||||
icon
|
||||
thumbsticks
|
||||
settings gears
|
||||
|
||||
|
||||
must do
|
||||
-------
|
||||
pause music in menu
|
||||
timelimit carry over to new levels
|
||||
server going to demo causes client to crash
|
||||
|
||||
fixed 10/13/09
|
||||
--------------
|
||||
recover from sound interruption
|
||||
caps for slider bar text
|
||||
reset defaults doesn't reset reverse-landscape
|
||||
pause music when going to the menu
|
||||
reset timelimit each level on deathmatch
|
||||
disable demos, new game, and web site during multiplayer
|
||||
|
||||
fixed
|
||||
-----
|
||||
automap / menu button actions with multi-thread
|
||||
capitalized "game saved."
|
||||
Added punctuation to "Ned a blue keycard", etc
|
||||
added "defaults" button in options
|
||||
|
||||
todo
|
||||
----
|
||||
don't accept fire from an owned touch?
|
||||
text scaling in buttons isn't perfect
|
||||
better view angle transport
|
||||
audio bugs
|
||||
rotor control shouldn't be dimmed
|
||||
volume button hack?
|
||||
change background color for networking
|
||||
tapping weapon change to cycle
|
||||
is openal thread safe? we issue touch clicks from asynctic
|
||||
disable multiplayer button if no ethernet addresses found
|
||||
touch latching issues
|
||||
add one tic latency to server?
|
||||
select new game while in netgame
|
||||
multiplayer arrow colors
|
||||
remove players with stale joins
|
||||
rocket explosion offset
|
||||
texture preload status bar stuff and blood / impacts
|
||||
stereo panning for headphones
|
||||
directly build 16 bit textures instead of translating from 32 bit
|
||||
texture wrap seam after end of e3m8
|
||||
sliders should be touch-latch controls
|
||||
respawn flash sounds sometimes not playing?
|
||||
don't allow starting on secret levels
|
||||
loaded savegame spot on different level didn't get view height on first frame
|
||||
use graphic?
|
||||
don't ever close doors with auto-use
|
||||
separators in map select
|
||||
rotor speed adjust
|
||||
check all powerup effects
|
||||
remove uses of prboom.wad?
|
||||
check patch outlining code
|
||||
flash all controls on initial level load
|
||||
touch sounds when cancelling demo playback
|
||||
play sound on respawn
|
||||
catch memory warning, purge textures
|
||||
use wad sound data
|
||||
stop sound cleanly
|
||||
entire gun doesn't get fullbright with muzzle flash
|
||||
require four touches in line for console
|
||||
restartable pwad interface
|
||||
somewhat normal based lighting on walls
|
||||
help menu
|
||||
visual tilt indicator
|
||||
tilt draw the turnstick when active
|
||||
console
|
||||
|
||||
*/
|
||||
|
||||
#include "../doomiphone.h"
|
||||
|
||||
|
||||
cvar_t *skill;
|
||||
cvar_t *episode;
|
||||
cvar_t *controlScheme;
|
||||
cvar_t *stickTurn;
|
||||
cvar_t *stickMove;
|
||||
cvar_t *stickDeadBand;
|
||||
cvar_t *rotorTurn;
|
||||
cvar_t *tiltTurn;
|
||||
cvar_t *tiltMove;
|
||||
cvar_t *tiltDeadBand;
|
||||
cvar_t *tiltAverages;
|
||||
cvar_t *miniNet;
|
||||
cvar_t *music;
|
||||
cvar_t *showTilt;
|
||||
cvar_t *showTime;
|
||||
cvar_t *showNet;
|
||||
cvar_t *showSound;
|
||||
cvar_t *cropSprites;
|
||||
cvar_t *revLand;
|
||||
cvar_t *mapScale;
|
||||
cvar_t *drawControls;
|
||||
cvar_t *autoUse;
|
||||
cvar_t *statusBar;
|
||||
cvar_t *touchClick;
|
||||
cvar_t *messages;
|
||||
cvar_t *timeLimit;
|
||||
cvar_t *fragLimit;
|
||||
cvar_t *mpDeathmatch;
|
||||
cvar_t *mpSkill;
|
||||
cvar_t *mpDataset;
|
||||
cvar_t *mpEpisode;
|
||||
cvar_t *mpMap;
|
||||
cvar_t *noBlend;
|
||||
cvar_t *glfinish;
|
||||
cvar_t *mapSelectY;
|
||||
cvar_t *throttle;
|
||||
cvar_t *centerSticks;
|
||||
cvar_t *rampTurn;
|
||||
cvar_t *netBuffer;
|
||||
|
||||
#define VERSION_BCONFIG ( 0x89490000 + sizeof( huds ) + sizeof( playState ) )
|
||||
|
||||
void Sys_Error( const char *format, ... )
|
||||
{
|
||||
va_list argptr;
|
||||
char string[ 1024 ];
|
||||
|
||||
va_start( argptr, format );
|
||||
(void)vsnprintf( string, sizeof( string ), format, argptr );
|
||||
va_end( argptr );
|
||||
|
||||
fprintf( stderr, "Error: %s\n", string );
|
||||
|
||||
_exit( 1 );
|
||||
|
||||
}
|
||||
|
||||
#define plyr (players+consoleplayer) /* the console player */
|
||||
|
||||
void Give_f() {
|
||||
|
||||
plyr->armorpoints = idfa_armor; // Ty 03/09/98 - deh
|
||||
plyr->armortype = idfa_armor_class; // Ty 03/09/98 - deh
|
||||
|
||||
// You can't own weapons that aren't in the game // phares 02/27/98
|
||||
for (int i=0;i<NUMWEAPONS;i++)
|
||||
if (!(((i == wp_plasma || i == wp_bfg) && gamemode == shareware) ||
|
||||
(i == wp_supershotgun && gamemode != commercial)))
|
||||
plyr->weaponowned[i] = true;
|
||||
|
||||
for (int i=0;i<NUMAMMO;i++)
|
||||
if (i!=am_cell || gamemode!=shareware)
|
||||
plyr->ammo[i] = plyr->maxammo[i];
|
||||
|
||||
plyr->message = s_STSTR_FAADDED;
|
||||
|
||||
for (int i=0;i<NUMCARDS;i++)
|
||||
if (!plyr->cards[i]) // only print message if at least one key added
|
||||
{ // however, caller may overwrite message anyway
|
||||
plyr->cards[i] = true;
|
||||
}
|
||||
}
|
||||
|
||||
void God_f() {
|
||||
plyr->cheats ^= CF_GODMODE;
|
||||
if (plyr->cheats & CF_GODMODE)
|
||||
{
|
||||
if (plyr->mo)
|
||||
plyr->mo->health = god_health; // Ty 03/09/98 - deh
|
||||
|
||||
plyr->health = god_health;
|
||||
plyr->message = s_STSTR_DQDON; // Ty 03/27/98 - externalized
|
||||
}
|
||||
else
|
||||
plyr->message = s_STSTR_DQDOFF; // Ty 03/27/98 - externalized
|
||||
}
|
||||
|
||||
void ResetMaps_f() {
|
||||
playState.numMapStats = 0;
|
||||
memset( playState.mapStats, 0, sizeof( playState.mapStats ) );
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
iphoneStartup
|
||||
|
||||
==================
|
||||
*/
|
||||
struct addrinfo *addrinfoHead;
|
||||
|
||||
void D_DoomMainSetup();
|
||||
void iphoneStartup() {
|
||||
int start = SysIphoneMilliseconds();
|
||||
|
||||
// print networking information
|
||||
ReportNetworkInterfaces();
|
||||
|
||||
// microseconds will be plenty random for playerID and localGameID
|
||||
playerID = localGameID = SysIphoneMicroseconds();
|
||||
|
||||
InitImmediateModeGL();
|
||||
|
||||
// init OpenAL before pak file, so the pak file can
|
||||
// make all the al static buffers
|
||||
Sound_Init();
|
||||
|
||||
// get our new-style pak file
|
||||
char pakFile[1024];
|
||||
sprintf( pakFile, "%s/base/base.iPack", SysIphoneGetAppDir() );
|
||||
PK_Init( pakFile );
|
||||
|
||||
// register console commands
|
||||
Cmd_AddCommand( "listcvars", Cvar_List_f );
|
||||
Cmd_AddCommand( "resetcvars", Cvar_Reset_f );
|
||||
Cmd_AddCommand( "resetmaps", ResetMaps_f );
|
||||
Cmd_AddCommand( "listcmds", Cmd_ListCommands_f );
|
||||
Cmd_AddCommand( "give", Give_f );
|
||||
Cmd_AddCommand( "god", God_f );
|
||||
Cmd_AddCommand( "mail", EmailConsole ); //gsh, mails the console to id
|
||||
|
||||
// register console variables
|
||||
Cvar_Get( "version", va( "%3.1f %s %s", DOOM_IPHONE_VERSION, __DATE__, __TIME__ ), 0 );
|
||||
|
||||
skill = Cvar_Get( "skill", "1", CVAR_ARCHIVE );
|
||||
episode = Cvar_Get( "episode", "0", CVAR_ARCHIVE );
|
||||
|
||||
controlScheme = Cvar_Get( "controlScheme", "0", CVAR_ARCHIVE );
|
||||
stickTurn = Cvar_Get( "stickTurn", "128", CVAR_ARCHIVE );
|
||||
stickMove = Cvar_Get( "stickMove", "128", CVAR_ARCHIVE );
|
||||
stickDeadBand = Cvar_Get( "stickDeadBand", "0.2", CVAR_ARCHIVE );
|
||||
rotorTurn = Cvar_Get( "rotorTurn", "50000", CVAR_ARCHIVE );
|
||||
tiltTurn = Cvar_Get( "tiltTurn", "0", CVAR_ARCHIVE );
|
||||
tiltMove = Cvar_Get( "tiltMove", "0", CVAR_ARCHIVE );
|
||||
tiltDeadBand = Cvar_Get( "tiltDeadBand", "0.08", CVAR_ARCHIVE );
|
||||
tiltAverages = Cvar_Get( "tiltAverages", "3", CVAR_ARCHIVE );
|
||||
centerSticks = Cvar_Get( "centerSticks", "1", CVAR_ARCHIVE );
|
||||
rampTurn = Cvar_Get( "rampTurn", "1", CVAR_ARCHIVE );
|
||||
|
||||
music = Cvar_Get( "music", "1", CVAR_ARCHIVE );
|
||||
cropSprites = Cvar_Get( "cropSprites", "1", 0 );
|
||||
revLand = Cvar_Get( "revLand", "0", CVAR_ARCHIVE );
|
||||
mapScale = Cvar_Get( "mapScale", "10", CVAR_ARCHIVE );
|
||||
drawControls = Cvar_Get( "drawControls", "1", CVAR_ARCHIVE );
|
||||
autoUse = Cvar_Get( "autoUse", "0", CVAR_ARCHIVE );
|
||||
statusBar = Cvar_Get( "statusBar", "1", CVAR_ARCHIVE );
|
||||
touchClick = Cvar_Get( "touchClick", "0.15", CVAR_ARCHIVE );
|
||||
messages = Cvar_Get( "messages", "1", CVAR_ARCHIVE );
|
||||
mapSelectY = Cvar_Get( "mapSelectY", "0", CVAR_ARCHIVE );
|
||||
miniNet = Cvar_Get( "miniNet", "1", CVAR_ARCHIVE );
|
||||
|
||||
// multiplayer setup
|
||||
timeLimit = Cvar_Get( "timeLimit", "0", CVAR_ARCHIVE );
|
||||
fragLimit = Cvar_Get( "fragLimit", "5", CVAR_ARCHIVE );
|
||||
mpDeathmatch = Cvar_Get( "mpDeathmatch", "0", CVAR_ARCHIVE );
|
||||
mpDataset = Cvar_Get( "mpDataset", "0", CVAR_ARCHIVE );
|
||||
mpEpisode = Cvar_Get( "mpEpisode", "1", CVAR_ARCHIVE );
|
||||
mpSkill = Cvar_Get( "mpSkill", "1", CVAR_ARCHIVE );
|
||||
mpMap = Cvar_Get( "mpMap", "1", CVAR_ARCHIVE );
|
||||
|
||||
// debug tools
|
||||
showTilt = Cvar_Get( "showTilt", "-1", 0 );
|
||||
showTime = Cvar_Get( "showTime", "0", 0 );
|
||||
showNet = Cvar_Get( "showNet", "0", 0 );
|
||||
showSound = Cvar_Get( "showSound", "0", 0 );
|
||||
noBlend = Cvar_Get( "noBlend", "0", 0 ); // disable the damae blends for screenshots
|
||||
glfinish = Cvar_Get( "glfinish", "0", 0 );
|
||||
throttle = Cvar_Get( "throttle", "1", 0 ); // network packet throttle enable
|
||||
netBuffer = Cvar_Get( "netBuffer", "4", 0 ); // max tics to buffer ahead
|
||||
|
||||
// load the archived cvars
|
||||
Cmd_ExecuteFile( va( "%s/config.cfg", SysIphoneGetDocDir() ) );
|
||||
|
||||
// make sure volume changes and incoming calls draw the right orientation
|
||||
SysIPhoneSetUIKitOrientation( revLand->value );
|
||||
|
||||
// start the intro music if it wasn't disabled with the music cvar
|
||||
iphonePlayMusic( "intro" );
|
||||
// iphonePlayMusic( "e1m1" );
|
||||
|
||||
// these should get overwritten by the config loading
|
||||
memset( &playState, 0, sizeof( playState ) );
|
||||
playState.map.skill = 1;
|
||||
playState.map.episode = 1;
|
||||
playState.map.map = 1;
|
||||
HudSetForScheme( 0 );
|
||||
|
||||
// load the binary config file
|
||||
FILE *f = fopen( va( "%s/binaryConfig.bin", SysIphoneGetDocDir() ), "rb" );
|
||||
if ( f ) {
|
||||
int version;
|
||||
|
||||
version = 0;
|
||||
fread( &version, 1, sizeof( version ), f );
|
||||
if ( version != VERSION_BCONFIG ) {
|
||||
Com_Printf( "Binary config file bad version.\n" );
|
||||
} else {
|
||||
fread( &playState, 1, sizeof( playState ), f );
|
||||
fread( &huds, 1, sizeof( huds ), f );
|
||||
|
||||
version = 0;
|
||||
fread( &version, 1, sizeof( version ), f );
|
||||
if ( version != VERSION_BCONFIG ) {
|
||||
Com_Error( "Binary config file bad trailing version.\n" );
|
||||
}
|
||||
}
|
||||
fclose( f );
|
||||
}
|
||||
|
||||
|
||||
Com_Printf( "startup time: %i msec\n", SysIphoneMilliseconds() - start );
|
||||
|
||||
start = SysIphoneMilliseconds();
|
||||
|
||||
// the texnums might have been different in the savegame
|
||||
HudSetTexnums();
|
||||
|
||||
arialFontTexture = PK_FindTexture( "iphone/arialImageLAL.tga" );
|
||||
|
||||
Com_Printf( "preloadBeforePlay(): %i msec\n", SysIphoneMilliseconds() - start );
|
||||
|
||||
Com_Printf( "---------- D_DoomMain ----------\n" );
|
||||
D_DoomMainSetup();
|
||||
|
||||
// put savegames here
|
||||
strcpy( basesavegame, SysIphoneGetDocDir() );
|
||||
|
||||
// prBoom seems to draw the static pic screens without setting up 2D, causing
|
||||
// a bad first frame
|
||||
iphoneSet2D();
|
||||
|
||||
menuState = IPM_MAIN;
|
||||
|
||||
#if 0
|
||||
// jump right to the save spot for debugging
|
||||
ResumeGame();
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
iphoneShutdown
|
||||
|
||||
Write out configs and save the game at this position
|
||||
==================
|
||||
*/
|
||||
void iphoneShutdown() {
|
||||
FILE *fp;
|
||||
char path[1024];
|
||||
cvar_t *var;
|
||||
char buffer[1024];
|
||||
|
||||
// write the ascii config file
|
||||
snprintf( path, sizeof( path ), "%s/config.cfg", SysIphoneGetDocDir() );
|
||||
fp = fopen( path, "w" );
|
||||
if( ! fp ) {
|
||||
Com_Printf( "Could not write config.cfg.\n" );
|
||||
return;
|
||||
}
|
||||
|
||||
// write out commands to set the archived cvars
|
||||
for( var = cvar_vars ; var ; var = var->next ) {
|
||||
if( var->flags & CVAR_ARCHIVE ) {
|
||||
snprintf( buffer, sizeof( buffer ), "%s %s\n", var->name, var->string );
|
||||
fprintf( fp, "%s", buffer );
|
||||
Com_Printf( "%s", buffer );
|
||||
}
|
||||
}
|
||||
|
||||
fclose( fp );
|
||||
|
||||
|
||||
// write the binary config file
|
||||
FILE *f = fopen( va( "%s/binaryConfig.bin", SysIphoneGetDocDir() ), "wb" );
|
||||
if ( !f ) {
|
||||
Com_Printf( "Could not write binaryConfig.cfg.\n" );
|
||||
return;
|
||||
}
|
||||
|
||||
int version = VERSION_BCONFIG;
|
||||
|
||||
fwrite( &version, 1, sizeof( version ), f );
|
||||
|
||||
fwrite( &playState, 1, sizeof( playState ), f );
|
||||
fwrite( &huds, 1, sizeof( huds ), f );
|
||||
|
||||
fwrite( &version, 1, sizeof( version ), f );
|
||||
fclose( f );
|
||||
|
||||
// write the Doom savegame, unless no game level
|
||||
// was actually started on this app invokation, or we
|
||||
// are at an intermission / finale, or we are dead
|
||||
if ( levelHasBeenLoaded && !netgame && gamestate == GS_LEVEL
|
||||
&& players[consoleplayer].playerstate != PST_DEAD ) {
|
||||
// let the game thread perform a savegame, since it
|
||||
// would be unsafe to do it in this thread
|
||||
saveOnExitState = 1;
|
||||
while( saveOnExitState != 2 ) {
|
||||
sem_post( ticSemaphore );
|
||||
usleep( 10000 );
|
||||
}
|
||||
}
|
||||
|
||||
// not sure if we should do this, or let UIKit exit...
|
||||
exit( 0 );
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,443 @@
|
||||
/*
|
||||
* iphone_mapSelect.c
|
||||
* doom
|
||||
*
|
||||
* Created by John Carmack on 4/19/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.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#include "../doomiphone.h"
|
||||
|
||||
typedef struct {
|
||||
int dataset;
|
||||
int episode;
|
||||
int map;
|
||||
const char *name;
|
||||
} mapData_t;
|
||||
|
||||
mapData_t mapData[] = {
|
||||
{ 0, 1, 1, "E1M1: Hangar" },
|
||||
{ 0, 1, 2, "E1M2: Nuclear Plant" },
|
||||
{ 0, 1, 3, "E1M3: Toxin Refinery" },
|
||||
{ 0, 1, 4, "E1M4: Command Control" },
|
||||
{ 0, 1, 5, "E1M5: Phobos Lab" },
|
||||
{ 0, 1, 6, "E1M6: Central Processing" },
|
||||
{ 0, 1, 7, "E1M7: Computer Station" },
|
||||
{ 0, 1, 8, "E1M8: Phobos Anomaly" },
|
||||
{ 0, 1, 9, "E1M9: Military Base" },
|
||||
|
||||
{ 0, 2, 1, "E2M1: Deimos Anomaly" },
|
||||
{ 0, 2, 2, "E2M2: Containment Area" },
|
||||
{ 0, 2, 3, "E2M3: Refinery" },
|
||||
{ 0, 2, 4, "E2M4: Deimos Lab" },
|
||||
{ 0, 2, 5, "E2M5: Command Center" },
|
||||
{ 0, 2, 6, "E2M6: Halls of the Damned" },
|
||||
{ 0, 2, 7, "E2M7: Spawning Vats" },
|
||||
{ 0, 2, 8, "E2M8: Tower of Babel" },
|
||||
{ 0, 2, 9, "E2M9: Fortress of Mystery" },
|
||||
|
||||
{ 0, 3, 1, "E3M1: Hell Keep" },
|
||||
{ 0, 3, 2, "E3M2: Slough of Despair" },
|
||||
{ 0, 3, 3, "E3M3: Pandemonium" },
|
||||
{ 0, 3, 4, "E3M4: House of Pain" },
|
||||
{ 0, 3, 5, "E3M5: Unholy Cathedral" },
|
||||
{ 0, 3, 6, "E3M6: Mt. Erebus" },
|
||||
{ 0, 3, 7, "E3M7: Limbo" },
|
||||
{ 0, 3, 8, "E3M8: Dis" },
|
||||
{ 0, 3, 9, "E3M9: Warrens" },
|
||||
|
||||
{ 0, 4, 1, "E4M1: Hell Beneath" },
|
||||
{ 0, 4, 2, "E4M2: Perfect Hatred" },
|
||||
{ 0, 4, 3, "E4M3: Sever The Wicked" },
|
||||
{ 0, 4, 4, "E4M4: Unruly Evil" },
|
||||
{ 0, 4, 5, "E4M5: They Will Repent" },
|
||||
{ 0, 4, 6, "E4M6: Against Thee Wickedly" },
|
||||
{ 0, 4, 7, "E4M7: And Hell Followed" },
|
||||
{ 0, 4, 8, "E4M8: Unto The Cruel" },
|
||||
{ 0, 4, 9, "E4M9: Fear" },
|
||||
|
||||
#if 0
|
||||
|
||||
{ 0, 0, 0, "level 1: entryway" },
|
||||
{ 0, 0, 0, "level 2: underhalls" },
|
||||
{ 0, 0, 0, "level 3: the gantlet" },
|
||||
{ 0, 0, 0, "level 4: the focus" },
|
||||
{ 0, 0, 0, "level 5: the waste tunnels" },
|
||||
{ 0, 0, 0, "level 6: the crusher" },
|
||||
{ 0, 0, 0, "level 7: dead simple" },
|
||||
{ 0, 0, 0, "level 8: tricks and traps" },
|
||||
{ 0, 0, 0, "level 9: the pit" },
|
||||
{ 0, 0, 0, "level 10: refueling base" },
|
||||
{ 0, 0, 0, "level 11: 'o' of destruction!" },
|
||||
{ 0, 0, 0, "level 12: the factory" },
|
||||
{ 0, 0, 0, "level 13: downtown" },
|
||||
{ 0, 0, 0, "level 14: the inmost dens" },
|
||||
{ 0, 0, 0, "level 15: industrial zone" },
|
||||
{ 0, 0, 0, "level 16: suburbs" },
|
||||
{ 0, 0, 0, "level 17: tenements" },
|
||||
{ 0, 0, 0, "level 18: the courtyard" },
|
||||
{ 0, 0, 0, "level 19: the citadel" },
|
||||
{ 0, 0, 0, "level 20: gotcha!" },
|
||||
{ 0, 0, 0, "level 21: nirvana" },
|
||||
{ 0, 0, 0, "level 22: the catacombs" },
|
||||
{ 0, 0, 0, "level 23: barrels o' fun" },
|
||||
{ 0, 0, 0, "level 24: the chasm" },
|
||||
{ 0, 0, 0, "level 25: bloodfalls" },
|
||||
{ 0, 0, 0, "level 26: the abandoned mines" },
|
||||
{ 0, 0, 0, "level 27: monster condo" },
|
||||
{ 0, 0, 0, "level 28: the spirit world" },
|
||||
{ 0, 0, 0, "level 29: the living end" },
|
||||
{ 0, 0, 0, "level 30: icon of sin" },
|
||||
{ 0, 0, 0, "level 31: wolfenstein" },
|
||||
{ 0, 0, 0, "level 32: grosse" },
|
||||
|
||||
{ 0, 0, 0, "level 1: congo" },
|
||||
{ 0, 0, 0, "level 2: well of souls" },
|
||||
{ 0, 0, 0, "level 3: aztec" },
|
||||
{ 0, 0, 0, "level 4: caged" },
|
||||
{ 0, 0, 0, "level 5: ghost town" },
|
||||
{ 0, 0, 0, "level 6: baron's lair" },
|
||||
{ 0, 0, 0, "level 7: caughtyard" },
|
||||
{ 0, 0, 0, "level 8: realm" },
|
||||
{ 0, 0, 0, "level 9: abattoire" },
|
||||
{ 0, 0, 0, "level 10: onslaught" },
|
||||
{ 0, 0, 0, "level 11: hunted" },
|
||||
{ 0, 0, 0, "level 12: speed" },
|
||||
{ 0, 0, 0, "level 13: the crypt" },
|
||||
{ 0, 0, 0, "level 14: genesis" },
|
||||
{ 0, 0, 0, "level 15: the twilight" },
|
||||
{ 0, 0, 0, "level 16: the omen" },
|
||||
{ 0, 0, 0, "level 17: compound" },
|
||||
{ 0, 0, 0, "level 18: neurosphere" },
|
||||
{ 0, 0, 0, "level 19: nme" },
|
||||
{ 0, 0, 0, "level 20: the death domain" },
|
||||
{ 0, 0, 0, "level 21: slayer" },
|
||||
{ 0, 0, 0, "level 22: impossible mission" },
|
||||
{ 0, 0, 0, "level 23: tombstone" },
|
||||
{ 0, 0, 0, "level 24: the final frontier" },
|
||||
{ 0, 0, 0, "level 25: the temple of darkness" },
|
||||
{ 0, 0, 0, "level 26: bunker" },
|
||||
{ 0, 0, 0, "level 27: anti-christ" },
|
||||
{ 0, 0, 0, "level 28: the sewers" },
|
||||
{ 0, 0, 0, "level 29: odyssey of noises" },
|
||||
{ 0, 0, 0, "level 30: the gateway of hell" },
|
||||
{ 0, 0, 0, "level 31: cyberden" },
|
||||
{ 0, 0, 0, "level 32: go 2 it" },
|
||||
|
||||
{ 0, 0, 0, "level 1: system control" },
|
||||
{ 0, 0, 0, "level 2: human bbq" },
|
||||
{ 0, 0, 0, "level 3: power control" },
|
||||
{ 0, 0, 0, "level 4: wormhole" },
|
||||
{ 0, 0, 0, "level 5: hanger" },
|
||||
{ 0, 0, 0, "level 6: open season" },
|
||||
{ 0, 0, 0, "level 7: prison" },
|
||||
{ 0, 0, 0, "level 8: metal" },
|
||||
{ 0, 0, 0, "level 9: stronghold" },
|
||||
{ 0, 0, 0, "level 10: redemption" },
|
||||
{ 0, 0, 0, "level 11: storage facility" },
|
||||
{ 0, 0, 0, "level 12: crater" },
|
||||
{ 0, 0, 0, "level 13: nukage processing" },
|
||||
{ 0, 0, 0, "level 14: steel works" },
|
||||
{ 0, 0, 0, "level 15: dead zone" },
|
||||
{ 0, 0, 0, "level 16: deepest reaches" },
|
||||
{ 0, 0, 0, "level 17: processing area" },
|
||||
{ 0, 0, 0, "level 18: mill" },
|
||||
{ 0, 0, 0, "level 19: shipping/respawning" },
|
||||
{ 0, 0, 0, "level 20: central processing" },
|
||||
{ 0, 0, 0, "level 21: administration center" },
|
||||
{ 0, 0, 0, "level 22: habitat" },
|
||||
{ 0, 0, 0, "level 23: lunar mining project" },
|
||||
{ 0, 0, 0, "level 24: quarry" },
|
||||
{ 0, 0, 0, "level 25: baron's den" },
|
||||
{ 0, 0, 0, "level 26: ballistyx" },
|
||||
{ 0, 0, 0, "level 27: mount pain" },
|
||||
{ 0, 0, 0, "level 28: heck" },
|
||||
{ 0, 0, 0, "level 29: river styx" },
|
||||
{ 0, 0, 0, "level 30: last call" },
|
||||
{ 0, 0, 0, "level 31: pharaoh" },
|
||||
{ 0, 0, 0, "level 32: caribbean" },
|
||||
|
||||
#endif
|
||||
|
||||
{ 0, 0, 0, NULL }
|
||||
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
===================
|
||||
FindMapStats
|
||||
|
||||
Finds or creats a mapStats_t structure for the given level.
|
||||
This can return NULL if the entire array is filled up, which may
|
||||
happen when people have an absurd number of downloaded levels.
|
||||
===================
|
||||
*/
|
||||
mapStats_t *FindMapStats( int dataset, int episode, int map, boolean create ) {
|
||||
for ( int i = 0 ; i < playState.numMapStats ; i++ ) {
|
||||
mapStats_t *ms = &playState.mapStats[i];
|
||||
if ( ms->dataset == dataset && ms->episode == episode && ms->map == map ) {
|
||||
return ms;
|
||||
}
|
||||
}
|
||||
if ( playState.numMapStats == MAX_MAPS ) {
|
||||
// all full.
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if ( !create ) {
|
||||
return NULL;
|
||||
}
|
||||
mapStats_t *cms = &playState.mapStats[playState.numMapStats];
|
||||
cms->dataset = dataset;
|
||||
cms->episode = episode;
|
||||
cms->map = map;
|
||||
playState.numMapStats++;
|
||||
|
||||
return cms;
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
FindMapName
|
||||
|
||||
episodes and maps are one base
|
||||
==================
|
||||
*/
|
||||
const char *FindMapName( int dataset, int episode, int map ) {
|
||||
for ( mapData_t *md = mapData ; md->name ; md++ ) {
|
||||
if ( md->dataset == dataset && md->episode == episode && md->map == map ) {
|
||||
return md->name;
|
||||
}
|
||||
}
|
||||
return "UNKNOWN MAP NAME";
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
==================
|
||||
iphoneMapSelectMenu
|
||||
|
||||
Skills are zero based:
|
||||
sk_baby=0,
|
||||
sk_easy,
|
||||
sk_medium,
|
||||
sk_hard,
|
||||
sk_nightmare=4
|
||||
|
||||
episodes are one base
|
||||
|
||||
==================
|
||||
*/
|
||||
static const int MAP_ROW_HEIGHT = 52;
|
||||
ibutton_t btnSkills[4];
|
||||
int dragVelocity;
|
||||
ibutton_t dragScroll;
|
||||
mapData_t *selectedMap;
|
||||
int totalDrag; // for determining if a release will activate the level
|
||||
boolean iphoneMapSelectMenu( mapStart_t *map ) {
|
||||
static int prevDragY;
|
||||
|
||||
if ( !dragScroll.x ) {
|
||||
// first initialization
|
||||
dragScroll.drawWidth = 480 - 80 - 64;
|
||||
dragScroll.drawHeight = 320;
|
||||
dragScroll.x = 64 + dragScroll.drawWidth / 2;
|
||||
dragScroll.y = 160;
|
||||
|
||||
static char * skillNames[4] = {
|
||||
"iphone/skill_easy.tga",
|
||||
"iphone/skill_normal.tga",
|
||||
"iphone/skill_hard.tga",
|
||||
"iphone/skill_nightmare.tga" }; // not really "nightmare" skill since easy is "baby"
|
||||
for ( int i = 0 ; i < 4 ; i++ ) {
|
||||
SetButtonPics( &btnSkills[i], skillNames[i], "", 400, i*80 );
|
||||
}
|
||||
}
|
||||
|
||||
// check for drag-scrolling
|
||||
if ( dragScroll.touch ) {
|
||||
if ( dragScroll.touch->y != prevDragY ) {
|
||||
dragVelocity = dragScroll.touch->y - prevDragY;
|
||||
prevDragY = dragScroll.touch->y;
|
||||
totalDrag += abs( dragVelocity );
|
||||
}
|
||||
}
|
||||
Cvar_SetValue( mapSelectY->name, mapSelectY->value - dragVelocity );
|
||||
|
||||
// decay the dragVelocity
|
||||
for ( int i = 0 ; i < 2 ; i++ ) {
|
||||
if ( dragVelocity < 0 ) {
|
||||
dragVelocity++;
|
||||
} else if ( dragVelocity > 0 ) {
|
||||
dragVelocity--;
|
||||
}
|
||||
}
|
||||
|
||||
int iskill = (int)skill->value;
|
||||
if ( iskill < 0 ) {
|
||||
iskill = 0;
|
||||
Cvar_SetValue( skill->name, iskill );
|
||||
} else if ( iskill >= MAX_SKILLS ) {
|
||||
iskill = MAX_SKILLS-1;
|
||||
Cvar_SetValue( skill->name, iskill );
|
||||
}
|
||||
|
||||
// snap back to bounds if dragging past end
|
||||
if ( mapSelectY->value < 0 ) {
|
||||
Cvar_SetValue( mapSelectY->name, 0 );
|
||||
}
|
||||
int numMaps = 0;
|
||||
for ( mapData_t *map = mapData ; map->name != NULL ; map++ ) {
|
||||
numMaps++;
|
||||
}
|
||||
if ( mapSelectY->value > numMaps * MAP_ROW_HEIGHT - 320 ) {
|
||||
Cvar_SetValue( mapSelectY->name, numMaps * MAP_ROW_HEIGHT - 320 );
|
||||
}
|
||||
|
||||
// scrolling display of levels
|
||||
int y = -mapSelectY->value;
|
||||
mapData_t *startMap = NULL;
|
||||
for ( mapData_t *map = mapData ; map->name != NULL ; map++ ) {
|
||||
if ( y > -64 && y < 320 ) {
|
||||
// find the mapStat_t for this map, if it has ever been started
|
||||
int completionFlags = 0;
|
||||
mapStats_t *ms = FindMapStats( map->dataset, map->episode, map->map, false );
|
||||
if ( ms ) {
|
||||
completionFlags = ms->completionFlags[iskill];
|
||||
}
|
||||
|
||||
// if we aren't already dragging, check for a touch on a map button
|
||||
if ( !dragScroll.touch ) {
|
||||
touch_t *touch = TouchInBounds( 120, y, 400-128, 48 );
|
||||
if ( touch ) {
|
||||
Sound_StartLocalSound( "iphone/bdown_01.wav" );
|
||||
dragScroll.touch = touch;
|
||||
prevDragY = touch->y;
|
||||
touch->controlOwner = &dragScroll;
|
||||
selectedMap = map;
|
||||
}
|
||||
}
|
||||
|
||||
// color background based on selected / entered / completed state
|
||||
if ( selectedMap == map ) {
|
||||
glColor4f( 1,1,1,1 ); // launch if released
|
||||
} else if ( completionFlags & MF_COMPLETED ) {
|
||||
glColor4f( 0.2, 0.5, 0.2, 1 );
|
||||
} else if ( completionFlags & MF_TRIED ) {
|
||||
glColor4f( 0.5, 0.2, 0.2, 1 );
|
||||
} else {
|
||||
glColor4f( 0.4, 0.4, 0.4, 1 );
|
||||
}
|
||||
|
||||
// use -1 x to avoid a texture wrap seam
|
||||
PK_StretchTexture( PK_FindTexture( "iphone/long_string_box.tga" ), 110, y, 400-114, 48 );
|
||||
glColor4f( 1,1,1,1 );
|
||||
|
||||
// draw the text
|
||||
float w = StringFontWidth( map->name );
|
||||
float fontScale = 0.75;
|
||||
if ( w > 360 ) {
|
||||
fontScale *= ( 360 / w );
|
||||
}
|
||||
iphoneDrawText( 120, y+32, fontScale, map->name );
|
||||
|
||||
// add the awards
|
||||
if ( completionFlags & MF_KILLS ) {
|
||||
PK_DrawTexture( PK_FindTexture( "iphone/kills.tga" ), 80, y+4 );
|
||||
}
|
||||
if ( completionFlags & MF_TIME ) {
|
||||
PK_DrawTexture( PK_FindTexture( "iphone/par.tga" ), 40, y+4 );
|
||||
}
|
||||
if ( completionFlags & MF_SECRETS ) {
|
||||
PK_DrawTexture( PK_FindTexture( "iphone/secrets.tga" ), 0, y+4 );
|
||||
}
|
||||
|
||||
}
|
||||
y += MAP_ROW_HEIGHT;
|
||||
}
|
||||
|
||||
if ( numTouches == 0 ) {
|
||||
totalDrag = 0;
|
||||
}
|
||||
|
||||
// draw the skill level
|
||||
for ( int i = 0 ; i < 4 ; i++ ) {
|
||||
if ( i == iskill ) {
|
||||
btnSkills[i].buttonFlags = 0;
|
||||
} else {
|
||||
btnSkills[i].buttonFlags = BF_DIMMED;
|
||||
}
|
||||
if ( HandleButton( &btnSkills[i] ) ) {
|
||||
Cvar_SetValue( skill->name, i );
|
||||
}
|
||||
}
|
||||
glColor4f( 1, 1, 1, 1 );
|
||||
|
||||
// handle back button before checking for touches in the awards area
|
||||
if ( BackButton() ) {
|
||||
map->map = -1;
|
||||
return true;
|
||||
}
|
||||
|
||||
// if we aren't already dragging, check for a touch anywhere outside the skill buttons
|
||||
if ( !dragScroll.touch ) {
|
||||
touch_t *touch = TouchInBounds( 0, 0, 400-128, 320 );
|
||||
if ( touch ) {
|
||||
dragScroll.touch = touch;
|
||||
prevDragY = touch->y;
|
||||
touch->controlOwner = &dragScroll;
|
||||
selectedMap = NULL;
|
||||
Sound_StartLocalSoundAtVolume( "iphone/controller_down_01_SILENCE.wav", touchClick->value );
|
||||
}
|
||||
} else {
|
||||
// if we dragged more than a few pixels, don't launch the level
|
||||
if ( totalDrag > 8 ) {
|
||||
selectedMap = NULL;
|
||||
}
|
||||
if ( !dragScroll.touch->down ) {
|
||||
// lifted finger
|
||||
dragScroll.touch = NULL;
|
||||
if ( selectedMap ) {
|
||||
Sound_StartLocalSound( "iphone/baction_01.wav" );
|
||||
startMap = selectedMap;
|
||||
selectedMap = NULL;
|
||||
} else {
|
||||
Sound_StartLocalSoundAtVolume( "iphone/controller_up_01_SILENCE.wav", touchClick->value );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if ( !startMap ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
map->skill = iskill;
|
||||
map->episode = startMap->episode;
|
||||
map->map = startMap->map;
|
||||
map->dataset = startMap->dataset;
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,958 @@
|
||||
/*
|
||||
|
||||
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.
|
||||
|
||||
*/
|
||||
|
||||
#include "../doomiphone.h"
|
||||
|
||||
// Only one game can be set up at a time on a given wireless segment, although
|
||||
// several independent games can be played.
|
||||
// If a valid setupPacket has arrived in the last second, that will be the
|
||||
// displayed game, otherwise the local system starts sending out setupPackets.
|
||||
packetSetup_t setupPacket;
|
||||
int setupPacketFrameNum;
|
||||
int localGameID; // change every time we take over as the sender of setupPackets
|
||||
|
||||
boolean levelHasBeenLoaded; // determines if "resume game" does a loadGame and exiting does a saveGame
|
||||
|
||||
menuState_t menuState;
|
||||
color4_t highlightColor = { 128, 128, 128, 255 };
|
||||
color4_t colorPressed = { 128, 128, 0, 255 };
|
||||
|
||||
void SetupEmptyNetGame();
|
||||
|
||||
void R_Draw_Blend( int x, int y, int w, int h, color4_t c ) {
|
||||
glDisable( GL_TEXTURE_2D );
|
||||
glColor4ubv( c );
|
||||
|
||||
glBegin( GL_QUADS );
|
||||
|
||||
glVertex2i( x, y );
|
||||
glVertex2i( x+w, y );
|
||||
glVertex2i( x+w, y+h );
|
||||
glVertex2i( x, y+h );
|
||||
|
||||
glEnd();
|
||||
|
||||
glColor3f( 1, 1, 1 );
|
||||
glEnable( GL_TEXTURE_2D );
|
||||
}
|
||||
|
||||
|
||||
void R_Draw_Fill( int x, int y, int w, int h, color3_t c ) {
|
||||
// as of 2.2 OS, doing a clear with a small scissor rect is MUCH slower
|
||||
// than drawing geometry
|
||||
color4_t c4;
|
||||
c4[0] = c[0];
|
||||
c4[1] = c[1];
|
||||
c4[2] = c[2];
|
||||
c4[3] = 255;
|
||||
R_Draw_Blend( x, y, w, h, c4 );
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
iphoneSlider
|
||||
|
||||
Returns true if modified
|
||||
==================
|
||||
*/
|
||||
#define SF_DISABLED 1 // grey out, don't respond to touches
|
||||
#define SF_INTEGER 2 // don't add percent
|
||||
boolean iphoneSlider( int x, int y, int w, int h, const char *title, cvar_t *cvar,
|
||||
float min, float max, int sliderFlags ) {
|
||||
float value = cvar->value;
|
||||
char str[80];
|
||||
float f = ( value - min ) / ( max - min );
|
||||
|
||||
if ( f < 0 ) {
|
||||
f = 0;
|
||||
}
|
||||
if ( f > 1 ) {
|
||||
f = 1;
|
||||
}
|
||||
|
||||
// draw the background
|
||||
PK_StretchTexture( PK_FindTexture( "iphone/slider_shadow.tga" ), x, y, w, h );
|
||||
|
||||
// draw the current range
|
||||
PK_BindTexture( PK_FindTexture( "iphone/slider_bar.tga" ) );
|
||||
#if 0
|
||||
// proportional
|
||||
glBegin( GL_QUADS );
|
||||
|
||||
glTexCoord2f( 0.0f, 0.0f ); glVertex2i( x, y );
|
||||
glTexCoord2f( f, 0.0f ); glVertex2i( x+w*f, y );
|
||||
glTexCoord2f( f, 1.0f ); glVertex2i( x+w*f, y+h );
|
||||
glTexCoord2f( 0.0f, 1.0f ); glVertex2i( x, y+h );
|
||||
|
||||
glEnd();
|
||||
#else
|
||||
// dragging thumb
|
||||
glBegin( GL_QUADS );
|
||||
|
||||
glTexCoord2f( 0.0f, 0.0f ); glVertex2i( x+w*f-8, y );
|
||||
glTexCoord2f( 1.0f, 0.0f ); glVertex2i( x+w*f+8, y );
|
||||
glTexCoord2f( 1.0f, 1.0f ); glVertex2i( x+w*f+8, y+h );
|
||||
glTexCoord2f( 0.0f, 1.0f ); glVertex2i( x+w*f-8, y+h );
|
||||
|
||||
glEnd();
|
||||
#endif
|
||||
|
||||
// draw the title and fraction
|
||||
if ( sliderFlags & SF_INTEGER ) {
|
||||
sprintf( str, "%s : %i", title, (int)value );
|
||||
} else {
|
||||
sprintf( str, "%s : %i%%", title, (int)(f*100+0.5) );
|
||||
}
|
||||
iphoneCenterText( x+ w/2, y+h-10, 0.75, str );
|
||||
|
||||
// check for touches
|
||||
if ( numTouches > 0 && touches[0][0] >= x && touches[0][0] < x + w
|
||||
&& touches[0][1] >= y && touches[0][1] < y+ h ) {
|
||||
float newValue;
|
||||
float delta;
|
||||
|
||||
f = (float)( touches[0][0] - x ) / w;
|
||||
|
||||
if ( sliderFlags & SF_INTEGER ) {
|
||||
newValue = rint( min + f * ( max - min ) );
|
||||
} else {
|
||||
// round to tenths
|
||||
f = (int)( ( f + 0.05 ) * 10 ) * 0.1f;
|
||||
if ( f < 0 ) {
|
||||
f = 0;
|
||||
}
|
||||
if ( f > 1.0 ) {
|
||||
f = 1.0;
|
||||
}
|
||||
newValue = min + f * ( max - min );
|
||||
}
|
||||
|
||||
delta = fabs( newValue - cvar->value );
|
||||
if ( f == 0 && cvar->value == 0 ) {
|
||||
// special case of disable-at-0
|
||||
} else if ( delta > 0.01 ) {
|
||||
Cvar_SetValue( cvar->name, newValue );
|
||||
Sound_StartLocalSound( "iphone/slide_01.wav" );
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
BackButton
|
||||
|
||||
==================
|
||||
*/
|
||||
ibutton_t btnBack;
|
||||
int BackButton() {
|
||||
if ( !btnBack.texture ) {
|
||||
SetButtonPicsAndSizes( &btnBack, "iphone/back_button.tga", "", 0, 0, 48, 48 );
|
||||
}
|
||||
return HandleButton( &btnBack );
|
||||
}
|
||||
|
||||
void GetMoreLevels( int x, int y ) {
|
||||
// directly to the app store for more levels
|
||||
SysIPhoneOpenURL( "http://phobos.apple.com/WebObjects/MZStore.woa/wa/viewSoftware?id=304694876" );
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
==================
|
||||
iphoneMainMenu
|
||||
|
||||
==================
|
||||
*/
|
||||
ibutton_t btnResumeGame;
|
||||
ibutton_t btnNewGame;
|
||||
ibutton_t btnControls;
|
||||
ibutton_t btnMultiplayer;
|
||||
ibutton_t btnWWW;
|
||||
ibutton_t btnDemo;
|
||||
|
||||
void iphoneMainMenu() {
|
||||
if ( !btnResumeGame.texture ) {
|
||||
// initial setup
|
||||
SetButtonPics( &btnResumeGame, "iphone/resume_game.tga", "Resume Game", 16, 4 );
|
||||
SetButtonPics( &btnNewGame, "iphone/new_game.tga", "New Game", 176, 4 );
|
||||
SetButtonPics( &btnDemo, "iphone/demo.tga", "Demos", 336, 4 );
|
||||
SetButtonPics( &btnMultiplayer, "iphone/multiplay.tga", "Multiplayer", 16, 168 );
|
||||
SetButtonPics( &btnWWW, "iphone/website.tga", "Website", 176, 168 );
|
||||
SetButtonPics( &btnControls, "iphone/controls.tga", "Options", 336, 168 );
|
||||
}
|
||||
|
||||
if ( netgame ) {
|
||||
// disable buttons if we are already in a netgame
|
||||
btnNewGame.buttonFlags = BF_INACTIVE | BF_TRANSPARENT;
|
||||
btnMultiplayer.buttonFlags = BF_INACTIVE | BF_TRANSPARENT;
|
||||
btnWWW.buttonFlags = BF_INACTIVE | BF_TRANSPARENT;
|
||||
btnDemo.buttonFlags = BF_INACTIVE | BF_TRANSPARENT;
|
||||
}
|
||||
|
||||
if ( HandleButton( &btnResumeGame ) ) {
|
||||
ResumeGame();
|
||||
}
|
||||
|
||||
if ( HandleButton( &btnNewGame ) ) {
|
||||
menuState = IPM_MAPS;
|
||||
}
|
||||
|
||||
if ( HandleButton( &btnControls ) ) {
|
||||
menuState = IPM_CONTROLS;
|
||||
}
|
||||
|
||||
if ( !NetworkAvailable() ) {
|
||||
// disable multiplayer if we don't have a good device
|
||||
btnMultiplayer.buttonFlags = BF_INACTIVE | BF_TRANSPARENT;
|
||||
} else if ( netgame ) {
|
||||
// disable multiplayer if we are already in a netgame
|
||||
btnMultiplayer.buttonFlags = BF_INACTIVE | BF_TRANSPARENT;
|
||||
} else if ( NetworkServerAvailable() ) {
|
||||
// blink the multiplayer button if a local server is available
|
||||
btnMultiplayer.buttonFlags = BF_GLOW;
|
||||
} else {
|
||||
btnMultiplayer.buttonFlags = 0;
|
||||
}
|
||||
|
||||
if ( HandleButton( &btnMultiplayer ) ) {
|
||||
// get the address for the local service, which may
|
||||
// start up a bluetooth personal area network
|
||||
boolean serverResolved = ResolveNetworkServer( &netServer.address );
|
||||
|
||||
// open our socket now that the network interfaces have been configured
|
||||
// Explicitly open on interface 1, which is en0. If bluetooth ever starts
|
||||
// working better, we can handle multiple interfaces.
|
||||
if ( gameSocket <= 0 ) {
|
||||
gameSocket = UDPSocket( "en0", DOOM_PORT );
|
||||
}
|
||||
|
||||
// get the address for the local service
|
||||
if ( !serverResolved ) {
|
||||
// nobody else is acting as a server, so start one here
|
||||
RegisterGameService();
|
||||
SetupEmptyNetGame();
|
||||
}
|
||||
menuState = IPM_MULTIPLAYER;
|
||||
}
|
||||
// draw the available interfaces over the blinking net button
|
||||
if ( NetworkServerAvailable() ) {
|
||||
iphoneCenterText( btnMultiplayer.x + btnMultiplayer.drawWidth / 2,
|
||||
btnMultiplayer.y + btnMultiplayer.drawHeight/2, 0.75,
|
||||
NetworkServerTransport() );
|
||||
}
|
||||
|
||||
if ( HandleButton( &btnWWW ) ) {
|
||||
// menuState = IPM_PACKET_TEST; // !@# debug
|
||||
SysIPhoneOpenURL( "http://www.idsoftware.com/doomclassic/" );
|
||||
}
|
||||
|
||||
if ( HandleButton( &btnDemo ) ) {
|
||||
StartDemoGame( btnDemo.twoFingerPress );
|
||||
}
|
||||
if ( btnDemo.twoFingerPress ) {
|
||||
strcpy( timeDemoResultString, "TIMEDEMO" );
|
||||
}
|
||||
// draw the timedemo results on top of the button
|
||||
if ( timeDemoResultString[0] ) {
|
||||
iphoneCenterText( btnDemo.x + btnDemo.drawWidth / 2, btnDemo.y + btnDemo.drawHeight/2, 0.75,
|
||||
timeDemoResultString );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
==================
|
||||
iphoneControlMenu
|
||||
|
||||
==================
|
||||
*/
|
||||
ibutton_t btnSchemes[4];
|
||||
ibutton_t btnSettings;
|
||||
ibutton_t btnMove;
|
||||
|
||||
void iphoneControlMenu() {
|
||||
int i;
|
||||
|
||||
// iphoneCenterText( 240, 16, 0.75, __DATE__" "__TIME__ );
|
||||
|
||||
// 112 units between
|
||||
|
||||
if ( BackButton() ) {
|
||||
menuState = IPM_MAIN;
|
||||
}
|
||||
if ( NewTextButton( &btnSettings, "Settings", 480-128, 0, 128, 48 ) ) {
|
||||
menuState = IPM_OPTIONS;
|
||||
}
|
||||
if ( NewTextButton( &btnMove, "Move Controls", 48 + (480-(128+160+48))/2, 0, 160, 48 ) ) {
|
||||
menuState = IPM_HUDEDIT;
|
||||
}
|
||||
|
||||
if ( !btnSchemes[0].texture ) {
|
||||
for ( int i = 0 ; i < 3 ; i++ ) {
|
||||
SetButtonPicsAndSizes( &btnSchemes[i], va("iphone/config_%i.tga",i+1), "",
|
||||
32 + (96+64) * i, 48 + ( 112 - 64 )*0.5, 96, 64 );
|
||||
}
|
||||
}
|
||||
|
||||
for ( i = 0 ; i < 3 ; i++ ) {
|
||||
char str[128];
|
||||
sprintf( str, "iphone/config_%i.tga", i+1 );
|
||||
if ( i != controlScheme->value ) {
|
||||
btnSchemes[i].buttonFlags = BF_DIMMED;
|
||||
} else {
|
||||
btnSchemes[i].buttonFlags = 0;
|
||||
}
|
||||
if ( HandleButton( &btnSchemes[i] ) ) {
|
||||
Cvar_SetValue( controlScheme->name, i );
|
||||
HudSetForScheme( i );
|
||||
}
|
||||
}
|
||||
|
||||
iphoneSlider( 20, 160, 440, 40, "Move stick size", stickMove, 64, 128, 0 );
|
||||
iphoneSlider( 20, 210, 440, 40, "Turn stick size", stickTurn, 64, 128, 0 );
|
||||
|
||||
iphoneSlider( 20, 260, 440, 40, "Tilt move speed", tiltMove, 100, 300, 0 );
|
||||
if ( tiltMove->value == 100 ) {
|
||||
Cvar_SetValue( tiltMove->name, 0 );
|
||||
}
|
||||
if ( tiltMove->value ) {
|
||||
Cvar_SetValue( tiltTurn->name, 0 );
|
||||
}
|
||||
#if 0
|
||||
iphoneSlider( 20, 280, 440, 40, "Tilt turn speed", tiltTurn, 1500, 3500, 0 );
|
||||
if ( tiltTurn->value == 1500 ) {
|
||||
Cvar_SetValue( tiltTurn->name, 0 );
|
||||
}
|
||||
if ( tiltTurn->value ) {
|
||||
Cvar_SetValue( tiltMove->name, 0 );
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void SetupEmptyNetGame() {
|
||||
Com_Printf( "SetupEmptyNetGame()\n" );
|
||||
// no current setup packet, so initialize with this phone's default values
|
||||
localGameID = SysIphoneMicroseconds();
|
||||
memset( &setupPacket, 0, sizeof( setupPacket ) );
|
||||
setupPacket.gameID = localGameID;
|
||||
setupPacket.packetType = PACKET_VERSION_SETUP;
|
||||
setupPacket.map.dataset = mpDataset->value;
|
||||
setupPacket.map.episode = mpEpisode->value;
|
||||
setupPacket.map.map = mpMap->value;
|
||||
setupPacket.map.skill = mpSkill->value;
|
||||
setupPacket.deathmatch = mpDeathmatch->value;
|
||||
setupPacket.timelimit = timeLimit->value;
|
||||
setupPacket.fraglimit = fragLimit->value;
|
||||
setupPacket.playerID[0] = playerID;
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
SendJoinPacket
|
||||
|
||||
These will be sent to the server ever frame we are in the multiplayer menu.
|
||||
==================
|
||||
*/
|
||||
void SendJoinPacket() {
|
||||
packetJoin_t pj;
|
||||
|
||||
pj.packetType = PACKET_VERSION_JOIN;
|
||||
pj.gameID = setupPacket.gameID;
|
||||
pj.playerID = playerID;
|
||||
|
||||
int r = sendto( gameSocket, &pj, sizeof( pj ), 0,
|
||||
&netServer.address, sizeof( netServer.address ) );
|
||||
if ( r == -1 ) {
|
||||
struct sockaddr_in *addr = (struct sockaddr_in *)&netServer.address;
|
||||
byte *ip = (byte *)&addr->sin_addr;
|
||||
Com_Printf( "UDP sendTo %i.%i.%i.%i failed: %s\n",
|
||||
ip[0], ip[1], ip[2], ip[3], strerror( errno ) );
|
||||
close( gameSocket );
|
||||
gameSocket = -1;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
iphoneMultiplayerMenu
|
||||
|
||||
==================
|
||||
*/
|
||||
ibutton_t btnCoop;
|
||||
ibutton_t btnDeathmatch;
|
||||
ibutton_t btnMap;
|
||||
ibutton_t btnNetSettings;
|
||||
typedef enum {
|
||||
NM_MAIN,
|
||||
NM_MAP_SELECT,
|
||||
NM_OPTIONS
|
||||
} netMenu_t;
|
||||
netMenu_t netMenu;
|
||||
|
||||
void iphoneMultiplayerMenu() {
|
||||
if ( gameSocket <= 0 ) {
|
||||
// no socket, so no multiplayer
|
||||
TerminateGameService(); // don't advertise for any more new players
|
||||
setupPacket.gameID = 0; // stop sending packets
|
||||
menuState = IPM_MAIN;
|
||||
return;
|
||||
}
|
||||
|
||||
boolean server = ( setupPacket.gameID == localGameID );
|
||||
|
||||
// different screen when selecting a map to play
|
||||
if ( netMenu == NM_MAP_SELECT ) {
|
||||
mapStart_t map;
|
||||
if ( !iphoneMapSelectMenu( &map ) ) {
|
||||
// haven't selected anything yet
|
||||
return;
|
||||
}
|
||||
netMenu = NM_MAIN;
|
||||
if ( map.map != -1 ) {
|
||||
// selected something new, didn't hit the back arrow
|
||||
setupPacket.map = map;
|
||||
}
|
||||
} else if ( netMenu == NM_OPTIONS ) {
|
||||
Cvar_SetValue( fragLimit->name, setupPacket.fraglimit );
|
||||
if ( iphoneSlider( 104, 64, 272, 40, "frag limit", fragLimit, 0, 20, SF_INTEGER ) ) {
|
||||
if ( server ) {
|
||||
setupPacket.fraglimit = fragLimit->value;
|
||||
}
|
||||
}
|
||||
|
||||
Cvar_SetValue( timeLimit->name, setupPacket.timelimit );
|
||||
if ( iphoneSlider( 104, 64+56, 272, 40, "time limit", timeLimit, 0, 20, SF_INTEGER ) ) {
|
||||
if ( server ) {
|
||||
setupPacket.timelimit = timeLimit->value;
|
||||
}
|
||||
}
|
||||
if ( BackButton() ) {
|
||||
netMenu = NM_MAIN;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if ( !btnDeathmatch.texture ) {
|
||||
// initial setup
|
||||
SetButtonPicsAndSizes( &btnDeathmatch, "iphone/deathmatch.tga", "Deathmatch", 4+48, 64, 96, 96 );
|
||||
SetButtonPicsAndSizes( &btnCoop, "iphone/co-op.tga", "Cooperative", 480-148, 64, 96, 96 );
|
||||
}
|
||||
|
||||
if ( BackButton() ) {
|
||||
if ( server ) {
|
||||
TerminateGameService(); // don't advertise for any more new players
|
||||
setupPacket.gameID = 0; // stop sending packets
|
||||
}
|
||||
menuState = IPM_MAIN;
|
||||
}
|
||||
|
||||
if ( !server ) {
|
||||
// we aren't the server
|
||||
// send our join packet every frame
|
||||
SendJoinPacket();
|
||||
|
||||
if ( setupPacketFrameNum < iphoneFrameNum - 30 ) {
|
||||
// haven't received a current server packet
|
||||
char str[1024];
|
||||
struct sockaddr_in *sin = (struct sockaddr_in *)&netServer.address;
|
||||
byte *ip = (byte *)&sin->sin_addr;
|
||||
sprintf( str, "Joining server at %i.%i.%i.%i:%i\n", ip[0], ip[1], ip[2], ip[3],
|
||||
ntohs( sin->sin_port ) );
|
||||
iphoneCenterText( 240, 160, 0.75, str );
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
// cull out any players that haven't given us a packet in a couple seconds
|
||||
int now = SysIphoneMilliseconds();
|
||||
for ( int i = 1 ; i < MAXPLAYERS ; i++ ) {
|
||||
if ( setupPacket.playerID[i] && now - netPlayers[i].peer.lastPacketTime > 1000 ) {
|
||||
printf( "Dropping player %i: last:%i now:%i\n", i, netPlayers[i].peer.lastPacketTime, now );
|
||||
setupPacket.playerID[i] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// draw the level and allow clicking to change
|
||||
Cvar_SetValue( mpDataset->name, setupPacket.map.dataset );
|
||||
Cvar_SetValue( mpEpisode->name, setupPacket.map.episode );
|
||||
Cvar_SetValue( mpMap->name, setupPacket.map.map );
|
||||
Cvar_SetValue( mpSkill->name, setupPacket.map.skill );
|
||||
|
||||
// map select button / display
|
||||
if ( NewTextButton( &btnMap, FindMapName( mpDataset->value, mpEpisode->value, mpMap->value ), 64, 0, 480-128, 48 ) ) {
|
||||
if ( server ) {
|
||||
// clients can't go into this menu
|
||||
netMenu = NM_MAP_SELECT;
|
||||
}
|
||||
}
|
||||
|
||||
if ( setupPacket.deathmatch ) {
|
||||
btnDeathmatch.buttonFlags = 0;
|
||||
btnCoop.buttonFlags = BF_DIMMED;
|
||||
} else {
|
||||
btnDeathmatch.buttonFlags = BF_DIMMED;
|
||||
btnCoop.buttonFlags = 0;
|
||||
}
|
||||
|
||||
if ( HandleButton( &btnDeathmatch ) ) {
|
||||
if ( server ) {
|
||||
Cvar_SetValue( mpDeathmatch->name, 3 ); // weapons stay, items respawn rules
|
||||
setupPacket.deathmatch = mpDeathmatch->value;
|
||||
}
|
||||
}
|
||||
if ( HandleButton( &btnCoop ) ) {
|
||||
if ( server ) {
|
||||
Cvar_SetValue( mpDeathmatch->name, 0 );
|
||||
setupPacket.deathmatch = mpDeathmatch->value;
|
||||
}
|
||||
}
|
||||
|
||||
if ( NewTextButton( &btnNetSettings, "Settings", 240-64, 64+24, 128, 48 ) ) {
|
||||
netMenu = NM_OPTIONS;
|
||||
}
|
||||
|
||||
for ( int i = 0 ; i < 4 ; i ++ ) {
|
||||
int x = 45 + ( 64+45) * i;
|
||||
int y = 64+128;
|
||||
// FIXME: show proper player colors
|
||||
byte color[4][4] = { { 0, 255, 0, 255 }, { 128, 128, 128, 255 }, { 128,64,0, 255 }, {255,0,0, 255 } };
|
||||
glColor4ubv( color[i] );
|
||||
PK_DrawTexture( PK_FindTexture( "iphone/multi_backdrop.tga" ), x, y );
|
||||
glColor4f( 1, 1, 1, 1 );
|
||||
if ( setupPacket.playerID[i] == playerID ) {
|
||||
// bigger outline for your player slot
|
||||
PK_StretchTexture( PK_FindTexture( "iphone/multi_frame.tga" ), x, y, 64, 64 );
|
||||
}
|
||||
|
||||
|
||||
// draw doom guy face
|
||||
if ( setupPacket.playerID[i] != 0 ) {
|
||||
PK_DrawTexture( PK_FindTexture( "iphone/multi_face.tga" ), x, y );
|
||||
#if 0
|
||||
// temp display IP address
|
||||
byte *ip = (byte *)&setupPacket.address[i].sin_addr;
|
||||
iphoneDrawText( x-16, (i&1) ? y+16 : y+48, 0.75, va("%i.%i.%i.%i", ip[0], ip[1], ip[2], ip[3] ) );
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if ( server ) {
|
||||
// flash a tiny pic when transmitting
|
||||
if ( iphoneFrameNum & 1 ) {
|
||||
glColor4f( 1,1,1,1 );
|
||||
} else {
|
||||
glColor4f( 0.5,0.5,0.5,1 );
|
||||
}
|
||||
iphoneCenterText( 470, 310, 0.75, "*" );
|
||||
glColor4f( 1,1,1,1 );
|
||||
}
|
||||
if ( setupPacketFrameNum == iphoneFrameNum ) {
|
||||
iphoneCenterText( 450, 310, 0.75, "*" );
|
||||
}
|
||||
// iphoneDrawText( 0, 310, 0.75, va("%i:%i", localGameID, setupPacket.gameID ) );
|
||||
|
||||
// only draw the start button if we have at least two players in game
|
||||
int numPlayers = 0;
|
||||
for ( int i = 0 ; i < MAXPLAYERS ; i++ ) {
|
||||
if ( setupPacket.playerID[i] != 0 ) {
|
||||
numPlayers++;
|
||||
}
|
||||
}
|
||||
|
||||
if ( numPlayers > 1 ) {
|
||||
if ( server ) {
|
||||
static ibutton_t btnStart;
|
||||
if ( NewTextButton( &btnStart, "Start game", 240-80, 320-48, 160, 48 ) ) {
|
||||
setupPacket.startGame = 1;
|
||||
StartNetGame();
|
||||
TerminateGameService(); // don't advertise for any more new players
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
iphoneCenterText( 240, 320-10, 0.75, "Waiting for server to start the game" );
|
||||
}
|
||||
} else {
|
||||
byte *ip = (byte *)&gameSocketAddress.sin_addr;
|
||||
iphoneCenterText( 240, 320-10, 0.75, va("Waiting for players on %i.%i.%i.%i",
|
||||
ip[0], ip[1], ip[2], ip[3] ) );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static ibutton_t optionButtons[2][6];
|
||||
static ibutton_t defaultsButton;
|
||||
boolean OptionButton( int col, int row, const char *title ) {
|
||||
assert( col >= 0 && col < 2 && row >= 0 && row < 6 );
|
||||
return NewTextButton( &optionButtons[col][row], title, 10 + 235 * col, 64 + 50 * row, 225, 48 );
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
iphoneOptionsMenu
|
||||
|
||||
==================
|
||||
*/
|
||||
void iphoneOptionsMenu() {
|
||||
if ( BackButton() ) {
|
||||
menuState = IPM_CONTROLS;
|
||||
}
|
||||
|
||||
boolean musicState = music->value;
|
||||
if ( SysIPhoneOtherAudioIsPlaying() ) {
|
||||
// music always off when ipod music is playing
|
||||
musicState = false;
|
||||
}
|
||||
|
||||
if ( NewTextButton( &defaultsButton, "Defaults", 240-225/2, 2, 225, 48 ) ) {
|
||||
// reset all cvars except the reverse-landscape mode value
|
||||
float value = revLand->value;
|
||||
Cvar_Reset_f();
|
||||
Cvar_SetValue( revLand->name, value );
|
||||
HudSetForScheme(0);
|
||||
iphoneStartMusic();
|
||||
}
|
||||
|
||||
if ( OptionButton( 0, 0, autoUse->value ? "Auto use: ON" : "Auto use: OFF" ) ) {
|
||||
Cvar_SetValue( autoUse->name, !autoUse->value );
|
||||
}
|
||||
if ( OptionButton( 0, 1, statusBar->value ? "Status bar: ON" : "Status bar: OFF" ) ) {
|
||||
Cvar_SetValue( statusBar->name, !statusBar->value );
|
||||
}
|
||||
if ( OptionButton( 0, 2, touchClick->value ? "Touch click: ON" : "Touch click: OFF" ) ) {
|
||||
Cvar_SetValue( touchClick->name, !touchClick->value );
|
||||
}
|
||||
if ( OptionButton( 0, 3, messages->value ? "Text messages: ON" : "Text messages: OFF" ) ) {
|
||||
Cvar_SetValue( messages->name, !messages->value );
|
||||
}
|
||||
if ( OptionButton( 1, 0, drawControls->value ? "Draw controls: ON" : "Draw controls: OFF" ) ) {
|
||||
Cvar_SetValue( drawControls->name, !drawControls->value );
|
||||
}
|
||||
if ( OptionButton( 1, 1, musicState ? "Music: ON" : "Music: OFF" ) ) {
|
||||
if ( !SysIPhoneOtherAudioIsPlaying() ) {
|
||||
Cvar_SetValue( music->name, !music->value );
|
||||
if ( music->value ) {
|
||||
iphoneStartMusic();
|
||||
} else {
|
||||
iphoneStopMusic();
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( OptionButton( 1, 2, centerSticks->value ? "Center sticks: ON" : "Center sticks: OFF" ) ) {
|
||||
Cvar_SetValue( centerSticks->name, !centerSticks->value );
|
||||
}
|
||||
if ( OptionButton( 1, 3, rampTurn->value ? "Ramp turn: ON" : "Ramp turn: OFF" ) ) {
|
||||
Cvar_SetValue( rampTurn->name, !rampTurn->value );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
===================
|
||||
iphoneIntermission
|
||||
|
||||
The end-of-level switch was just hit, note the state and awards
|
||||
for the map select menu
|
||||
===================
|
||||
*/
|
||||
void iphoneIntermission( wbstartstruct_t* wb ) {
|
||||
if ( deathmatch || netgame ) {
|
||||
// no achievements in deathmatch mode
|
||||
return;
|
||||
}
|
||||
|
||||
// find the current episode / map combination
|
||||
|
||||
// if a mapStat_t doesn't exist for this yet, create one
|
||||
|
||||
// mark this level / skill combination as tried
|
||||
mapStats_t *cms = FindMapStats( playState.map.dataset, playState.map.episode, playState.map.map, true );
|
||||
if ( !cms ) {
|
||||
return;
|
||||
}
|
||||
|
||||
int skill = playState.map.skill;
|
||||
cms->completionFlags[skill] |= MF_COMPLETED;
|
||||
|
||||
// add the awards
|
||||
if ( wb->plyr[0].stime < wb->partime ) {
|
||||
cms->completionFlags[skill] |= MF_TIME;
|
||||
}
|
||||
|
||||
int numkills = 0;
|
||||
int numsecrets = 0;
|
||||
int numitems = 0;
|
||||
for ( int i = 0 ; i < MAXPLAYERS ; i++ ) {
|
||||
if ( wb->plyr[i].in ) {
|
||||
numkills += wb->plyr[i].skills;
|
||||
numitems += wb->plyr[i].sitems;
|
||||
numsecrets += wb->plyr[i].ssecret;
|
||||
}
|
||||
}
|
||||
if ( numkills >= wb->maxkills ) {
|
||||
cms->completionFlags[skill] |= MF_KILLS;
|
||||
}
|
||||
if ( numitems >= wb->maxitems ) {
|
||||
cms->completionFlags[skill] |= MF_TREASURE;
|
||||
}
|
||||
if ( numsecrets >= wb->maxsecret ) {
|
||||
cms->completionFlags[skill] |= MF_SECRETS;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
===================
|
||||
iphoneStartLevel
|
||||
|
||||
Do a savegame with the current state
|
||||
===================
|
||||
*/
|
||||
void iphoneStartLevel() {
|
||||
if ( deathmatch || netgame ) {
|
||||
// no achievements in deathmatch mode
|
||||
|
||||
// reset the levelTimer
|
||||
if ( levelTimer && setupPacket.timelimit > 0 ) {
|
||||
// 30 hz, minutes
|
||||
levelTimeCount = setupPacket.timelimit * 30 * 60;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
playState.map.map = gamemap;
|
||||
|
||||
// automatic save game
|
||||
G_SaveGame( 0, "entersave" );
|
||||
G_DoSaveGame(true);
|
||||
|
||||
// mark this level as tried
|
||||
mapStats_t *cms = FindMapStats( playState.map.dataset, playState.map.episode, playState.map.map, true );
|
||||
if ( cms ) {
|
||||
cms->completionFlags[playState.map.skill] |= MF_TRIED;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
===================
|
||||
DrawLiveBackground
|
||||
|
||||
Draw a randomish moving cloudy background
|
||||
===================
|
||||
*/
|
||||
void DrawLiveBackground() {
|
||||
static float bgVectors[2][2] = { { 0.01, 0.015 }, { -0.01, -0.02 } };
|
||||
float fade[2];
|
||||
|
||||
// slide and fade a couple textures around
|
||||
static float tc[2][4][2];
|
||||
for ( int i = 0 ; i < 2 ; i++ ) {
|
||||
int ofs = iphoneFrameNum + i * 32;
|
||||
float dist = ( ofs & 63 );
|
||||
for ( int j = 0 ; j < 2 ; j ++ ) {
|
||||
for ( int k = 0 ; k < 2 ; k++ ) {
|
||||
if ( rand()&1 ) {
|
||||
if ( bgVectors[j][k] < 0.03 ) {
|
||||
bgVectors[j][k] += 0.0001;
|
||||
}
|
||||
} else {
|
||||
if ( bgVectors[j][k] > -0.03 ) {
|
||||
bgVectors[j][k] -= 0.0001;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
fade[i] = sin( ( dist - 16 ) / 32.0 * M_PI ) * 0.5 + 0.5;
|
||||
fade[i] *= 0.7;
|
||||
|
||||
for ( int j = 0 ; j < 2 ; j++ ) {
|
||||
tc[i][0][j] += bgVectors[i][j];
|
||||
tc[i][0][j] -= floor( tc[i][0][j] );
|
||||
}
|
||||
tc[i][1][0] = tc[i][0][0]+1;
|
||||
tc[i][1][1] = tc[i][0][1]+0;
|
||||
|
||||
tc[i][2][0] = tc[i][0][0]+0;
|
||||
tc[i][2][1] = tc[i][0][1]+1;
|
||||
|
||||
tc[i][3][0] = tc[i][0][0]+1;
|
||||
tc[i][3][1] = tc[i][0][1]+1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Fill rate performance is an issue just for two scrolling layers under
|
||||
// modest GUI objects. Using a PVR2 texture and a single multitexture
|
||||
// pass helps. If all the GUI objects were drawn with depth buffering,
|
||||
// the surface rejection would help out, but bumping depth after every
|
||||
// draw would be a bit of a chore.
|
||||
|
||||
#if 0
|
||||
glClear( GL_DEPTH_BUFFER_BIT );
|
||||
glDepthMask( 1 ); // write the depth buffer
|
||||
glEnable( GL_DEPTH_TEST ); // depth test this background
|
||||
#endif
|
||||
|
||||
PK_BindTexture( PK_FindTexture( "iphone/livetile_1.tga" ) );
|
||||
|
||||
glDisable( GL_BLEND );
|
||||
glDisable( GL_DEPTH_TEST );
|
||||
|
||||
// multitexture setup
|
||||
glActiveTexture( GL_TEXTURE1 );
|
||||
glClientActiveTexture( GL_TEXTURE1 );
|
||||
glEnable( GL_TEXTURE_2D );
|
||||
PK_BindTexture( PK_FindTexture( "iphone/livetile_1.tga" ) );
|
||||
glTexCoordPointer( 2, GL_FLOAT, 8, tc[1][0] );
|
||||
glEnableClientState( GL_TEXTURE_COORD_ARRAY );
|
||||
|
||||
glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_ADD );
|
||||
// glColor4f( fade[0], fade[0], fade[0], fade[1] );
|
||||
glBegin( GL_TRIANGLE_STRIP );
|
||||
|
||||
glTexCoord2f( tc[0][0][0], tc[0][0][1] ); glVertex3f( 0, 0, 0.5 );
|
||||
glTexCoord2f( tc[0][1][0], tc[0][1][1] ); glVertex3f( 480, 0, 0.5 );
|
||||
glTexCoord2f( tc[0][2][0], tc[0][2][1]+1 ); glVertex3f( 0, 320, 0.5 );
|
||||
glTexCoord2f( tc[0][3][0], tc[0][3][1]+1 ); glVertex3f( 480, 320, 0.5 );
|
||||
|
||||
glEnd();
|
||||
|
||||
// unbind the second texture
|
||||
glBindTexture( GL_TEXTURE_2D, 0 );
|
||||
glDisable( GL_TEXTURE_2D );
|
||||
glDisableClientState( GL_TEXTURE_COORD_ARRAY );
|
||||
glActiveTexture( GL_TEXTURE0 );
|
||||
glClientActiveTexture( GL_TEXTURE0 );
|
||||
|
||||
glColor4f( 1, 1, 1, 1 );
|
||||
glEnable( GL_BLEND );
|
||||
#if 0
|
||||
// Enable depth test, but not depth writes, so the tile dorting
|
||||
// minimizes the amount of time drawing the background when it
|
||||
// is mostly covered.
|
||||
glEnable( GL_DEPTH_TEST );
|
||||
glDepthMask( 0 );
|
||||
#endif
|
||||
}
|
||||
|
||||
#define MAX_PACKET_LOG 64
|
||||
int currentPacketLog;
|
||||
int packetLogMsec[MAX_PACKET_LOG];
|
||||
|
||||
void iphonePacketTester() {
|
||||
glClear( GL_COLOR_BUFFER_BIT );
|
||||
|
||||
if ( BackButton() ) {
|
||||
menuState = IPM_MAIN;
|
||||
return;
|
||||
}
|
||||
|
||||
struct sockaddr_in sender;
|
||||
unsigned senderLen = sizeof( sender );
|
||||
byte buffer[1024];
|
||||
while( 1 ) {
|
||||
int r = recvfrom( gameSocket, buffer, sizeof( buffer ), 0, (struct sockaddr *)&sender, &senderLen );
|
||||
if ( r == -1 ) {
|
||||
break;
|
||||
}
|
||||
packetSetup_t *sp = (packetSetup_t *)buffer;
|
||||
if ( sp->sendCount == setupPacket.sendCount ) {
|
||||
Com_Printf( "Duplicated receive: %i\n", sp->sendCount );
|
||||
} else if ( sp->sendCount < setupPacket.sendCount ) {
|
||||
Com_Printf( "Out of order receive: %i < %i\n", sp->sendCount, setupPacket.sendCount );
|
||||
} else if ( sp->sendCount > setupPacket.sendCount + 1 ) {
|
||||
Com_Printf( "Dropped %i packets before %i\n", sp->sendCount - 1 - setupPacket.sendCount, sp->sendCount );
|
||||
}
|
||||
setupPacket = *sp;
|
||||
packetLogMsec[currentPacketLog&(MAX_PACKET_LOG-1)] = SysIphoneMilliseconds();
|
||||
currentPacketLog++;
|
||||
}
|
||||
|
||||
color4_t activeColor = { 0, 255, 0, 255 };
|
||||
for ( int i = 1 ; i < MAX_PACKET_LOG ; i++ ) {
|
||||
int t1 = packetLogMsec[(currentPacketLog - i)&(MAX_PACKET_LOG-1)];
|
||||
int t2 = packetLogMsec[(currentPacketLog - i - 1)&(MAX_PACKET_LOG-1)];
|
||||
int msec = t1 - t2;
|
||||
R_Draw_Fill( 0, i * 4, msec, 2, activeColor );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
===================
|
||||
iphoneStartMenu
|
||||
|
||||
===================
|
||||
*/
|
||||
void iphoneStartMenu() {
|
||||
mapStart_t map;
|
||||
if ( !iphoneMapSelectMenu( &map ) ) {
|
||||
return;
|
||||
}
|
||||
if ( map.map == -1 ) {
|
||||
// hit the back button
|
||||
menuState = IPM_MAIN;
|
||||
return;
|
||||
}
|
||||
|
||||
StartSinglePlayerGame( map );
|
||||
}
|
||||
|
||||
/*
|
||||
===================
|
||||
iphoneDrawMenus
|
||||
|
||||
===================
|
||||
*/
|
||||
void iphoneDrawMenus() {
|
||||
if ( menuState == IPM_PACKET_TEST ) {
|
||||
// do this before the slow drawing background to get 60hz update rate
|
||||
iphonePacketTester();
|
||||
return;
|
||||
}
|
||||
|
||||
// draw the slow double-cloud layer
|
||||
DrawLiveBackground();
|
||||
|
||||
// check for game start in a received setup packet
|
||||
if ( !netgame && setupPacket.startGame ) {
|
||||
if ( StartNetGame() ) {
|
||||
setupPacket.startGame = false;
|
||||
// we aren't in this game
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// interactive menus
|
||||
switch ( menuState ) {
|
||||
case IPM_MAIN: iphoneMainMenu(); break;
|
||||
case IPM_MULTIPLAYER: iphoneMultiplayerMenu(); break;
|
||||
case IPM_MAPS: iphoneStartMenu(); break;
|
||||
case IPM_CONTROLS: iphoneControlMenu(); break;
|
||||
case IPM_OPTIONS: iphoneOptionsMenu(); break;
|
||||
case IPM_HUDEDIT: HudEditFrame(); break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,604 @@
|
||||
/*
|
||||
* iphone_net.c
|
||||
* doom
|
||||
*
|
||||
* Created by John Carmack on 7/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.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
|
||||
Deal with all the DNS / bonjour service discovery and resolution
|
||||
|
||||
*/
|
||||
|
||||
#include "../doomiphone.h"
|
||||
|
||||
#include <dns_sd.h>
|
||||
#include <netdb.h> // for gethostbyname
|
||||
#include <net/if.h> // for if_nameindex()
|
||||
|
||||
DNSServiceRef browseRef;
|
||||
DNSServiceRef clientServiceRef;
|
||||
DNSServiceRef serviceRef;
|
||||
boolean serviceRefValid;
|
||||
|
||||
typedef struct {
|
||||
int interfaceIndex;
|
||||
char browseName[1024];
|
||||
char browseRegtype[1024];
|
||||
char browseDomain[1024];
|
||||
} service_t;
|
||||
|
||||
boolean localServer;
|
||||
|
||||
// we can find services on both WiFi and Bluetooth interfaces
|
||||
#define MAX_SERVICE_INTEFACES 4
|
||||
service_t serviceInterfaces[MAX_SERVICE_INTEFACES];
|
||||
|
||||
boolean gotServerAddress;
|
||||
struct sockaddr resolvedServerAddress;
|
||||
|
||||
static const char *serviceName = "_DoomServer._udp.";
|
||||
|
||||
int InterfaceIndexForName( const char *ifname );
|
||||
|
||||
void DNSServiceRegisterReplyCallback (
|
||||
DNSServiceRef sdRef,
|
||||
DNSServiceFlags flags,
|
||||
DNSServiceErrorType errorCode,
|
||||
const char *name,
|
||||
const char *regtype,
|
||||
const char *domain,
|
||||
void *context ) {
|
||||
if ( errorCode == kDNSServiceErr_NoError ) {
|
||||
localServer = true;
|
||||
} else {
|
||||
localServer = false;
|
||||
}
|
||||
}
|
||||
|
||||
boolean RegisterGameService() {
|
||||
DNSServiceErrorType err = DNSServiceRegister(
|
||||
&serviceRef,
|
||||
kDNSServiceFlagsNoAutoRename, // we want a conflict error
|
||||
InterfaceIndexForName( "en0" ), // pass 0 for all interfaces
|
||||
"iPhone Doom Classic",
|
||||
serviceName,
|
||||
NULL, // domain
|
||||
NULL, // host
|
||||
htons( DOOM_PORT ),
|
||||
0, // txtLen
|
||||
NULL, // txtRecord
|
||||
DNSServiceRegisterReplyCallback,
|
||||
NULL // context
|
||||
);
|
||||
|
||||
if ( err != kDNSServiceErr_NoError ) {
|
||||
printf( "DNSServiceRegister error\n" );
|
||||
} else {
|
||||
// block until we get a response, process it, and run the callback
|
||||
err = DNSServiceProcessResult( serviceRef );
|
||||
if ( err != kDNSServiceErr_NoError ) {
|
||||
printf( "DNSServiceProcessResult error\n" );
|
||||
}
|
||||
}
|
||||
return localServer;
|
||||
}
|
||||
|
||||
void TerminateGameService() {
|
||||
if ( localServer ) {
|
||||
localServer = false;
|
||||
}
|
||||
DNSServiceRefDeallocate( serviceRef );
|
||||
memset( serviceInterfaces, 0, sizeof( serviceInterfaces ) );
|
||||
}
|
||||
|
||||
void DNSServiceQueryRecordReplyCallback (
|
||||
DNSServiceRef DNSServiceRef,
|
||||
DNSServiceFlags flags,
|
||||
uint32_t interfaceIndex,
|
||||
DNSServiceErrorType errorCode,
|
||||
const char *fullname,
|
||||
uint16_t rrtype,
|
||||
uint16_t rrclass,
|
||||
uint16_t rdlen,
|
||||
const void *rdata,
|
||||
uint32_t ttl,
|
||||
void *context ) {
|
||||
assert( rdlen == 4 );
|
||||
const byte *ip = (const byte *)rdata;
|
||||
char interfaceName[IF_NAMESIZE];
|
||||
if_indextoname( interfaceIndex, interfaceName );
|
||||
printf( "DNSServiceQueryRecordReplyCallback: %s, interface[%i] = %s, [%i] = %i.%i.%i.%i\n",
|
||||
fullname, interfaceIndex, interfaceName, rdlen, ip[0], ip[1], ip[2], ip[3] );
|
||||
|
||||
ReportNetworkInterfaces();
|
||||
|
||||
memset( &resolvedServerAddress, 0, sizeof( resolvedServerAddress ) );
|
||||
struct sockaddr_in *sin = (struct sockaddr_in *)&resolvedServerAddress;
|
||||
sin->sin_len = sizeof( resolvedServerAddress );
|
||||
sin->sin_family = AF_INET;
|
||||
sin->sin_port = htons( DOOM_PORT );
|
||||
memcpy( &sin->sin_addr, ip, 4 );
|
||||
|
||||
gotServerAddress = true;
|
||||
}
|
||||
|
||||
|
||||
DNSServiceFlags callbackFlags;
|
||||
|
||||
void DNSServiceResolveReplyCallback (
|
||||
DNSServiceRef sdRef,
|
||||
DNSServiceFlags flags,
|
||||
uint32_t interfaceIndex,
|
||||
DNSServiceErrorType errorCode,
|
||||
const char *fullname,
|
||||
const char *hosttarget,
|
||||
uint16_t port,
|
||||
uint16_t txtLen,
|
||||
const unsigned char *txtRecord,
|
||||
void *context ) {
|
||||
char interfaceName[IF_NAMESIZE];
|
||||
if_indextoname( interfaceIndex, interfaceName );
|
||||
printf( "Resolve: interfaceIndex [%i]=%s : %s @ %s\n", interfaceIndex, interfaceName, fullname, hosttarget );
|
||||
callbackFlags = flags;
|
||||
|
||||
#if 0
|
||||
struct hostent * host = gethostbyname( hosttarget );
|
||||
if ( host ) {
|
||||
printf( "h_name: %s\n", host->h_name );
|
||||
if ( host->h_aliases ) { // this can be NULL
|
||||
for ( char **list = host->h_aliases ; *list ; list++ ) {
|
||||
printf( "h_alias: %s\n", *list );
|
||||
}
|
||||
}
|
||||
printf( "h_addrtype: %i\n", host->h_addrtype );
|
||||
printf( "h_length: %i\n", host->h_length );
|
||||
if ( !host->h_addr_list ) { // I doubt this would ever be NULL...
|
||||
return;
|
||||
}
|
||||
for ( char **list = host->h_addr_list ; *list ; list++ ) {
|
||||
printf( "addr: %i.%i.%i.%i\n", ((byte *)*list)[0], ((byte *)*list)[1], ((byte *)*list)[2], ((byte *)*list)[3] );
|
||||
}
|
||||
|
||||
memset( &resolvedServerAddress, 0, sizeof( resolvedServerAddress ) );
|
||||
resolvedServerAddress.sin_len = sizeof( resolvedServerAddress );
|
||||
resolvedServerAddress.sin_family = host->h_addrtype;
|
||||
resolvedServerAddress.sin_port = htons( DOOM_PORT );
|
||||
assert( host->h_length == 4 );
|
||||
memcpy( &resolvedServerAddress.sin_addr, *host->h_addr_list, host->h_length );
|
||||
|
||||
gotServerAddress = true;
|
||||
}
|
||||
#else
|
||||
DNSServiceRef queryRef;
|
||||
|
||||
// look up the name for this host
|
||||
DNSServiceErrorType err = DNSServiceQueryRecord (
|
||||
&queryRef,
|
||||
kDNSServiceFlagsForceMulticast,
|
||||
interfaceIndex,
|
||||
hosttarget,
|
||||
kDNSServiceType_A, // we want the host address
|
||||
kDNSServiceClass_IN,
|
||||
DNSServiceQueryRecordReplyCallback,
|
||||
NULL /* may be NULL */
|
||||
);
|
||||
if ( err != kDNSServiceErr_NoError ) {
|
||||
printf( "DNSServiceQueryRecord error\n" );
|
||||
} else {
|
||||
// block until we get a response, process it, and run the callback
|
||||
err = DNSServiceProcessResult( queryRef );
|
||||
if ( err != kDNSServiceErr_NoError ) {
|
||||
printf( "DNSServiceProcessResult error\n" );
|
||||
}
|
||||
DNSServiceRefDeallocate( queryRef );
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
boolean NetworkServerAvailable() {
|
||||
for ( int i = 0 ; i < MAX_SERVICE_INTEFACES ; i++ ) {
|
||||
if ( serviceInterfaces[i].interfaceIndex != 0 ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// returns "WiFi", "BlueTooth", or "" for display on the
|
||||
// main menu multiplayer icon
|
||||
const char *NetworkServerTransport() {
|
||||
int count = 0;
|
||||
for ( int i = 0 ; i < MAX_SERVICE_INTEFACES ; i++ ) {
|
||||
if ( serviceInterfaces[i].interfaceIndex != 0 ) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
static char str[1024];
|
||||
|
||||
str[0] = 0;
|
||||
for ( int i = 0 ; i < MAX_SERVICE_INTEFACES ; i++ ) {
|
||||
int index = serviceInterfaces[i].interfaceIndex;
|
||||
if ( index == 0 ) {
|
||||
continue;
|
||||
}
|
||||
if ( str[0] ) {
|
||||
strcat( str, "+" );
|
||||
}
|
||||
if ( index == -1 ) {
|
||||
strcat( str, "BT-NEW" );
|
||||
} else if ( index == 1 ) {
|
||||
strcat( str, "LOOP" ); // we should never see this!
|
||||
} else if ( index == 2 ) {
|
||||
strcat( str, "WiFi" );
|
||||
} else {
|
||||
strcat( str, "BT-EST" );
|
||||
}
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
|
||||
|
||||
boolean ResolveNetworkServer( struct sockaddr *addr ) {
|
||||
if ( !NetworkServerAvailable() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
gotServerAddress = false;
|
||||
|
||||
DNSServiceRef resolveRef;
|
||||
|
||||
// An unconnected bluetooth service will report an interfaceIndex of -1, so if
|
||||
// we have a wifi link with an interfaceIndex > 0, use that
|
||||
// explicitly.
|
||||
service_t *service = NULL;
|
||||
for ( int i = 0 ; i < MAX_SERVICE_INTEFACES ; i++ ) {
|
||||
if ( serviceInterfaces[i].interfaceIndex > 0 ) {
|
||||
service = &serviceInterfaces[i];
|
||||
char interfaceName[IF_NAMESIZE];
|
||||
if_indextoname( service->interfaceIndex, interfaceName );
|
||||
printf( "explicitly resolving server on interface %i = %s\n", service->interfaceIndex, interfaceName );
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ( !service ) {
|
||||
#if 0 // don't support bluetooth now
|
||||
// settle for the unconnected bluetooth service
|
||||
for ( int i = 0 ; i < MAX_SERVICE_INTEFACES ; i++ ) {
|
||||
if ( serviceInterfaces[i].interfaceIndex != 0 ) {
|
||||
service = &serviceInterfaces[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if ( !service ) {
|
||||
printf( "No serviceInterface current.\n" );
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// look up the name for this service
|
||||
|
||||
DNSServiceErrorType err = DNSServiceResolve (
|
||||
&resolveRef,
|
||||
kDNSServiceFlagsForceMulticast, // always on local link
|
||||
service->interfaceIndex > 0 ? service->interfaceIndex : 0, // don't use -1 for bluetooth
|
||||
service->browseName,
|
||||
service->browseRegtype,
|
||||
service->browseDomain,
|
||||
DNSServiceResolveReplyCallback,
|
||||
NULL /* context */
|
||||
);
|
||||
|
||||
if ( err != kDNSServiceErr_NoError ) {
|
||||
printf( "DNSServiceResolve error\n" );
|
||||
} else {
|
||||
// We can get two callbacks when both wifi and bluetooth are enabled
|
||||
callbackFlags = 0;
|
||||
do {
|
||||
err = DNSServiceProcessResult( resolveRef );
|
||||
if ( err != kDNSServiceErr_NoError ) {
|
||||
printf( "DNSServiceProcessResult error\n" );
|
||||
}
|
||||
} while ( callbackFlags & kDNSServiceFlagsMoreComing );
|
||||
DNSServiceRefDeallocate( resolveRef );
|
||||
}
|
||||
|
||||
if ( gotServerAddress ) {
|
||||
*addr = resolvedServerAddress;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void DNSServiceBrowseReplyCallback(
|
||||
DNSServiceRef sdRef,
|
||||
DNSServiceFlags flags,
|
||||
uint32_t interfaceIndex,
|
||||
DNSServiceErrorType errorCode,
|
||||
const char *serviceName,
|
||||
const char *regtype,
|
||||
const char *replyDomain,
|
||||
void *context ) {
|
||||
printf( "DNSServiceBrowseReplyCallback %s: interface:%i name:%s regtype:%s domain:%s\n",
|
||||
(flags & kDNSServiceFlagsAdd) ? "ADD" : "REMOVE",
|
||||
interfaceIndex, serviceName, regtype, replyDomain );
|
||||
if ( flags & kDNSServiceFlagsAdd ) {
|
||||
// add it to the list
|
||||
if ( interfaceIndex == 1 ) {
|
||||
printf( "Not adding service on loopback interface.\n" );
|
||||
} else {
|
||||
for ( int i = 0 ; i < MAX_SERVICE_INTEFACES ; i++ ) {
|
||||
service_t *service = &serviceInterfaces[i];
|
||||
if ( service->interfaceIndex == 0 ) {
|
||||
strncpy( service->browseName, serviceName, sizeof( service->browseName ) -1 );
|
||||
strncpy( service->browseRegtype, regtype, sizeof( service->browseRegtype ) -1 );
|
||||
strncpy( service->browseDomain, replyDomain, sizeof( service->browseDomain ) -1 );
|
||||
service->interfaceIndex = interfaceIndex;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// remove it from the list
|
||||
for ( int i = 0 ; i < MAX_SERVICE_INTEFACES ; i++ ) {
|
||||
if ( serviceInterfaces[i].interfaceIndex == interfaceIndex ) {
|
||||
serviceInterfaces[i].interfaceIndex = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ProcessDNSMessages() {
|
||||
static boolean initialized;
|
||||
|
||||
if ( !initialized ) {
|
||||
initialized = true;
|
||||
DNSServiceErrorType err = DNSServiceBrowse (
|
||||
&browseRef,
|
||||
0, /* flags */
|
||||
0, /* interface */
|
||||
serviceName,
|
||||
NULL, /* domain */
|
||||
DNSServiceBrowseReplyCallback,
|
||||
NULL /* context */
|
||||
);
|
||||
if ( err != kDNSServiceErr_NoError ) {
|
||||
printf( "DNSServiceBrowse error\n" );
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// poll the socket for updates
|
||||
int socket = DNSServiceRefSockFD( browseRef );
|
||||
if ( socket <= 0 ) {
|
||||
return;
|
||||
}
|
||||
fd_set set;
|
||||
FD_ZERO( &set );
|
||||
FD_SET( socket, &set );
|
||||
|
||||
struct timeval tv;
|
||||
memset( &tv, 0, sizeof( tv ) );
|
||||
if ( select( socket+1, &set, NULL, NULL, &tv ) > 0 ) {
|
||||
DNSServiceProcessResult( browseRef );
|
||||
}
|
||||
}
|
||||
|
||||
void ReportNetworkInterfaces() {
|
||||
struct ifaddrs *ifap;
|
||||
printf( "getifaddrs():\n" );
|
||||
if ( getifaddrs( &ifap ) == -1 ) {
|
||||
perror( "getifaddrs(): " );
|
||||
} else {
|
||||
for ( struct ifaddrs *ifa = ifap ; ifa ; ifa = ifa->ifa_next ) {
|
||||
struct sockaddr_in *ina = (struct sockaddr_in *)ifa->ifa_addr;
|
||||
if ( ina->sin_family == AF_INET ) {
|
||||
byte *ip = (byte *)&ina->sin_addr;
|
||||
printf( "ifa_name: %s ifa_flags: %i sa_family: %i=AF_INET ip: %i.%i.%i.%i\n", ifa->ifa_name, ifa->ifa_flags,
|
||||
ina->sin_family, ip[0], ip[1], ip[2], ip[3] );
|
||||
} else if ( ina->sin_family == AF_LINK ) {
|
||||
struct if_data *data = (struct if_data *)ifa->ifa_data;
|
||||
printf( "ifa_name: %s ifa_flags: %i sa_family: %i=AF_LINK ifi_ipackets: %i\n", ifa->ifa_name, ifa->ifa_flags,
|
||||
ina->sin_family, data->ifi_ipackets );
|
||||
} else {
|
||||
printf( "ifa_name: %s ifa_flags: %i sa_family: %i=???\n", ifa->ifa_name, ifa->ifa_flags,
|
||||
ina->sin_family );
|
||||
}
|
||||
}
|
||||
freeifaddrs( ifap );
|
||||
}
|
||||
|
||||
printf( "if_nameindex():\n" );
|
||||
struct if_nameindex *ifnames = if_nameindex();
|
||||
if ( !ifnames ) {
|
||||
perror( "if_ameindex():" );
|
||||
} else {
|
||||
for ( int i = 0 ; ifnames[i].if_index != 0 ; i++ ) {
|
||||
printf( "%i : %s\n", ifnames[i].if_index, ifnames[i].if_name );
|
||||
}
|
||||
if_freenameindex( ifnames );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
boolean NetworkAvailable() {
|
||||
struct ifaddrs *ifap;
|
||||
if ( getifaddrs( &ifap ) == -1 ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// We can't tell if bluetooth is available from here, because
|
||||
// the interface doesn't appear until after the service is found,
|
||||
// but I decided not to support bluetooth for now due to the poor performance.
|
||||
boolean goodInterface = false;
|
||||
|
||||
for ( struct ifaddrs *ifa = ifap ; ifa ; ifa = ifa->ifa_next ) {
|
||||
struct sockaddr_in *ina = (struct sockaddr_in *)ifa->ifa_addr;
|
||||
if ( ina->sin_family == AF_INET ) {
|
||||
if ( !strcmp( ifa->ifa_name, "en0" ) ) {
|
||||
goodInterface = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
freeifaddrs( ifap );
|
||||
|
||||
return goodInterface;
|
||||
}
|
||||
|
||||
int InterfaceIndexForAddress( struct sockaddr_in *adr ) {
|
||||
// FIXME: compare against getifaddrs
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct sockaddr_in AddressForInterfaceName( const char *ifname ) {
|
||||
struct sockaddr_in s;
|
||||
memset( &s, 0, sizeof( s ) );
|
||||
|
||||
struct ifaddrs *ifap;
|
||||
if ( getifaddrs( &ifap ) == -1 ) {
|
||||
perror( "getifaddrs()" );
|
||||
return s;
|
||||
}
|
||||
|
||||
struct ifaddrs *ifa;
|
||||
for ( ifa = ifap ; ifa ; ifa = ifa->ifa_next ) {
|
||||
struct sockaddr_in *ina = (struct sockaddr_in *)ifa->ifa_addr;
|
||||
if ( ina->sin_family == AF_INET && !strcmp( ifa->ifa_name, ifname ) ) {
|
||||
byte *ip = (byte *)&ina->sin_addr;
|
||||
printf( "AddressForInterfaceName( %s ) = ifa_name: %s ifa_flags: %i sa_family: %i=AF_INET ip: %i.%i.%i.%i\n",
|
||||
ifname, ifa->ifa_name, ifa->ifa_flags,
|
||||
ina->sin_family, ip[0], ip[1], ip[2], ip[3] );
|
||||
freeifaddrs( ifap );
|
||||
return *ina;
|
||||
}
|
||||
}
|
||||
freeifaddrs( ifap );
|
||||
printf( "AddressForInterfaceName( %s ): Couldn't find IP address\n", ifname );
|
||||
return s;
|
||||
}
|
||||
|
||||
int InterfaceIndexForName( const char *ifname ) {
|
||||
struct if_nameindex *ifnames = if_nameindex();
|
||||
if ( !ifnames ) {
|
||||
perror( "if_nameindex()" );
|
||||
return 0;
|
||||
}
|
||||
for ( int i = 0 ; ifnames[i].if_index != 0 ; i++ ) {
|
||||
if ( !strcmp( ifname, ifnames[i].if_name ) ) {
|
||||
int index = ifnames[i].if_index;
|
||||
if_freenameindex( ifnames );
|
||||
return index;
|
||||
}
|
||||
}
|
||||
printf( "InterfaceIndexForName( %s ): Couldn't find interface\n", ifname );
|
||||
if_freenameindex( ifnames );
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct sockaddr_in AddressForInterfaceIndex( int interfaceIndex ) {
|
||||
struct sockaddr_in addr;
|
||||
|
||||
memset( &addr, 0, sizeof( addr ) );
|
||||
addr.sin_len = sizeof( addr );
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_addr.s_addr = INADDR_ANY;
|
||||
|
||||
if ( interfaceIndex == 0 ) {
|
||||
return addr;
|
||||
}
|
||||
|
||||
struct if_nameindex *ifnames = if_nameindex();
|
||||
if ( !ifnames ) {
|
||||
perror( "if_ameindex()" );
|
||||
return addr;
|
||||
}
|
||||
for ( int i = 0 ; ifnames[i].if_index != 0 ; i++ ) {
|
||||
if ( ifnames[i].if_index == interfaceIndex ) {
|
||||
addr = AddressForInterfaceName( ifnames[i].if_name );
|
||||
if_freenameindex( ifnames );
|
||||
return addr;
|
||||
}
|
||||
}
|
||||
printf( "AddressForInterfaceIndex( %i ): Couldn't find interface\n", interfaceIndex );
|
||||
if_freenameindex( ifnames );
|
||||
return addr;
|
||||
}
|
||||
|
||||
struct sockaddr_in gameSocketAddress;
|
||||
|
||||
int UDPSocket( const char *interfaceName, int portnum ) {
|
||||
int udpSocket = socket( AF_INET, SOCK_DGRAM, IPPROTO_UDP );
|
||||
if ( udpSocket == -1 ) {
|
||||
Com_Printf( "UDP socket failed: %s\n", strerror( errno ) );
|
||||
return -1;
|
||||
}
|
||||
struct sockaddr_in addr = AddressForInterfaceName( interfaceName );
|
||||
addr.sin_port = htons( portnum );
|
||||
byte *ip = (byte *)&addr.sin_addr;
|
||||
gameSocketAddress = addr;
|
||||
Com_Printf( "UDPSocket( %s, %i ) = %i.%i.%i.%i\n", interfaceName, portnum,
|
||||
ip[0], ip[1], ip[2], ip[3] );
|
||||
if ( bind( udpSocket, (struct sockaddr *)&addr, sizeof( addr ) ) == -1 ) {
|
||||
Com_Printf( "UDP bind failed: %s\n", strerror( errno ) );
|
||||
close( udpSocket );
|
||||
return -1;
|
||||
}
|
||||
|
||||
#if 0
|
||||
// enable broadcast
|
||||
int on = 1;
|
||||
if ( setsockopt( udpSocket, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on) ) == -1 ) {
|
||||
Com_Printf( "UDP setsockopt failed: %s\n", strerror( errno ) );
|
||||
close( udpSocket );
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
// set the type-of-service, in hopes that the link level drivers use it
|
||||
// to stop buffering huge amounts of data when there are line errors
|
||||
int tos = 0x10; /* IPTOS_LOWDELAY; */ /* see <netinet/in.h> */
|
||||
if ( setsockopt( udpSocket, IPPROTO_IP, IP_TOS, &tos, sizeof(tos) ) == -1 ) {
|
||||
Com_Printf( "setsockopt IP_TOS failed: %s\n", strerror( errno ) );
|
||||
}
|
||||
#endif
|
||||
|
||||
// enable non-blocking IO
|
||||
if ( fcntl( udpSocket, F_SETFL, O_NONBLOCK ) == -1 ) {
|
||||
Com_Printf( "UDP fcntl failed: %s\n", strerror( errno ) );
|
||||
close( udpSocket );
|
||||
return -1;
|
||||
}
|
||||
|
||||
return udpSocket;
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
|
||||
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_QGL_ENUMERANTS_H
|
||||
#define IPHONE_QGL_ENUMERANTS_H
|
||||
|
||||
#ifdef QGL_LOG_GL_CALLS
|
||||
|
||||
#include <OpenGLES/ES1/gl.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
const char *StringFromGLEnumerant( GLenum enumerant );
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // QGL_LOG_GL_CALLS
|
||||
|
||||
#endif // IPHONE_QGL_ENUMERANTS_H
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,304 @@
|
||||
/*
|
||||
* iphone_sound.c
|
||||
* doom
|
||||
*
|
||||
* Created by John Carmack on 4/16/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.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#include "../doomiphone.h"
|
||||
#import <AudioToolbox/AudioServices.h>
|
||||
|
||||
|
||||
typedef struct {
|
||||
unsigned sourceName; // OpenAL sourceName
|
||||
pkWav_t *sfx; // NULL if unused
|
||||
float volume; // stored for showSound display
|
||||
} channel_t;
|
||||
|
||||
static ALCcontext *Context;
|
||||
static ALCdevice *Device;
|
||||
|
||||
#define MAX_CHANNELS 16
|
||||
static channel_t s_channels[ MAX_CHANNELS ];
|
||||
|
||||
cvar_t *s_sfxVolume;
|
||||
|
||||
void Sound_StartLocalSound( const char *filename ) {
|
||||
Sound_StartLocalSoundAtVolume( filename, 1.0f );
|
||||
}
|
||||
|
||||
void Sound_StartLocalSoundAtVolume( const char *filename, float volume ) {
|
||||
pkWav_t *sfx;
|
||||
|
||||
sfx = PK_FindWav( filename );
|
||||
if( ! sfx ) {
|
||||
Com_Printf( "Sound_StartLocalSound: could not cache (%s)\n", filename );
|
||||
return;
|
||||
}
|
||||
// printf( "sound:%s\n", filename );
|
||||
// channel 0 is reserved for UI sounds, the other channels
|
||||
// are for DOOM sounds
|
||||
channel_t *ch = &s_channels[ 0 ];
|
||||
|
||||
ch->sfx = sfx;
|
||||
ch->volume = s_sfxVolume->value * volume;
|
||||
|
||||
alSourceStop( ch->sourceName );
|
||||
alSourcef( ch->sourceName, AL_GAIN, ch->volume );
|
||||
alSourcei( ch->sourceName, AL_BUFFER, sfx->alBufferNum );
|
||||
alSourcef( ch->sourceName, AL_PITCH, 1.0f );
|
||||
alSourcePlay( ch->sourceName );
|
||||
}
|
||||
|
||||
|
||||
static void Sound_Play_f( void ) {
|
||||
if( Cmd_Argc() == 1 ) {
|
||||
Com_Printf( "Usage: play <soundfile>\n" );
|
||||
return;
|
||||
}
|
||||
Sound_StartLocalSound( Cmd_Argv( 1 ) );
|
||||
}
|
||||
|
||||
// we won't allow music to be toggled on or off in the menu when this is true
|
||||
int otherAudioIsPlaying;
|
||||
|
||||
int SysIPhoneOtherAudioIsPlaying() {
|
||||
return otherAudioIsPlaying;
|
||||
}
|
||||
|
||||
void interruptionListener( void *inUserData, UInt32 inInterruption)
|
||||
{
|
||||
printf("Session interrupted! --- %s ---\n", inInterruption == kAudioSessionBeginInterruption ? "Begin Interruption" : "End Interruption");
|
||||
|
||||
if ( inInterruption == kAudioSessionBeginInterruption ) {
|
||||
printf("Audio interrupted.\n" );
|
||||
iphonePauseMusic();
|
||||
alcMakeContextCurrent( NULL );
|
||||
AudioSessionSetActive( false );
|
||||
} else if ( inInterruption == kAudioSessionEndInterruption ) {
|
||||
printf("Audio restored.\n" );
|
||||
|
||||
OSStatus r = AudioSessionSetActive( true );
|
||||
if ( r != kAudioSessionNoError ) {
|
||||
printf( "AudioSessionSetActive( true ) failed: 0x%x\n", r );
|
||||
} else {
|
||||
printf( "AudioSessionSetActive( true ) succeeded.\n" );
|
||||
}
|
||||
alcMakeContextCurrent( Context );
|
||||
if( alcGetError( Device ) != ALC_NO_ERROR ) {
|
||||
Com_Error( "Failed to alcMakeContextCurrent\n" );
|
||||
}
|
||||
iphoneResumeMusic();
|
||||
}
|
||||
}
|
||||
|
||||
void Sound_Init( void ) {
|
||||
|
||||
Com_Printf( "\n------- Sound Initialization -------\n" );
|
||||
|
||||
s_sfxVolume = Cvar_Get( "s_sfxVolume", "1.0", 0 );
|
||||
|
||||
Cmd_AddCommand( "play", Sound_Play_f );
|
||||
|
||||
// make sure background ipod music mixes with our sound effects
|
||||
Com_Printf( "...Initializing AudioSession\n" );
|
||||
OSStatus status = 0;
|
||||
status = AudioSessionInitialize(NULL, NULL, interruptionListener, NULL); // else "couldn't initialize audio session"
|
||||
|
||||
// if there is iPod music playing in the background, we want to use
|
||||
// the AmbientSound catagory, otherwise we will leave it at the default.
|
||||
// If we always set it to AmbientSound, then the mp3 background music
|
||||
// playback goes to software on 3.0 for a huge slowdown.
|
||||
UInt32 propOtherAudioIsPlaying = 'othr'; // kAudioSessionProperty_OtherAudioIsPlaying
|
||||
UInt32 size = sizeof( otherAudioIsPlaying );
|
||||
AudioSessionGetProperty( propOtherAudioIsPlaying, &size, &otherAudioIsPlaying );
|
||||
Com_Printf("OtherAudioIsPlaying = %d\n", otherAudioIsPlaying );
|
||||
|
||||
if ( otherAudioIsPlaying ) {
|
||||
UInt32 audioCategory = kAudioSessionCategory_AmbientSound;
|
||||
status = AudioSessionSetProperty(kAudioSessionProperty_AudioCategory, sizeof(audioCategory), &audioCategory);
|
||||
}
|
||||
|
||||
status = AudioSessionSetActive(true); // else "couldn't set audio session active\n"
|
||||
|
||||
Com_Printf( "...Initializing OpenAL subsystem\n" );
|
||||
|
||||
// get the OpenAL device
|
||||
Device = alcOpenDevice( NULL );
|
||||
if( Device == NULL ) {
|
||||
Com_Printf( "Failed to alcOpenDevice\n" );
|
||||
}
|
||||
|
||||
// set the mixer output rate lower, so we don't waste time doing 44khz
|
||||
// must be done before the context is created!
|
||||
extern ALvoid alcMacOSXMixerOutputRate(const ALdouble value);
|
||||
alcMacOSXMixerOutputRate( 22050 );
|
||||
|
||||
// Create context(s)
|
||||
Context = alcCreateContext( Device, NULL );
|
||||
if( Context == NULL ) {
|
||||
Com_Error( "Failed to alcCreateContext\n" );
|
||||
}
|
||||
|
||||
// Set active context
|
||||
alcGetError( Device );
|
||||
alcMakeContextCurrent( Context );
|
||||
if( alcGetError( Device ) != ALC_NO_ERROR ) {
|
||||
Com_Error( "Failed to alcMakeContextCurrent\n" );
|
||||
}
|
||||
|
||||
// allocate all the channels we are going to use
|
||||
channel_t *ch;
|
||||
int i;
|
||||
for( i = 0, ch = s_channels ; i < MAX_CHANNELS ; ++i, ++ch ) {
|
||||
alGenSources( 1, &ch->sourceName );
|
||||
|
||||
if( alGetError() != AL_NO_ERROR ) {
|
||||
Com_Error( "Allocating AL sound sources" );
|
||||
}
|
||||
alSourcei( ch->sourceName, AL_SOURCE_RELATIVE, AL_FALSE );
|
||||
}
|
||||
|
||||
Com_Printf( "------------------------------------\n" );
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
ShowSound
|
||||
|
||||
Display active sound channels
|
||||
==================
|
||||
*/
|
||||
void ShowSound() {
|
||||
|
||||
if ( !showSound->value ) {
|
||||
return;
|
||||
}
|
||||
channel_t *ch;
|
||||
int i;
|
||||
for( i = 0, ch = s_channels ; i < MAX_CHANNELS ; ++i, ++ch ) {
|
||||
int state;
|
||||
alGetSourcei( ch->sourceName, AL_SOURCE_STATE, &state );
|
||||
if ( state != AL_PLAYING ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int v = ch->volume * 255;
|
||||
if ( v > 255 ) {
|
||||
v = 255;
|
||||
}
|
||||
color4_t color = { v, v, v, 255 };
|
||||
R_Draw_Fill( i*16, 0, 12, 12, color );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
==================================================================
|
||||
|
||||
PrBoom interface
|
||||
|
||||
==================================================================
|
||||
*/
|
||||
|
||||
// Init at program start...
|
||||
void I_InitSound(void) {}
|
||||
|
||||
// ... shut down and relase at program termination.
|
||||
void I_ShutdownSound(void) {}
|
||||
|
||||
// Initialize channels?
|
||||
void I_SetChannels(void) {}
|
||||
|
||||
// Get raw data lump index for sound descriptor.
|
||||
int I_GetSfxLumpNum (sfxinfo_t *sfx) {
|
||||
// find the pkWav_t for this sfxinfo
|
||||
char upper[16], *d = upper;
|
||||
for ( const char *c = sfx->name ; *c ; c++ ) {
|
||||
*d++ = toupper( *c );
|
||||
}
|
||||
*d = 0;
|
||||
pkWav_t *pkwav = PK_FindWav( va( "newsfx/DS%s.wav", upper ) );
|
||||
|
||||
return pkwav - pkWavs;
|
||||
}
|
||||
|
||||
// Starts a sound in a particular sound channel.
|
||||
// volume ranges 0 - 64
|
||||
// seperation tanges is 128 straight ahead, 0 = all left ear, 255 = all right ear
|
||||
// pitch centers around 128
|
||||
int I_StartSound(int sfx_id, int channel, int vol, int sep, int pitch, int priority) {
|
||||
|
||||
sfxinfo_t *dsfx = &S_sfx[sfx_id];
|
||||
|
||||
assert( dsfx->lumpnum >= 0 && dsfx->lumpnum < pkHeader->wavs.count );
|
||||
|
||||
pkWav_t *sfx = &pkWavs[dsfx->lumpnum];
|
||||
// printf( "sound: %s chan:%i vol:%i sep:%i pitch:%i priority:%i\n", sfx->wavData->name.name, channel, vol, sep, pitch, priority );
|
||||
|
||||
assert( channel >= 0 && channel < MAX_CHANNELS - 1 );
|
||||
channel_t *ch = &s_channels[ 1+channel ];
|
||||
|
||||
alSourceStop( ch->sourceName );
|
||||
if ( ch->sfx == sfx ) {
|
||||
// restarting the same sound
|
||||
alSourceRewind( ch->sourceName );
|
||||
} else {
|
||||
alSourcei( ch->sourceName, AL_BUFFER, sfx->alBufferNum );
|
||||
}
|
||||
|
||||
ch->sfx = sfx;
|
||||
ch->volume = s_sfxVolume->value * vol / 64.0;
|
||||
alSourcef( ch->sourceName, AL_GAIN, ch->volume );
|
||||
alSourcef( ch->sourceName, AL_PITCH, pitch / 128.0f );
|
||||
alSourcePlay( ch->sourceName );
|
||||
|
||||
return (int)ch;
|
||||
}
|
||||
|
||||
// Stops a sound channel.
|
||||
void I_StopSound(int handle) {}
|
||||
|
||||
// Called by S_*() functions
|
||||
// to see if a channel is still playing.
|
||||
// Returns 0 if no longer playing, 1 if playing.
|
||||
boolean I_SoundIsPlaying(int handle) {
|
||||
|
||||
channel_t *ch = (channel_t *)handle;
|
||||
if ( !ch ) {
|
||||
return false;
|
||||
}
|
||||
int state;
|
||||
alGetSourcei( ch->sourceName, AL_SOURCE_STATE, &state );
|
||||
|
||||
return state == AL_PLAYING;
|
||||
}
|
||||
|
||||
// Called by m_menu.c to let the quit sound play and quit right after it stops
|
||||
boolean I_AnySoundStillPlaying(void) { return false; }
|
||||
|
||||
// Updates the volume, separation,
|
||||
// and pitch of a sound channel.
|
||||
void I_UpdateSoundParams(int handle, int vol, int sep, int pitch) {}
|
||||
@@ -0,0 +1,231 @@
|
||||
/*
|
||||
* iphone_start.c
|
||||
* doom
|
||||
*
|
||||
* Created by John Carmack on 7/7/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.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#include "../doomiphone.h"
|
||||
|
||||
|
||||
/*
|
||||
==================
|
||||
ResumeGame
|
||||
|
||||
==================
|
||||
*/
|
||||
void ResumeGame() {
|
||||
if ( levelHasBeenLoaded && !demoplayback ) {
|
||||
// return to the already started game
|
||||
iphoneResumeMusic();
|
||||
drawWeaponSelect = false;
|
||||
weaponSelected = -1;
|
||||
automapmode = 0;
|
||||
advancedemo = false;
|
||||
menuState = IPM_GAME;
|
||||
return;
|
||||
}
|
||||
|
||||
if ( !playState.saveGameIsValid ) {
|
||||
// they hit "resume game" on the first app lounch, so just start E1M1
|
||||
mapStart_t map;
|
||||
map.skill = 1;
|
||||
map.episode = 1;
|
||||
map.map = 1;
|
||||
StartSinglePlayerGame( map );
|
||||
} else {
|
||||
StartSaveGame();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
==================
|
||||
GameSetup
|
||||
|
||||
==================
|
||||
*/
|
||||
void GameSetup() {
|
||||
// resume game just goes back to playing, it doesn't load anything
|
||||
levelHasBeenLoaded = true;
|
||||
|
||||
// make sure doom doesn't cycle to the next demo loop and kill the new game
|
||||
advancedemo = false;
|
||||
|
||||
// not in a timedemo yet
|
||||
timeDemoStart = 0;
|
||||
iphoneTimeDemo = false;
|
||||
|
||||
// display the game next time through the main loop
|
||||
drawWeaponSelect = false;
|
||||
weaponSelected = -1;
|
||||
automapmode = 0;
|
||||
menuState = IPM_GAME;
|
||||
|
||||
demoplayback = false;
|
||||
|
||||
levelTimer = false;
|
||||
levelFragLimit = false;
|
||||
|
||||
// single player game
|
||||
netgame = false;
|
||||
deathmatch = false;
|
||||
nomonsters = false;
|
||||
memset( playeringame, 0, sizeof( playeringame ) );
|
||||
consoleplayer = 0;
|
||||
displayplayer = 0;
|
||||
playeringame[consoleplayer] = 1;
|
||||
}
|
||||
|
||||
/*
|
||||
=======================
|
||||
StartSaveGame
|
||||
|
||||
Can be called by both the resume game button after launch, or the
|
||||
load game button after a player death
|
||||
=======================
|
||||
*/
|
||||
void StartSaveGame() {
|
||||
GameSetup();
|
||||
G_LoadGame( 0, true );
|
||||
G_DoLoadGame();
|
||||
}
|
||||
|
||||
/*
|
||||
=======================
|
||||
StartSinglePlayerGame
|
||||
|
||||
=======================
|
||||
*/
|
||||
void StartSinglePlayerGame( mapStart_t map ) {
|
||||
playState.map = map;
|
||||
playState.saveGameIsValid = true; // assume we will save the game on exit
|
||||
|
||||
// mark this level / skill combination as tried
|
||||
//
|
||||
mapStats_t *cms = FindMapStats( playState.map.dataset, playState.map.episode, playState.map.map, true );
|
||||
if ( cms ) {
|
||||
// if we are at MAX_MAPS, no stat tracking...
|
||||
cms->completionFlags[playState.map.skill] |= MF_TRIED;
|
||||
}
|
||||
|
||||
GameSetup();
|
||||
|
||||
// start the map
|
||||
G_InitNew( playState.map.skill, playState.map.episode, playState.map.map );
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
=======================
|
||||
StartNetGame
|
||||
|
||||
Begins a game based on the contents of setupPacket
|
||||
=======================
|
||||
*/
|
||||
boolean StartNetGame() {
|
||||
// make sure we are supposed to be in this game
|
||||
int slot = -1;
|
||||
for ( int i = 0 ; i < MAXPLAYERS ; i++ ) {
|
||||
if ( setupPacket.playerID[i] == playerID ) {
|
||||
slot = i;
|
||||
}
|
||||
}
|
||||
if ( slot == -1 ) {
|
||||
return false;
|
||||
}
|
||||
GameSetup();
|
||||
|
||||
consoleplayer = displayplayer = slot;
|
||||
|
||||
netgame = true; // respawn without restarting levels
|
||||
|
||||
if ( setupPacket.deathmatch ) {
|
||||
// deathmatch game
|
||||
deathmatch = setupPacket.deathmatch; // could be either 1 or 2 for altdeath
|
||||
nomonsters = true;
|
||||
|
||||
if ( setupPacket.timelimit > 0 ) {
|
||||
levelTimer = true;
|
||||
// 30 hz, minutes
|
||||
levelTimeCount = setupPacket.timelimit * 30 * 60;
|
||||
}
|
||||
|
||||
if ( setupPacket.fraglimit > 0 ) {
|
||||
levelFragLimit = true;
|
||||
levelFragLimitCount = setupPacket.fraglimit;
|
||||
}
|
||||
} else {
|
||||
// coop game
|
||||
deathmatch = false;
|
||||
nomonsters = false;
|
||||
}
|
||||
|
||||
for ( int i = 0 ; i < MAXPLAYERS ; i++ ) {
|
||||
if ( setupPacket.playerID[i] != 0 ) {
|
||||
playeringame[i] = 1;
|
||||
} else {
|
||||
playeringame[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
gametic = 0;
|
||||
maketic = 1; // allow everyone to run the first frame without waiting for a packet
|
||||
|
||||
memset( netcmds, 0, sizeof( netcmds ) );
|
||||
memset( consistancy, 0, sizeof( consistancy ) );
|
||||
|
||||
gameID = setupPacket.gameID;
|
||||
|
||||
// start the map
|
||||
G_InitNew( setupPacket.map.skill, setupPacket.map.episode, setupPacket.map.map );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
=======================
|
||||
StartDemoGame
|
||||
|
||||
The demo button has been hit on the main menu
|
||||
=======================
|
||||
*/
|
||||
void StartDemoGame( boolean timeDemoMode ) {
|
||||
if ( levelHasBeenLoaded && !netgame && !demoplayback && usergame && gamestate == GS_LEVEL ) {
|
||||
// save the current game before starting the demos
|
||||
levelHasBeenLoaded = false;
|
||||
G_SaveGame( 0, "quicksave" );
|
||||
G_DoSaveGame(true);
|
||||
}
|
||||
|
||||
GameSetup();
|
||||
if ( timeDemoMode ) {
|
||||
iphoneTimeDemo = true;
|
||||
}
|
||||
|
||||
// always skip to the next one on each exit from the menu
|
||||
advancedemo = true;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
|
||||
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.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#include "../doomiphone.h"
|
||||
#import <AudioToolbox/AudioServices.h>
|
||||
|
||||
int SysIphoneMicroseconds() {
|
||||
struct timeval tp;
|
||||
struct timezone tzp;
|
||||
static int secbase;
|
||||
|
||||
gettimeofday( &tp, &tzp );
|
||||
|
||||
if( ! secbase ) {
|
||||
secbase = tp.tv_sec;
|
||||
return tp.tv_usec;
|
||||
}
|
||||
|
||||
int curtime = (tp.tv_sec - secbase) * 1000000 + tp.tv_usec;
|
||||
|
||||
return curtime;
|
||||
}
|
||||
|
||||
int SysIphoneMilliseconds() {
|
||||
return SysIphoneMicroseconds()/1000;
|
||||
}
|
||||
|
||||
|
||||
extern char iphoneDocDirectory[1024];
|
||||
extern char iphoneAppDirectory[1024];
|
||||
|
||||
const char *SysIphoneGetAppDir() {
|
||||
return iphoneAppDirectory;
|
||||
}
|
||||
|
||||
const char *SysIphoneGetDocDir() {
|
||||
return iphoneDocDirectory;
|
||||
}
|
||||
|
||||
void SysIPhoneVibrate() {
|
||||
AudioServicesPlaySystemSound( kSystemSoundID_Vibrate );
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,299 @@
|
||||
/*
|
||||
|
||||
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.
|
||||
|
||||
*/
|
||||
|
||||
// define this to get only the first episode on selections, and the
|
||||
// automatic sell screen at the end of episode 1
|
||||
#define EPISODE_ONE_ONLY
|
||||
|
||||
// this is the version number displayed on the menu screen
|
||||
#define DOOM_IPHONE_VERSION 0.1
|
||||
|
||||
// if defined, the game runs in a separate thread from the app event loop
|
||||
#define USE_GAME_THREAD
|
||||
|
||||
typedef enum menuState {
|
||||
IPM_GAME,
|
||||
IPM_MAIN,
|
||||
IPM_MAPS,
|
||||
IPM_MULTIPLAYER,
|
||||
IPM_MASTER,
|
||||
IPM_CONTROLS,
|
||||
IPM_OPTIONS,
|
||||
IPM_HUDEDIT
|
||||
} menuState_t;
|
||||
|
||||
extern menuState_t menuState;
|
||||
|
||||
void iphoneDrawMenus();
|
||||
|
||||
#define VID_WIDTH 480
|
||||
#define VID_HEIGHT 320
|
||||
|
||||
#define MAX_SKILLS 5
|
||||
#define MAX_MAPS 200
|
||||
|
||||
#define MF_TRIED 1
|
||||
#define MF_COMPLETED 2
|
||||
#define MF_KILLS 4
|
||||
#define MF_SECRETS 8
|
||||
#define MF_TREASURE 16
|
||||
#define MF_TIME 32
|
||||
|
||||
// we want to track mapStats for downloaded content, so we
|
||||
// won't have a known number of these
|
||||
typedef struct {
|
||||
int dataset;
|
||||
int episode;
|
||||
int map;
|
||||
|
||||
int completionFlags[MAX_SKILLS];
|
||||
} mapStats_t;
|
||||
|
||||
// this structure is saved out at the head of the binary save file,
|
||||
// and allows all the menus to work without having to load a game save
|
||||
typedef struct {
|
||||
int version;
|
||||
|
||||
int episode;
|
||||
int map;
|
||||
int skill;
|
||||
|
||||
int mapsStarted; // when 0, resume game will just be a new game
|
||||
|
||||
// if someone downloads more than MAX_MAPS, they won't get stat tracking on them.
|
||||
int numMapStats;
|
||||
mapStats_t mapStats[MAX_MAPS];
|
||||
} currentMap_t;
|
||||
|
||||
extern currentMap_t currentMap;
|
||||
|
||||
extern boolean levelHasBeenLoaded; // determines if "resume game" does a loadGame and exiting does a saveGame
|
||||
|
||||
extern pkTexture_t *fontTexture;
|
||||
extern pkTexture_t *numberPics[10];
|
||||
|
||||
extern int iphoneFrameNum;
|
||||
extern int levelLoadFrameNum;
|
||||
extern int consoleActive;
|
||||
|
||||
extern cvar_t *skill;
|
||||
extern cvar_t *episode;
|
||||
extern cvar_t *controlScheme;
|
||||
extern cvar_t *stickMove;
|
||||
extern cvar_t *stickTurn;
|
||||
extern cvar_t *rotorTurn;
|
||||
extern cvar_t *stickDeadBand;
|
||||
extern cvar_t *tiltTurn;
|
||||
extern cvar_t *tiltMove;
|
||||
extern cvar_t *tiltDeadBand;
|
||||
extern cvar_t *tiltAverages;
|
||||
extern cvar_t *music;
|
||||
extern cvar_t *showTilt;
|
||||
extern cvar_t *showTime;
|
||||
extern cvar_t *cropSprites;
|
||||
extern cvar_t *revLand;
|
||||
extern cvar_t *mapScale;
|
||||
extern cvar_t *hideControls;
|
||||
extern cvar_t *tapFire;
|
||||
extern cvar_t *skipSleep;
|
||||
extern cvar_t *autoUse;
|
||||
extern cvar_t *statusBar;
|
||||
extern cvar_t *touchClick;
|
||||
|
||||
extern int numTouches;
|
||||
extern int touches[5][2]; // [0] = x, [1] = y in landscape mode, raster order with y = 0 at top
|
||||
// so we can detect button releases
|
||||
extern int numPrevTouches;
|
||||
extern int prevTouches[5][2];
|
||||
|
||||
extern float tilt; // -1.0 to 1.0
|
||||
extern float tiltPitch;
|
||||
|
||||
extern boolean drawWeaponSelect; // true when the weapon select overlay is up
|
||||
extern int weaponSelected; // -1 for no change
|
||||
|
||||
typedef unsigned char color4_t[4];
|
||||
typedef unsigned char color3_t[3];
|
||||
|
||||
typedef struct {
|
||||
int enterFrame;
|
||||
int beforeSwap;
|
||||
int afterSwap;
|
||||
int afterSleep;
|
||||
} logTime_t;
|
||||
#define MAX_LOGGED_TIMES 512
|
||||
extern logTime_t loggedTimes[MAX_LOGGED_TIMES]; // indexed by iphoneFrameNum
|
||||
|
||||
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 iphoneDrawText( int x, int y, const char *str );
|
||||
int iphoneCenterText( int x, int y, const char *str );
|
||||
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 );
|
||||
void StartGame();
|
||||
void iphoneOpenAutomap();
|
||||
void iphoneDrawNotifyText();
|
||||
void iphoneSet2D( void );
|
||||
bool TextButton( const char *title, int x, int y, int w, int h );
|
||||
|
||||
void R_Draw_Fill( int x, int y, int w, int h, color3_t c );
|
||||
void R_Draw_Blend( int x, int y, int w, int h, color4_t c );
|
||||
|
||||
void InitImmediateModeGL();
|
||||
int iphoneRotateForLandscape();
|
||||
void iphoneCheckForLandscapeReverse();
|
||||
|
||||
void iphonePacifierUpdate();
|
||||
void iphoneDrawScreen();
|
||||
|
||||
extern int damageflash;
|
||||
extern int bonusFrameNum;
|
||||
extern int attackDirTime[2];
|
||||
|
||||
|
||||
#define HF_DISABLED 1
|
||||
#define HF_NODRAW 2 // invisible button
|
||||
|
||||
typedef struct {
|
||||
int x, y; // mdpoint
|
||||
int drawWidth, drawHeight;
|
||||
int touchWidth, touchHeight; // allow touches outside the actual bounds
|
||||
pkTexture_t *texture;
|
||||
boolean drawAsLimit; // draw with red tint to show further movement won't do anything
|
||||
float touchState;
|
||||
float drawState; // offsets for rotors
|
||||
int hudFlags;
|
||||
struct touch_s *touch;
|
||||
} hudPic_t;
|
||||
|
||||
typedef struct {
|
||||
hudPic_t forwardStick;
|
||||
hudPic_t sideStick;
|
||||
hudPic_t turnStick;
|
||||
hudPic_t turnRotor;
|
||||
hudPic_t fire;
|
||||
hudPic_t menu;
|
||||
hudPic_t map;
|
||||
hudPic_t weaponSelect;
|
||||
} hud_t;
|
||||
|
||||
extern hud_t huds;
|
||||
|
||||
void HudSetForScheme( int schemeNum );
|
||||
void HudSetTexnums();
|
||||
void HudEditFrame();
|
||||
|
||||
void Sound_StartLocalSound( const char *sound );
|
||||
void Sound_StartLocalSoundAtVolume( const char *sound, float volume );
|
||||
|
||||
int BackButton();
|
||||
void ResumeGame();
|
||||
|
||||
//---------------------------------------
|
||||
// Touch and button
|
||||
//---------------------------------------
|
||||
|
||||
typedef struct touch_s {
|
||||
boolean down;
|
||||
int x, y;
|
||||
int prevX, prevY; // will be set to x, y on first touch, copied after each game frame
|
||||
int stateCount; // set to 1 on first event that state changes, incremented each game frame
|
||||
void *controlOwner;
|
||||
void *identification;
|
||||
} touch_t;
|
||||
|
||||
#define MAX_TOUCHES 5
|
||||
extern touch_t sysTouches[MAX_TOUCHES];
|
||||
extern touch_t gameTouches[MAX_TOUCHES];
|
||||
extern pthread_mutex_t eventMutex; // used to sync between game and event threads
|
||||
|
||||
touch_t *TapInBounds( int x, int y, int w, int h );
|
||||
touch_t *TouchInBounds( int x, int y, int w, int h );
|
||||
touch_t *UpdateHudTouch( hudPic_t *hud );
|
||||
|
||||
typedef struct {
|
||||
pkTexture_t *texture;
|
||||
const char *title;
|
||||
int x, y, w, h;
|
||||
touch_t *touch;
|
||||
float scale; // ramps up and down after touches
|
||||
int frameNum; // reset scale if not checked on previous frame
|
||||
} ibutton_t;
|
||||
|
||||
void SetButtonPics( ibutton_t *button, const char *picName, const char *title, int x, int y );
|
||||
void SetButtonText( ibutton_t *button, const char *title, int x, int y, int w, int h );
|
||||
boolean HandleButton( ibutton_t *button );
|
||||
|
||||
|
||||
//---------------------------------------
|
||||
// Doom stuff we call directly
|
||||
//---------------------------------------
|
||||
void G_DoSaveGame (boolean menu);
|
||||
|
||||
//---------------------------------------
|
||||
// iphone_mapSelect.c
|
||||
//---------------------------------------
|
||||
void DisplayLoadingScreen();
|
||||
void iphoneMapSelectMenu();
|
||||
|
||||
//---------------------------------------
|
||||
// interfaces from the original game code
|
||||
//---------------------------------------
|
||||
void iphoneSetNotifyText( const char *str, ... );
|
||||
|
||||
void iphoneIntermission( wbstartstruct_t* wbstartstruct );
|
||||
|
||||
//---------------------------------------
|
||||
// interfaces to Objective-C land
|
||||
//---------------------------------------
|
||||
|
||||
// The event thread will fill this after hitting enter
|
||||
// on the console. The game thread should check it,
|
||||
// execute it, and clear it under mutex.
|
||||
extern char consoleCommand[1024];
|
||||
|
||||
void SysIPhoneSwapBuffers();
|
||||
void SysIPhoneVibrate();
|
||||
void SysIPhoneOpenURL( const char *url );
|
||||
void SysIPhoneSetUIKitOrientation( int isLandscapeRight );
|
||||
const char * SysIPhoneGetConsoleTextField();
|
||||
void SysIPhoneSetConsoleTextField(const char *);
|
||||
void SysIPhoneInitAudioSession();
|
||||
int SysIPhoneOtherAudioIsPlaying();
|
||||
int SysIphoneMilliseconds();
|
||||
int SysIphoneMicroseconds();
|
||||
const char * SysIphoneGetAppDir();
|
||||
const char * SysIphoneGetDocDir();
|
||||
|
||||
//---------------------------------------
|
||||
// interfaces from Objective-C land
|
||||
//---------------------------------------
|
||||
void iphoneStartup();
|
||||
void iphoneShutdown();
|
||||
void iphoneFrame();
|
||||
void iphoneTiltEvent( float *tilts );
|
||||
void iphoneTouchEvent( int numTouches, int touches[16] );
|
||||
void iphoneActivateConsole();
|
||||
void iphoneDeactivateConsole();
|
||||
void iphoneExecuteCommandLine();
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
|
||||
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 <UIKit/UIKit.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
|
||||
extern char iphoneAppDirectory[1024];
|
||||
extern int myargc;
|
||||
extern char **myargv;
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
// save for doom
|
||||
myargc = argc;
|
||||
myargv = argv;
|
||||
|
||||
// get the app directory based on argv[0]
|
||||
strcpy( iphoneAppDirectory, argv[0] );
|
||||
int len = strlen( iphoneAppDirectory );
|
||||
for( int i = len-1; i >= 0; i-- ) {
|
||||
if ( iphoneAppDirectory[i] == '/' ) {
|
||||
iphoneAppDirectory[i] = 0;
|
||||
break;
|
||||
}
|
||||
iphoneAppDirectory[i] = 0;
|
||||
}
|
||||
|
||||
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
|
||||
int retVal = UIApplicationMain(argc, argv, nil, nil);
|
||||
[pool release];
|
||||
return retVal;
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* misc.c
|
||||
* doom
|
||||
*
|
||||
* Created by John Carmack on 4/13/09.
|
||||
* Copyright 2009 idSoftware. 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.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#include "../doomiphone.h"
|
||||
|
||||
void Com_Printf( const char *fmt, ... ) {
|
||||
va_list argptr;
|
||||
|
||||
va_start( argptr, fmt );
|
||||
|
||||
//gsh, send output to the console buffer
|
||||
char buffer[1024];
|
||||
vsnprintf( buffer, sizeof( buffer ), fmt, argptr );
|
||||
AppendConsoleBuffer(buffer);
|
||||
|
||||
vprintf( fmt, argptr );
|
||||
va_end( argptr );
|
||||
}
|
||||
|
||||
void Com_Error( const char *fmt, ... ) {
|
||||
va_list argptr;
|
||||
|
||||
va_start( argptr, fmt );
|
||||
|
||||
//gsh, send output to the console buffer
|
||||
char buffer[1024];
|
||||
vsnprintf( buffer, sizeof( buffer ), fmt, argptr );
|
||||
AppendConsoleBuffer(buffer);
|
||||
|
||||
vprintf( fmt, argptr );
|
||||
va_end( argptr );
|
||||
|
||||
//gsh, email the console to id
|
||||
EmailConsole();
|
||||
|
||||
// drop into the editor
|
||||
abort();
|
||||
exit( 1 );
|
||||
}
|
||||
|
||||
char *va( const char *format, ... ) {
|
||||
va_list argptr;
|
||||
static char string[ 1024 ];
|
||||
|
||||
va_start( argptr, format );
|
||||
(void)vsnprintf( string, sizeof( string ), format, argptr );
|
||||
va_end( argptr );
|
||||
|
||||
string[ sizeof( string ) - 1 ] = '\0';
|
||||
|
||||
return string;
|
||||
}
|
||||
|
||||
int HashString( const char *string ) {
|
||||
int hash = *string;
|
||||
|
||||
if( hash ) {
|
||||
for( string += 1; *string != '\0'; ++string ) {
|
||||
hash = (hash << 5) - hash + tolower(*string);
|
||||
}
|
||||
}
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* misc.h
|
||||
* doom
|
||||
*
|
||||
* Created by John Carmack on 4/13/09.
|
||||
* Copyright 2009 idSoftware. 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.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
char *va( const char *format, ... );
|
||||
void Com_Printf( const char *fmt, ... );
|
||||
void Com_Error( const char *fmt, ... );
|
||||
int HashString( const char *string );
|
||||
|
||||
/*
|
||||
|
||||
Command execution takes a NUL-terminated string, breaks it into tokens,
|
||||
then searches for a command or variable that matches the first token.
|
||||
|
||||
*/
|
||||
|
||||
typedef void (*xcommand_t) (void);
|
||||
|
||||
// called by the init functions of other parts of the program to
|
||||
// register commands and functions to call for them.
|
||||
// The cmd_name is referenced later, so it should not be in temp memory
|
||||
// if function is NULL, the command will be forwarded to the server
|
||||
// as a clc_stringcmd instead of executed locally
|
||||
void Cmd_AddCommand( const char *cmd_name, xcommand_t function );
|
||||
|
||||
// print all the added commands
|
||||
void Cmd_ListCommands_f();
|
||||
|
||||
// attempts to match a partial command for automatic command line completion
|
||||
// returns NULL if nothing fits
|
||||
char *Cmd_CompleteCommand( const char *partial );
|
||||
|
||||
// The functions that execute commands get their parameters with these
|
||||
// functions. Cmd_Argv () will return an empty string, not a NULL
|
||||
// if arg > argc, so string operations are always safe.
|
||||
int Cmd_Argc( void );
|
||||
const char *Cmd_Argv( int arg );
|
||||
|
||||
// Takes a NUL-terminated string. Does not need to be /n terminated.
|
||||
// breaks the string up into argc / argv tokens.
|
||||
void Cmd_TokenizeString( const char *text );
|
||||
|
||||
// Parses a single line of text into arguments and tries to execute it
|
||||
// as if it was typed at the console
|
||||
void Cmd_ExecuteString( const char *text );
|
||||
|
||||
// execute each line of the config file
|
||||
void Cmd_ExecuteFile( const char *fullPathName );
|
||||
|
||||
@@ -0,0 +1,364 @@
|
||||
/*
|
||||
* prboomInterface.c
|
||||
* doom
|
||||
*
|
||||
* Created by John Carmack on 4/14/09.
|
||||
* Copyright 2009 Id Software. All rights reserved.
|
||||
*
|
||||
* Stuff to get prboom to compile without SDL
|
||||
*/
|
||||
/*
|
||||
|
||||
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.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#include "../doomiphone.h"
|
||||
|
||||
int desired_fullscreen;
|
||||
int usejoystick;
|
||||
int joyright;
|
||||
int joyleft;
|
||||
int joydown;
|
||||
int joyup;
|
||||
int gl_colorbuffer_bits;
|
||||
int gl_depthbuffer_bits;
|
||||
int snd_card;
|
||||
int mus_card;
|
||||
int endoom_mode;
|
||||
int use_fullscreen;
|
||||
int snd_samplerate;
|
||||
int ms_to_next_tick;
|
||||
int realtic_clock_rate;
|
||||
|
||||
|
||||
/* I_SafeExit
|
||||
* This function is called instead of exit() by functions that might be called
|
||||
* during the exit process (i.e. after exit() has already been called)
|
||||
* Prevent infinitely recursive exits -- killough
|
||||
*
|
||||
* JDC: we don't do any atexit() calls on iphone, so this shouldn't be necessary
|
||||
*/
|
||||
void I_SafeExit(int rc) {
|
||||
static int has_exited;
|
||||
if (!has_exited) {
|
||||
has_exited=rc ? 2 : 1;
|
||||
exit(rc);
|
||||
}
|
||||
}
|
||||
|
||||
void I_uSleep( unsigned long usec ) {
|
||||
usleep( usec );
|
||||
}
|
||||
|
||||
/*
|
||||
* HasTrailingSlash
|
||||
*
|
||||
* cphipps - simple test for trailing slash on dir names
|
||||
*/
|
||||
|
||||
boolean HasTrailingSlash(const char* dn)
|
||||
{
|
||||
return ( (dn[strlen(dn)-1] == '/') );
|
||||
}
|
||||
|
||||
char* I_FindFile(const char* wfname, const char* ext)
|
||||
{
|
||||
char *p = malloc( 1024 );
|
||||
|
||||
sprintf( p, "%s/base/%s", SysIphoneGetAppDir(), wfname );
|
||||
if (access(p,F_OK))
|
||||
strcat(p, ext); // try adding the extension
|
||||
if (!access(p,F_OK)) {
|
||||
lprintf(LO_INFO, " found %s\n", p);
|
||||
return p;
|
||||
}
|
||||
free( p );
|
||||
return NULL;
|
||||
#if 0
|
||||
// lookup table of directories to search
|
||||
static const struct {
|
||||
const char *dir; // directory
|
||||
const char *sub; // subdirectory
|
||||
const char *env; // environment variable
|
||||
const char *(*func)(void); // for I_DoomExeDir
|
||||
} search[] = {
|
||||
{NULL}, // current working directory
|
||||
{NULL, NULL, "DOOMWADDIR"}, // run-time $DOOMWADDIR
|
||||
{DOOMWADDIR}, // build-time configured DOOMWADDIR
|
||||
{NULL, "doom", "HOME"}, // ~/doom
|
||||
{NULL, NULL, "HOME"}, // ~
|
||||
{NULL, NULL, NULL, I_DoomExeDir}, // config directory
|
||||
{"/usr/local/share/games/doom"},
|
||||
{"/usr/share/games/doom"},
|
||||
{"/usr/local/share/doom"},
|
||||
{"/usr/share/doom"},
|
||||
};
|
||||
|
||||
int i;
|
||||
/* Precalculate a length we will need in the loop */
|
||||
size_t pl = strlen(wfname) + strlen(ext) + 4;
|
||||
|
||||
for (i = 0; i < sizeof(search)/sizeof(*search); i++) {
|
||||
char * p;
|
||||
const char * d = NULL;
|
||||
const char * s = NULL;
|
||||
/* Each entry in the switch sets d to the directory to look in,
|
||||
* and optionally s to a subdirectory of d */
|
||||
// switch replaced with lookup table
|
||||
if (search[i].env) {
|
||||
if (!(d = getenv(search[i].env)))
|
||||
continue;
|
||||
} else if (search[i].func)
|
||||
d = search[i].func();
|
||||
else
|
||||
d = search[i].dir;
|
||||
s = search[i].sub;
|
||||
|
||||
p = malloc((d ? strlen(d) : 0) + (s ? strlen(s) : 0) + pl);
|
||||
sprintf(p, "%s%s%s%s%s", d ? d : "", (d && !HasTrailingSlash(d)) ? "/" : "",
|
||||
s ? s : "", (s && !HasTrailingSlash(s)) ? "/" : "",
|
||||
wfname);
|
||||
|
||||
if (access(p,F_OK))
|
||||
strcat(p, ext);
|
||||
if (!access(p,F_OK)) {
|
||||
lprintf(LO_INFO, " found %s\n", p);
|
||||
return p;
|
||||
}
|
||||
free(p);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
boolean I_StartDisplay(void) {
|
||||
return true;
|
||||
}
|
||||
|
||||
void I_EndDisplay(void) {}
|
||||
int I_GetTime_RealTime(void) { return 0; }
|
||||
fixed_t I_GetTimeFrac (void) { return 0; }
|
||||
void I_GetTime_SaveMS(void) {}
|
||||
unsigned long I_GetRandomTimeSeed(void) { return 0; }
|
||||
|
||||
//const char* I_GetVersionString(char* buf, size_t sz);
|
||||
//const char* I_SigString(char* buf, size_t sz, int signum);
|
||||
|
||||
const char *I_DoomExeDir(void) { return SysIphoneGetAppDir(); }
|
||||
|
||||
//void I_SetAffinityMask(void);
|
||||
|
||||
|
||||
/*
|
||||
* I_Read
|
||||
*
|
||||
* cph 2001/11/18 - wrapper for read(2) which handles partial reads and aborts
|
||||
* on error.
|
||||
*/
|
||||
void I_Read(int fd, void* vbuf, size_t sz)
|
||||
{
|
||||
unsigned char* buf = vbuf;
|
||||
|
||||
while (sz) {
|
||||
int rc = read(fd,buf,sz);
|
||||
if (rc <= 0) {
|
||||
I_Error("I_Read: read failed: %s", rc ? strerror(errno) : "EOF");
|
||||
}
|
||||
sz -= rc; buf += rc;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* I_Filelength
|
||||
*
|
||||
* Return length of an open file.
|
||||
*/
|
||||
|
||||
int I_Filelength(int handle)
|
||||
{
|
||||
struct stat fileinfo;
|
||||
if (fstat(handle,&fileinfo) == -1)
|
||||
I_Error("I_Filelength: %s",strerror(errno));
|
||||
return fileinfo.st_size;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//
|
||||
// MUSIC I/O
|
||||
//
|
||||
void I_InitMusic(void) {}
|
||||
void I_ShutdownMusic(void) {}
|
||||
|
||||
void I_UpdateMusic(void) {}
|
||||
|
||||
// Volume.
|
||||
void I_SetMusicVolume(int volume) {}
|
||||
|
||||
// PAUSE game handling.
|
||||
void I_PauseSong(int handle) {}
|
||||
void I_ResumeSong(int handle) {}
|
||||
|
||||
// Registers a song handle to song data.
|
||||
int I_RegisterSong(const void *data, size_t len) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// cournia - tries to load a music file
|
||||
int I_RegisterMusic( const char* filename, musicinfo_t *music ) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Called by anything that wishes to start music.
|
||||
// plays a song, and when the song is done,
|
||||
// starts playing it again in an endless loop.
|
||||
// Horrible thing to do, considering.
|
||||
void I_PlaySong(int handle, int looping) {
|
||||
}
|
||||
|
||||
// Stops a song over 3 seconds.
|
||||
void I_StopSong(int handle) {
|
||||
}
|
||||
|
||||
// See above (register), then think backwards
|
||||
void I_UnRegisterSong(int handle) {
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void I_PreInitGraphics(void){}
|
||||
void I_CalculateRes(unsigned int width, unsigned int height){}
|
||||
void I_ShutdownGraphics(void){}
|
||||
void I_SetPalette(int pal){}
|
||||
void I_UpdateNoBlit (void){}
|
||||
void I_FinishUpdate (void){}
|
||||
int I_ScreenShot (const char *fname){return 0;}
|
||||
|
||||
|
||||
// CPhipps -
|
||||
// I_SetRes
|
||||
// Sets the screen resolution
|
||||
void I_SetRes(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
I_CalculateRes(SCREENWIDTH, SCREENHEIGHT);
|
||||
|
||||
// set first three to standard values
|
||||
for (i=0; i<3; i++) {
|
||||
screens[i].width = SCREENWIDTH;
|
||||
screens[i].height = SCREENHEIGHT;
|
||||
screens[i].byte_pitch = SCREENPITCH;
|
||||
screens[i].short_pitch = SCREENPITCH / V_GetModePixelDepth(VID_MODE16);
|
||||
screens[i].int_pitch = SCREENPITCH / V_GetModePixelDepth(VID_MODE32);
|
||||
}
|
||||
|
||||
// statusbar
|
||||
screens[4].width = SCREENWIDTH;
|
||||
screens[4].height = (ST_SCALED_HEIGHT+1);
|
||||
screens[4].byte_pitch = SCREENPITCH;
|
||||
screens[4].short_pitch = SCREENPITCH / V_GetModePixelDepth(VID_MODE16);
|
||||
screens[4].int_pitch = SCREENPITCH / V_GetModePixelDepth(VID_MODE32);
|
||||
|
||||
lprintf(LO_INFO,"I_SetRes: Using resolution %dx%d\n", SCREENWIDTH, SCREENHEIGHT);
|
||||
}
|
||||
|
||||
void I_UpdateVideoMode(void)
|
||||
{
|
||||
lprintf(LO_INFO, "I_UpdateVideoMode: %dx%d\n", SCREENWIDTH, SCREENHEIGHT );
|
||||
|
||||
V_InitMode(VID_MODEGL);
|
||||
I_SetRes();
|
||||
#if 0
|
||||
screens[0].not_on_heap = true;
|
||||
screens[0].data = NULL;
|
||||
screens[0].byte_pitch = screen->pitch;
|
||||
screens[0].short_pitch = screen->pitch / V_GetModePixelDepth(VID_MODE16);
|
||||
screens[0].int_pitch = screen->pitch / V_GetModePixelDepth(VID_MODE32);
|
||||
#endif
|
||||
|
||||
V_AllocScreens();
|
||||
|
||||
R_InitBuffer(SCREENWIDTH, SCREENHEIGHT);
|
||||
gld_Init(SCREENWIDTH, SCREENHEIGHT);
|
||||
}
|
||||
|
||||
void I_InitGraphics(void)
|
||||
{
|
||||
char titlebuffer[2048];
|
||||
static int firsttime=1;
|
||||
|
||||
SCREENWIDTH = 480;
|
||||
SCREENHEIGHT = 320;
|
||||
|
||||
if (firsttime)
|
||||
{
|
||||
firsttime = 0;
|
||||
|
||||
atexit(I_ShutdownGraphics);
|
||||
lprintf(LO_INFO, "I_InitGraphics: %dx%d\n", SCREENWIDTH, SCREENHEIGHT);
|
||||
|
||||
/* Set the video mode */
|
||||
I_UpdateVideoMode();
|
||||
|
||||
/* Setup the window title */
|
||||
strcpy(titlebuffer,PACKAGE);
|
||||
strcat(titlebuffer," ");
|
||||
strcat(titlebuffer,VERSION);
|
||||
// SDL_WM_SetCaption(titlebuffer, titlebuffer);
|
||||
|
||||
/* Initialize the input system */
|
||||
// I_InitInputs();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* I_StartTic
|
||||
* Called by D_DoomLoop,
|
||||
* called before processing each tic in a frame.
|
||||
* Quick syncronous operations are performed here.
|
||||
* Can call D_PostEvent.
|
||||
*/
|
||||
void I_StartTic (void){}
|
||||
|
||||
/* I_StartFrame
|
||||
* Called by D_DoomLoop,
|
||||
* called before processing any tics in a frame
|
||||
* (just after displaying a frame).
|
||||
* Time consuming syncronous operations
|
||||
* are performed here (joystick reading).
|
||||
* Can call D_PostEvent.
|
||||
*/
|
||||
|
||||
void I_StartFrame (void){}
|
||||
|
||||
|
||||
void I_Init(){}
|
||||
|
||||
unsigned int SDL_GetTicks() { return 0; };
|
||||
|
||||
int (*I_GetTime)(void) = I_GetTime_RealTime;
|
||||
|
||||
Reference in New Issue
Block a user