1 #include <config.h>
2 
3 #include <stdio.h>
4 #include <gettext.h>
5 
6 #include "G-Force_Proj.h"
7 #include "G-Force.h"
8 #include "XFloatList.h"
9 
10 #ifdef UNIX_X
11 
12 #define __defaultTTFormat 	""
13 #define __defaultFont 		""
14 
15 #define __drawText( x, y, str )		fprintf(stderr,str->getCStr());
16 
17 
18 #include "RectUtils.h"
19 #define __setupPort
20 
21 #define __restorePort
22 
23 #endif
24 
25 
26 #include "CEgFileSpec.h"
27 #include "EgOSUtils.h"
28 #include <math.h>
29 #include <stdlib.h>
30 #include "ConfigFile.h"
31 #include "Expression.h"
32 #include "Hashtable.h"
33 #include "ParticleGroup.h"
34 
GForce(void * inRefCon)35 GForce::GForce( void* inRefCon ) :
36 #if defined(UNIX_X)
37 	mPrefs( ".G-Force", true ),
38 #endif
39 	mWave1( &mT ),
40 	mWave2( &mT ),
41 	mPal1( &mT, &mIntensityParam ),
42 	mPal2( &mT, &mIntensityParam ),
43 	mConsoleLines( cDuplicatesAllowed, cOrderImportant ),
44 	mLineExpireTimes( cOrderImportant ),
45 	mParticles		( cNoDuplicates_CaseInsensitive, cSortLowToHigh ),
46 	mColorMaps		( cNoDuplicates_CaseInsensitive, cSortLowToHigh ),
47 	mDeltaFields	( cNoDuplicates_CaseInsensitive, cSortLowToHigh ),
48 	mWaveShapes		( cNoDuplicates_CaseInsensitive, cSortLowToHigh )  {
49 
50 	// Do initting...
51 	mWind				= 0;
52 	mOutPort			= 0;
53 	mRefCon				= inRefCon;
54 	mFrameCount			= 0;
55 	mT_MS_Base			= EgOSUtils::CurTimeMS();
56 	mConsoleExpireTime	=
57 	mLastCursorUpdate	=
58 	mLastGetKeys		=
59 	mT_MS				= 0;
60 	mFrameCountStart	=
61 	mNextPaletteUpdate	=
62 	mT					= 0;
63 	mNextShapeChange	= mT + 10;
64 	mNextFieldChange	= mT + 10;
65 	mNextColorChange	= mT + 10;
66 	mLastSongStart		= mT - 10000;
67 	mLastKeyPollTime	= mT;
68 	mLastActiveTime		= mT;
69 	mDoingSetPortWin	= false;
70 	mNeedsPaneErased	= true;
71 	mShapeSlideShow		=
72 	mColorSlideShow		=
73 	mFieldSlideShow		= true;
74 	mAtFullScreen		= false;
75 	mMouseWillAwaken	= false;
76 	mTrackTextDur		= 0;
77 
78 	mPrefs.Load();
79 	if ( mPrefs.GetPref( 'Vers' ) != GFORCE_COMPAT_VERSION ) {
80 		mConsoleDelay		= 8;
81 		mConsoleLineDur		= 14;
82 		mMagScale 			= 1;
83 		mTransitionLo		= 4;
84 		mTransitionHi		= 18;
85 		mScrnSaverDelay		= -1 * 60;			// Factory: screen saver mode disabled
86 		mPrefs.SetPref( 'Vers', GFORCE_COMPAT_VERSION );
87 		mBorderlessWind		= 0;
88 		mHandleKeys			= 1;
89 		mNum_S_Steps 		= 200;
90 		mFullscreenSize.h	= 640;
91 		mFullscreenSize.v	= 480;
92 		mFullscreenDepth	= 8;
93 		mFullscreenDevice	= 0;
94 		mMaxSize.h			= 30000;
95 		mMaxSize.v			= 360;
96 		mTrackTextPosMode	= 5;
97 		mTrackTextSize		= 18;
98 		mNormalizeInput		= false;
99 		mNewConfigNotify	= false;
100 		mParticlesOn		= true;
101 		mKeyMap					.Assign( "TLRY`SNGFZXCQWE,.M[]{}P******!@#$%^&*()1234567890" );
102 		mFieldIntervalStr		.Assign( "18 + rnd( 15 )" );
103 		mColorIntervalStr		.Assign( "10 + rnd( 15 )" );
104 		mShapeIntervalStr		.Assign( "10 + rnd( 15 )" );
105 		mTrackFont				.Assign( __defaultFont );
106 		mTrackTextStartStr		.Assign( "4 + LAST_SONG_START - t" );
107 		mTrackTextDurationStr	.Assign( "5" );
108 		mTrackMetaText			.Assign( __defaultTTFormat );
109 		mParticleDuration		.Assign( "8 + rnd( 15 )" );
110 		mParticleProbability	.Assign( ".09/((NUM_PARTICLES+1)^1.66)" );
111 
112 
113 		// Show the welcome msg for a pref rewrite...
114 		Println( GFORCE_VERS_STR );
115 		Println( "Press '?' for help" );
116 		}
117 	else {
118 		mConsoleDelay		= mPrefs.GetPref( 'CDur' );
119 		mConsoleLineDur		= mPrefs.GetPref( 'CLin' );
120 		mMagScale			= mPrefs.GetPref( 'MScl' ) / 1000.0;
121 		mTransitionLo		= mPrefs.GetPref( 'TrLo' );
122 		mTransitionHi		= mPrefs.GetPref( 'TrHi' );
123 		mScrnSaverDelay		= mPrefs.GetPref( 'SSvr' ) * 60.0;
124 		mBorderlessWind		= mPrefs.GetPref( 'NoBo' );
125 		mHandleKeys			= mPrefs.GetPref( 'Kybd' );
126 		mNum_S_Steps		= mPrefs.GetPref( 'Stps' );
127 		mFullscreenSize.h	= mPrefs.GetPref( 'FS_X' );
128 		mFullscreenSize.v	= mPrefs.GetPref( 'FS_Y' );
129 		mFullscreenDepth	= mPrefs.GetPref( 'FS_D' );
130 		mFullscreenDevice	= mPrefs.GetPref( 'FS_#' );
131 		mParticlesOn		= mPrefs.GetPref( 'P_On' );
132 		mNormalizeInput		= mPrefs.GetPref( 'Norm' );
133 		mMaxSize.v			= mPrefs.GetPref( 'MaxY' );
134 		mMaxSize.h			= mPrefs.GetPref( 'MaxX' );
135 		mNewConfigNotify	= mPrefs.GetPref( 'ShwT' );
136 		mTrackTextPosMode	= mPrefs.GetPref( 'TPos' );
137 		mTrackTextSize		= mPrefs.GetPref( 'TSze' );
138 		mPrefs.GetPref( 'WInt', mShapeIntervalStr );
139 		mPrefs.GetPref( 'DInt', mFieldIntervalStr );
140 		mPrefs.GetPref( 'CInt', mColorIntervalStr );
141 		mPrefs.GetPref( 'T?', mTrackTextStartStr );
142 		mPrefs.GetPref( 'TDur', mTrackTextDurationStr );
143 		mPrefs.GetPref( 'TFnt', mTrackFont );
144 		mPrefs.GetPref( 'TStr', mTrackMetaText );
145 		mPrefs.GetPref( 'PDur', mParticleDuration );
146 		mPrefs.GetPref( 'PPrb', mParticleProbability );
147 		mPrefs.GetPref( 'KMap', mKeyMap );
148 	}
149 
150 	mPortA.SetTrackTextFont( mTrackFont, mTrackTextSize );
151 	mPortB.SetTrackTextFont( mTrackFont, mTrackTextSize );
152 
153 	// Catch any bad values for mNumSampleBins
154 	if ( mNum_S_Steps < 1 || mNum_S_Steps > 10000 )
155 		mNum_S_Steps = 320;
156 
157 
158 	mNum_FFT_Steps = 256;
159 
160 	// Alloc/setup the data we'll have our virtual machines accessing...
161 	SetNumSampleBins( mNum_S_Steps );
162 	SetNumFFTBins( mNum_FFT_Steps );
163 
164 	// Setup waveshape members
165 	mWave1.SetMagFcn( (ExprUserFcn**) &mSampleFcn );
166 	mWave2.SetMagFcn( (ExprUserFcn**) &mSampleFcn );
167 
168 	// FIXME, use FFT data here
169 	mWave1.SetFFTFcn( (ExprUserFcn**) &mFFTFcn );
170 	mWave2.SetFFTFcn( (ExprUserFcn**) &mFFTFcn );
171 
172 	// Init particle stuff
173 	mDict.AddVar( "T", &mT );
174 	mDict.AddVar( "LAST_PARTICLE_START", &mLastParticleStart );
175 	mDict.AddVar( "NUM_PARTICLES", &mNumRunningParticles );
176 	mNumRunningParticles = 0;
177 	mNextParticleCheck = mT + 1;
178 	mParticleProbabilityFcn.Compile( mParticleProbability, mDict );
179 	mParticleDurationFcn.Compile( mParticleDuration, mDict );
180 	mShapeInterval.Compile( mShapeIntervalStr, mDict );
181 	mColorInterval.Compile( mColorIntervalStr, mDict );
182 	mFieldInterval.Compile( mFieldIntervalStr, mDict );
183 
184 	// Track Text stuff
185 	mDict.AddVar( "LAST_SONG_START", &mLastSongStart );
186 	mTrackTextStartFcn.Compile( mTrackTextStartStr, mDict );
187 	mTrackTextDurFcn.Compile( mTrackTextDurationStr, mDict );
188 
189 	// Transition bookkeeping
190 	mColorTransTime		= -1;
191 	mShapeTransTime		= -1;
192 	mGF_Palette			= 0;
193 	mWave				= 0;
194 
195 
196 	// Look in G-Force's support folders and see what we have to select from...
197 	BuildConfigLists();
198 
199 	mField		= &mField1;
200 	mNextField	= &mField2;
201 
202 	for ( int i = 0; i < 4; i++ )
203 		mCurKeys[ i ] = 0;
204 }
205 
206 
207 
208 
~GForce()209 GForce::~GForce() {
210 
211 
212 	// Rewrite the prefs to disk...
213 	mPrefs.SetPref( 'SSvr', mScrnSaverDelay / 60.0 );
214 	mPrefs.SetPref( 'TrHi', mTransitionHi );
215 	mPrefs.SetPref( 'TrLo', mTransitionLo );
216 	mPrefs.SetPref( 'MScl', mMagScale * 1000 );
217 	mPrefs.SetPref( 'CInt', mColorIntervalStr );
218 	mPrefs.SetPref( 'WInt', mShapeIntervalStr );
219 	mPrefs.SetPref( 'DInt', mFieldIntervalStr );
220 	mPrefs.SetPref( 'Stps', mNum_S_Steps );
221 	mPrefs.SetPref( 'PDur', mParticleDuration );
222 	mPrefs.SetPref( 'PPrb', mParticleProbability );
223 	mPrefs.SetPref( 'NoBo', mBorderlessWind );
224 	mPrefs.SetPref( 'Kybd', mHandleKeys	);
225 	mPrefs.SetPref( 'FS_X', mFullscreenSize.h );
226 	mPrefs.SetPref( 'FS_Y', mFullscreenSize.v );
227 	mPrefs.SetPref( 'FS_D', mFullscreenDepth );
228 	mPrefs.SetPref( 'FS_#', mFullscreenDevice );
229 	mPrefs.SetPref( 'Norm', mNormalizeInput );
230 	mPrefs.SetPref( 'MaxY', mMaxSize.v );
231 	mPrefs.SetPref( 'MaxX', mMaxSize.h );
232 	mPrefs.SetPref( 'TPos', mTrackTextPosMode );
233 	mPrefs.SetPref( 'TSze', mTrackTextSize );
234 	mPrefs.SetPref( 'TFnt', mTrackFont );
235 	mPrefs.SetPref( 'TStr', mTrackMetaText );
236 	mPrefs.SetPref( 'T?',   mTrackTextStartStr );
237 	mPrefs.SetPref( 'TDur', mTrackTextDurationStr );
238 	mPrefs.SetPref( 'KMap', mKeyMap );
239 	mPrefs.SetPref( 'P_On', mParticlesOn ? 1 : 0 );
240 	mPrefs.SetPref( 'ShwT', mNewConfigNotify ? 1 : 0 );
241 	mPrefs.SetPref( 'CDur', mConsoleDelay );
242 	mPrefs.SetPref( 'CLin', mConsoleLineDur );
243 
244 	// Init the track text info
245 	NewSong();
246 
247 	mPrefs.Store();
248 
249 }
250 
251 
252 
253 
254 
SetNumSampleBins(long inNumBins)255 void GForce::SetNumSampleBins( long inNumBins ) {
256 	float k;
257 
258 	if ( inNumBins > 0 && inNumBins < 10000 ) {
259 		mSampleFcn = (ExprUserFcn*) mSamplesBuf.Dim( sizeof( float ) * inNumBins + sizeof( ExprUserFcn ) + 32 );
260 		mNum_S_Steps = inNumBins;
261 		mSampleFcn -> mNumFcnBins = inNumBins;
262 
263 		// A fast lookup table for a sine wave
264 		mSine = (float*) mSineBuf.Dim( sizeof( float ) * inNumBins );
265 		k =  6.2831853071795 / ( (float) inNumBins );
266 
267 		for ( int i = 0; i < inNumBins; i++ ) {
268 			mSampleFcn -> mFcn[ i ] = 0;
269 			mSine[ i ] = sin(  k * ( (float) i ) );
270 		}
271 	}
272 }
273 
274 
275 
276 
277 
SetNumFFTBins(long inNumBins)278 void GForce::SetNumFFTBins( long inNumBins ) {
279 
280 	if ( inNumBins > 0 && inNumBins < 1000 ) {
281 		mFFTFcn = (ExprUserFcn*) mFFTBuf.Dim( sizeof( float ) * inNumBins + sizeof( ExprUserFcn ) + 32);
282 		mNum_FFT_Steps = inNumBins;
283 		mFFTFcn -> mNumFcnBins = inNumBins;
284 	}
285 }
286 
287 
288 
289 #define __setChar( n, ID )		s.setChar( n, mKeyMap.getChar( ID ) )
290 
291 
ShowHelp()292 void GForce::ShowHelp() {
293 	UtilStr s;
294 
295 	s.Assign( "X     - Display track title" );			__setChar( 1, cDispTrackTitle );	Println( &s );
296 	s.Assign( "X     - List configs" );					__setChar( 1, cGetConfigInfo );		Println( &s );
297 	s.Assign( "X     - Frame Rate" );					__setChar( 1, cFrameRate );			Println( &s );
298 	s.Assign( "X     - Particles on/off" );				__setChar( 1, cToggleParticles );	Println( &s );
299 	s.Assign( "X     - Spawn new particle" );			__setChar( 1, cSpawnNewParticle );	Println( &s );
300 	s.Assign( "X     - Fullscreen on/off" );			__setChar( 1, cToggleFullsceen );	Println( &s );
301 	s.Assign( "X     - Show config titles on/off" );	__setChar( 1, cToggleConfigName );	Println( &s );
302 	s.Assign( "X     - Normalize input on/off" );		__setChar( 1, cToggleNormalize );	Println( &s );
303 
304 	s.Assign( "X X   - Freeze/Continue slideshow" );		__setChar( 1, cStopSlideshowAll );	__setChar( 3, cStartSlideshowAll );		Println( &s );
305 	s.Assign( "X X   - +/- sound amplitude (See Extras docs)" );		__setChar( 1, cDecMagScale );		__setChar( 3, cIncMagScale );		Println( &s );
306 	s.Assign( "X X   - +/- num of bins (Stps) (See Extras docs)" );		__setChar( 1, cDecNumSSteps );		__setChar( 3, cIncNumSSteps );		Println( &s );
307 	s.Assign( "X X X - Prev/Next/Hold DeltaField" );		__setChar( 1, cPrevDeltaField );	__setChar( 3, cNextDeltaField );		__setChar( 5, cToggleFieldShow );		Println( &s );
308 	s.Assign( "X X X - Prev/Next/Hold ColorMap" );			__setChar( 1, cPrevColorMap );		__setChar( 3, cNextColorMap );			__setChar( 5, cToggleColorShow );		Println( &s );
309 	s.Assign( "X X X - Prev/Next/Hold WaveShape" );			__setChar( 1, cPrevWaveShape );		__setChar( 3, cNextWaveShape );			__setChar( 5, cToggleShapeShow );		Println( &s );
310 
311 	Println( "" );
312 	Println( "Press SHIFT and a number to store the current ColorMap, Waveshape, and" );
313 	Println( "   DeltaField, and press just the number to recall them." );
314 
315 	// Give the user more time than usual since this is help info
316 	mConsoleExpireTime += 8000;
317 }
318 
HandleKey(long inChar)319 bool GForce::HandleKey( long inChar ) {
320 	bool handled = true;
321 	int n;
322 
323 	// See if this keystroke is to be ignored
324 	if ( ! mHandleKeys )
325 		return false;
326 
327 
328 	if ( inChar >= 'a' && inChar <= 'z' )
329 		inChar = 'A' + ( inChar - 'a' );
330 
331 	if ( inChar == '/' || inChar == '?' )
332 		ShowHelp();
333 	else if ( inChar >= ' ' && inChar < 129 ) {
334 
335 		inChar = mKeyMap.FindNextInstanceOf( 0, inChar );
336 
337 		switch ( inChar ) {
338 
339 		case cDispTrackTitle:
340 			StartTrackText();
341 			break;
342 
343 		case cGetConfigInfo:
344 		  {
345 			Print(_("WaveShape:  "));
346 			Println( &mWaveShapeName );
347 			Print(_("ColorMap:   "));
348 			Println( &mColorMapName );
349 			Print(_("DeltaField: "));
350 			Println( mField -> GetName() );
351 			ParticleGroup* particle = (ParticleGroup*) mRunningParticlePool.GetHead();
352 			if ( particle ) {
353 				Print(_("Particles:  "));
354 
355 				while ( particle ) {
356 					Print( &particle -> mTitle );
357 					particle = (ParticleGroup*) particle -> GetNext();
358 					if ( particle )
359 						Print( ", " );
360 				}
361 				Println( "" );
362 			}
363 			break;
364 		  }
365 		case cFrameRate:
366 			mTemp.SetFloatValue( ( (float) mCurFrameRate ) / 10.0 );
367 			mTemp.Append(_(" frames/sec"));
368 			Println( &mTemp );
369 			break;
370 
371 		case cDecMagScale:
372 			mMagScale /= 1.2;
373 			mTemp.SetFloatValue( mMagScale );
374 			Print(_("Amplitude scale: "));
375 			Println( &mTemp );
376 			break;
377 
378 		case cIncMagScale:
379 			mMagScale *= 1.2;
380 			mTemp.SetFloatValue( mMagScale );
381 			Print(_("Amplitude scale: "));
382 			Println( &mTemp );
383 			break;
384 
385 		case cToggleParticles:
386 			mParticlesOn = ! mParticlesOn;
387 			if ( mParticlesOn )
388 				Println(_("Particles ON"));
389 			else
390 				Println(_("Particles OFF"));
391 			break;
392 
393 		case cSpawnNewParticle:
394 			SpawnNewParticle();
395 			break;
396 
397 		case cDecNumSSteps:
398 		case cIncNumSSteps:
399 			if ( inChar == cDecNumSSteps )
400 				n = - 4;
401 			else
402 				n = + 4;
403 			SetNumSampleBins( mNum_S_Steps + n );
404 			mTemp.Assign(_("Number s steps: "));
405 			mTemp.Append( mNum_S_Steps );
406 			Println( &mTemp );
407 			break;
408 
409 		case cToggleConfigName:
410 			mNewConfigNotify = ! mNewConfigNotify;
411 			if ( mNewConfigNotify )
412 				Println(_("Show names ON"));
413 			else
414 				Println(_("Show names OFF"));
415 			break;
416 
417 		case cToggleNormalize:
418 			mNormalizeInput = ! mNormalizeInput;
419 			if ( mNormalizeInput )
420 				Println(_("Normalize ON"));
421 			else
422 				Println(_("Normalize OFF"));
423 			break;
424 
425 		case cPrevDeltaField:
426 		case cNextDeltaField:
427 			n = mFieldPlayList.FindIndexOf( mCurFieldNum );
428 			if ( inChar == cPrevDeltaField )
429 				n = n + mFieldPlayList.Count() - 2;
430 
431 			loadDeltaField( mFieldPlayList.Fetch( 1 + n % mFieldPlayList.Count() ) );
432 
433 			// If the pref says so, display that we're loading a new config
434 			if ( mNewConfigNotify ) {
435 				Print(_("Loading DeltaField: "));
436 				Println( mField -> GetName() );
437 			}
438 
439 			// Turn field slide show off when we change deltafields manually
440 			if ( ! mFieldSlideShow )
441 				break;
442 		case cToggleFieldShow:
443 			mFieldSlideShow = ! mFieldSlideShow;
444 			mNextFieldChange = mT;
445 			if ( mFieldSlideShow ) {
446 				Println(_("DeltaField slideshow ON"));
447 				mFieldPlayList.Randomize(); }
448 			else
449 				Println(_("DeltaField slideshow OFF"));
450 			break;
451 
452 
453 		case cStartSlideshowAll:
454 			mFieldSlideShow = true;		mNextFieldChange = mT;
455 			mColorSlideShow = true;		mNextColorChange = mT;
456 			mShapeSlideShow = true;		mNextShapeChange = mT;
457 			Println(_("All slideshows ON"));
458 			break;
459 
460 		case cStopSlideshowAll:
461 			mFieldSlideShow = false;
462 			mColorSlideShow = false;
463 			mShapeSlideShow = false;
464 			Println(_("All slideshows OFF"));
465 			break;
466 
467 
468 		case cPrevColorMap:
469 		case cNextColorMap:
470 			n = mColorPlayList.FindIndexOf( mCurColorMapNum );
471 			if ( inChar == cPrevColorMap )
472 				n = n + mColorPlayList.Count() - 2;
473 
474 			loadColorMap( mColorPlayList.Fetch( 1 + n % mColorPlayList.Count() ), false );
475 
476 			// Turn slide show off when we change colormaps manually
477 			if ( ! mColorSlideShow )
478 				break;
479 		case cToggleColorShow:
480 			mColorSlideShow = ! mColorSlideShow;
481 			mNextColorChange = mT;
482 			if ( mColorSlideShow ) {
483 				Println(_("ColorMap slideshow ON"));
484 				mColorPlayList.Randomize(); }
485 			else
486 				Println(_("ColorMap slideshow OFF"));
487 			break;
488 
489 		case cPrevWaveShape:
490 		case cNextWaveShape:
491 			n = mShapePlayList.FindIndexOf( mCurShapeNum );
492 			if ( inChar == cPrevWaveShape )
493 				n = n + mShapePlayList.Count() - 2;
494 
495 			loadWaveShape( mShapePlayList.Fetch( 1 + n % mShapePlayList.Count() ), false );
496 
497 			// Turn slide show off when we change shapes manually
498 			if ( ! mShapeSlideShow )
499 				break;
500 		case cToggleShapeShow:
501 			mShapeSlideShow = ! mShapeSlideShow;
502 			mNextShapeChange = mT;
503 			if ( mShapeSlideShow ) {
504 				Println(_("WaveShape slideshow ON"));
505 				mShapePlayList.Randomize(); }
506 			else
507 				Println(_("WaveShape slideshow OFF"));
508 			break;
509 
510 		case cSetPreset0:	StoreConfigState( 'SET0' );	break;
511 		case cSetPreset1:	StoreConfigState( 'SET1' );	break;
512 		case cSetPreset2:	StoreConfigState( 'SET2' );	break;
513 		case cSetPreset3:	StoreConfigState( 'SET3' );	break;
514 		case cSetPreset4:	StoreConfigState( 'SET4' );	break;
515 		case cSetPreset5:	StoreConfigState( 'SET5' );	break;
516 		case cSetPreset6:	StoreConfigState( 'SET6' );	break;
517 		case cSetPreset7:	StoreConfigState( 'SET7' );	break;
518 		case cSetPreset8:	StoreConfigState( 'SET8' );	break;
519 		case cSetPreset9:	StoreConfigState( 'SET9' );	break;
520 
521 
522 		case cPreset0:	handled = RestoreConfigState( 'SET0' );	break;
523 		case cPreset1:	handled = RestoreConfigState( 'SET1' );	break;
524 		case cPreset2:	handled = RestoreConfigState( 'SET2' );	break;
525 		case cPreset3:	handled = RestoreConfigState( 'SET3' );	break;
526 		case cPreset4:	handled = RestoreConfigState( 'SET4' );	break;
527 		case cPreset5:	handled = RestoreConfigState( 'SET5' );	break;
528 		case cPreset6:	handled = RestoreConfigState( 'SET6' );	break;
529 		case cPreset7:	handled = RestoreConfigState( 'SET7' );	break;
530 		case cPreset8:	handled = RestoreConfigState( 'SET8' );	break;
531 		case cPreset9:	handled = RestoreConfigState( 'SET9' );	break;
532 
533 		default:
534 			handled = false;
535 		}	}
536 	else
537 		handled = false;
538 
539 	return handled;
540 }
541 
542 
543 
StoreConfigState(long inParamName)544 void GForce::StoreConfigState( long inParamName ) {
545 	UtilStr str;
546 
547 	str.Assign( mWaveShapeName );		str.Append( ',' );
548 	str.Append( mColorMapName );		str.Append( ',' );
549 	str.Append( mField -> GetName() );	str.Append( ',' );
550 
551 	mPrefs.SetPref( inParamName, str );
552 	Println(_("State stored."));
553 }
554 
555 
RestoreConfigState(long inParamName)556 bool GForce::RestoreConfigState( long inParamName ) {
557 	UtilStr str, configName;
558 	long pos, n, found = false;
559 
560 	if ( mPrefs.GetPref( inParamName, str ) ) {
561 
562 		// Parse the waveshape config name
563 		pos = str.FindNextInstanceOf( 0, ',' );
564 		configName.Assign( str.getCStr(), pos - 1 );
565 		n = mWaveShapes.FetchBestMatch( configName );
566 		loadWaveShape( n, false );
567 		mShapeSlideShow = false;
568 
569 		// Parse the colormap config name
570 		str.Trunc( pos, false );
571 		pos = str.FindNextInstanceOf( 0, ',' );
572 		configName.Assign( str.getCStr(), pos - 1 );
573 		n = mColorMaps.FetchBestMatch( configName );
574 		loadColorMap( n, false );
575 		mColorSlideShow = false;
576 
577 		// Parse the colormap config name
578 		str.Trunc( pos, false );
579 		n = mDeltaFields.FetchBestMatch( str );
580 		loadDeltaField( n );
581 		mFieldSlideShow = false;
582 
583 		found = true;
584 	}
585 
586 	return found;
587 }
588 
589 
590 
ManageColorChanges()591 void GForce::ManageColorChanges() {
592 	int i;
593 
594 	// If in a ColorMap transition/morph
595 	if ( mColorTransTime > 0 ) {
596 
597 		// If we've the ColorMap transition is over, end it
598 		if ( mT_MS > mColorTransEnd ) {
599 			GF_Palette* temp = mGF_Palette;
600 			mGF_Palette = mNextPal;
601 			mNextPal = temp;
602 			mColorTransTime = -1;
603 			mNextColorChange = mT + mColorInterval.Evaluate();
604 		}
605 	}
606 
607 	// Time for a color map change?
608 	else if ( mT > mNextColorChange && mColorSlideShow ) {
609 
610 		// Load the next config in the (randomized) config list...
611 		i = mColorPlayList.FindIndexOf( mCurColorMapNum );
612 
613 		// Make a new play list if we've reached the end of the list...
614 		if ( i >= mColorPlayList.Count() ) {
615 			mColorPlayList.Randomize();
616 			i = 0;
617 		}
618 		loadColorMap( mColorPlayList.Fetch( i + 1 ), true );
619 	}
620 
621 	// Update the screen palette if it's time
622 	if ( mT > mNextPaletteUpdate ) {
623 
624 		// If in a ColorMap transition/morph then we must set mColorTrans, for it's linked into mGF_Palette
625 		if ( mColorTransTime > 0 ) {
626 			float t = (float) ( mColorTransEnd - mT_MS ) / ( (float) mColorTransTime );
627 			mColorTrans = pow( t, TRANSITION_ALPHA );
628 		}
629 
630 		// Evaluate the palette at this time
631 		mGF_Palette -> Evaluate( mPalette );
632 
633 		// Set our offscreen ports to the right palette...
634 		mPortA.SetPalette( mPalette );
635 		mPortB.SetPalette( mPalette );
636 
637 		// If we're at fullsceen, the screen device may need the current palette too
638 		if ( mAtFullScreen && mFullscreenDepth == 8 ) {
639 			mScreen.SetPalette( mPalette );
640 			mPortA.PreventActivate( mOutPort );
641 			mPortB.PreventActivate( mOutPort );
642 		}
643 
644 		// Reevaluate the palette a short time from now
645 		mNextPaletteUpdate = mT + .1;
646 	}
647 }
648 
649 
650 
ManageShapeChanges()651 void GForce::ManageShapeChanges() {
652 	int i;
653 
654 	// If in a WaveShape transition/morph
655 	if ( mShapeTransTime > 0 ) {
656 
657 		// If we've the ColorMap transition is over, end it
658 		if ( mT_MS > mShapeTransEnd ) {
659 			WaveShape* temp = mWave;
660 			mWave = mNextWave;
661 			mNextWave = temp;
662 			mShapeTransTime = -1;
663 			mNextShapeChange = mT + mShapeInterval.Evaluate();
664 		} }
665 
666 	// Time for a wave shape change?
667 	else if ( mT > mNextShapeChange && mShapeSlideShow ) {
668 
669 		// Load the next config in the (randomized) config list...
670 		i = mShapePlayList.FindIndexOf( mCurShapeNum );
671 
672 		// Make a new play list if we've reached the end of the list...
673 		if ( i >= mShapePlayList.Count() ) {
674 			mShapePlayList.Randomize();
675 			i = 0;
676 		}
677 		loadWaveShape( mShapePlayList.Fetch( i + 1 ), true );
678 	}
679 
680 }
681 
682 
ManageFieldChanges()683 void GForce::ManageFieldChanges() {
684 	long i;
685 
686 	// If we have have a delta field in mid-calculation, chip away at it...
687 	if ( ! mNextField -> IsCalculated() )
688 		mNextField -> CalcSome();
689 
690 	if ( mT > mNextFieldChange && mNextField -> IsCalculated() && mFieldSlideShow ) {
691 
692 		// Load the next field in the (randomized) field list...
693 		i = mFieldPlayList.FindIndexOf( mCurFieldNum );
694 
695 		// Make a new play list if we've reached the end of the list...
696 		if ( i >= mFieldPlayList.Count() ) {
697 			mFieldPlayList.Randomize();
698 			i = 0;
699 		}
700 
701 		// loadGradField() will initiate computation on mField with a new grad field...
702 		loadDeltaField( mFieldPlayList.Fetch( i + 1 ) );
703 		DeltaField* temp = mField;
704 		mField = mNextField;
705 		mNextField = temp;
706 
707 		// If the pref says so, display that we're loading a new config
708 		if ( mNewConfigNotify ) {
709 			Print( "Loaded DeltaField: " );
710 			Println( mField -> GetName() );
711 		}
712 	}
713 }
714 
715 
716 
ManageParticleChanges()717 void GForce::ManageParticleChanges() {
718 	float rndVar;
719 
720 	if ( mT > mNextParticleCheck && mParticlesOn ) {
721 
722 		// Generate a random probability value.
723 		rndVar = ( (float) rand() ) / ( (float) RAND_MAX );
724 
725 		// Comparing that to the evalated probability of a new particle being spawned determines if a new one *should* be spawned
726 		if ( rndVar < mParticleProbabilityFcn.Evaluate() ) {
727 
728 			SpawnNewParticle();
729 		}
730 
731 		// Check to make a new particle one second from now
732 		mNextParticleCheck = mT + 1;
733 	}
734 }
735 
736 
737 
DrawParticles(PixPort & inPort)738 void GForce::DrawParticles( PixPort& inPort ) {
739 
740 	// Draw all the particles
741 	ParticleGroup* particle, *next;
742 	particle = (ParticleGroup*) mRunningParticlePool.GetHead();
743 	while ( particle ) {
744 		next = (ParticleGroup*) particle -> GetNext();
745 
746 		// When particles stop, move them to a holding/stopped list
747 		if ( ! particle -> IsExpired() )
748 			particle -> DrawGroup( inPort );
749 		else {
750 			mStoppedParticlePool.addToHead( particle );
751 
752 			// Update the var that holds how many particles are running (and is accessible in the PPrb expr)
753 			mNumRunningParticles = mRunningParticlePool.shallowCount();
754 		}
755 
756 		particle = next;
757 	}
758 
759 }
760 
761 
762 
763 
RecordZeroSample(long inCurTime)764 void GForce::RecordZeroSample( long inCurTime ) {
765 	int i;
766 
767 	for ( i = 0; i < mNum_S_Steps; i++ )
768 		mSampleFcn -> mFcn[ i ] = 0;
769 
770 	RecordSample( inCurTime );
771 }
772 
773 
774 
775 /*
776 void GForce::RecordSample( long inCurTime, float* inFourier, long inNumBins ) {
777 	long w, s, n;
778 	float sample;
779 	ExprUserFcn* fcn;
780 
781 	// Now write the sample to memory, adjusted for amplitude...
782 	fcn = (ExprUserFcn*) mSampleFcn;
783 	fcn -> mNumFcnBins = mNum_S_Steps;
784 
785 	for ( s = 0; s < mNum_S_Steps; s++ ) {
786 
787 		sample = 0;
788 		for ( w = 0; w < inNumBins; w++ ) {
789 			n = ( 2.42322211 * w + 1.9 ) * ((float) s) + 1.23231121211 * w;
790 			sample += inFourier[ w ] * mSine[ n % mNum_S_Steps ];
791 		}
792 
793 		fcn -> mFcn[ s ] = sample;
794 	}
795 
796 	RecordSample( inCurTime );
797 }
798 */
799 
800 
IdleMonitor()801 void GForce::IdleMonitor() {
802 	bool kybdPress = false;
803 	float pollDelay;
804 	float secsUntilSleep = mScrnSaverDelay - ( mT - mLastActiveTime );
805 	Point pt;
806 
807 	// Calc time till next kybd poll (Don't waste time checking the kybd unless we've been idle a while)
808 	if ( IsFullscreen() )
809 		pollDelay = .6;
810 
811 	// Don't bother rapildly checking the kybd until we're really close to going into screen saver mode
812 	else if ( secsUntilSleep < 90 )
813 		pollDelay = secsUntilSleep / 120.0;
814 	else
815 		pollDelay = 10;
816 
817 	// If it's time to poll for activity...
818 	if ( mT > mLastKeyPollTime + pollDelay ) {
819 
820 		mLastKeyPollTime = mT;
821 
822 		// Check the mouse pos and record it as active if its been moved.
823 		EgOSUtils::GetMouse( pt );
824 		if ( pt.h != mLastMousePt.h || pt.v != mLastMousePt.v || kybdPress ) {
825 			mLastMousePt		= pt;
826 			mLastActiveTime		= mT;
827 		}
828 
829 		// If we're elligible to enter fullscreen then do it
830 		if ( ! mAtFullScreen && mT - mLastActiveTime > mScrnSaverDelay ) {
831 			mMouseWillAwaken = true;
832 		}
833 	}
834 }
835 
836 
837 
RecordSample(long inCurTime,float * inSound,float inScale,long inNumBins,float * inFFT,float inFFTScale,long inFFTNumBins)838 void GForce::RecordSample( long inCurTime, float* inSound, float inScale, long inNumBins, float* inFFT, float inFFTScale, long inFFTNumBins) {
839 	float mag, sum;
840 	int i, n;
841 	ExprUserFcn* fcn;
842 
843 	// Only use/process bins we'll actually use
844 	if ( inNumBins > mNum_S_Steps )
845 		inNumBins = mNum_S_Steps;
846 
847 
848 
849 	// Calc a 1/RMS avg value...
850 	if ( mNormalizeInput ) {
851 
852 		// Find an RMS amplitude for the sample
853 		for ( sum = 0.0001, i = 0; i < inNumBins; i++ ) {
854 			mag = inSound[ i ];
855 			sum += mag * mag;
856 		}
857 		inScale = mMagScale * .009 * ( (float) inNumBins ) / ( sqrt( sum ) ); }
858 	else
859 		inScale *= mMagScale;
860 
861 
862 	// Now write the sample to memory, adjusted for amplitude...
863 	fcn = (ExprUserFcn*) mSampleFcn;
864 	fcn -> mNumFcnBins = inNumBins;
865 	for ( i = 0; i < inNumBins; i++ ) {
866 		mag = inSound[ i ];
867 		fcn -> mFcn[ i ] = inSound[ i ] * inScale;
868 	}
869 
870 	XFloatList::GaussSmooth( 1.3, inNumBins, fcn -> mFcn );
871 
872 	// Flatten the ends of the sample...
873 	n = inNumBins / 20 + 1;
874 	if ( n <= inNumBins ) {
875 		for ( i = 0; i < n; i++ ) {
876 			mag = sin( .5 * 3.1 * i / n );
877 			fcn -> mFcn[ i ] *= mag;
878 			fcn -> mFcn[ inNumBins - i - 1 ] *= mag;
879 		}
880 	}
881 
882 	// Now write the FFT data to memory, adjusted for amplitude...
883 	fcn = (ExprUserFcn*) mFFTFcn;
884 	fcn -> mNumFcnBins = inFFTNumBins;
885 
886 	for ( i = 0; i < inFFTNumBins; i++ ) {
887 		fcn -> mFcn[ i ] = inFFT[ i ] * inFFTScale;
888 	}
889 
890 
891 	RecordSample( inCurTime );
892 }
893 
894 
895 
896 
897 
RecordSample(long inCurTime)898 void GForce::RecordSample( long inCurTime ) {
899 	bool drewTitleText = false;
900 	long intensity;
901 	float t;
902 
903 	if ( &mPortA == mCurPort )
904 		mCurPort = &mPortB;
905 	else
906 		mCurPort = &mPortA;
907 
908 	// All the waveshape virtual machines are linked to our time index
909 	mT_MS = inCurTime - mT_MS_Base;
910 	mT = ( (float) inCurTime ) / 1000.0;
911 
912 	// Don't bother doing mouse or kybd poll if sceeen saver mode is disabled
913 	if ( mScrnSaverDelay > 0 )
914 		IdleMonitor();
915 
916 	ManageColorChanges();
917 	ManageShapeChanges();
918 	ManageFieldChanges();
919 	ManageParticleChanges();
920 
921 	// Do the blur operation, a fcn of what's oqn the screen, and the current delta field
922 
923 	if ( mCurPort == &mPortA )
924 		mPortB.Fade( mPortA, mField -> GetField() );
925 	else
926 		mPortA.Fade( mPortB, mField -> GetField() );
927 
928         /* This redraws the image */
929 
930 	// Draw all the current particles
931 	DrawParticles( *mCurPort );
932 
933 	// Draw the current wave shape for the current music sample playing
934 	// If there's a morph going, drawing is a mix of both waves
935 	if ( mShapeTransTime > 0 ) {
936 		float morphPct = (float) ( mShapeTransEnd - mT_MS ) / ( (float) mShapeTransTime );
937 		mWave -> Draw( mNum_S_Steps, *mCurPort, 1, mNextWave, morphPct ); }
938 	else
939 		mWave -> Draw( mNum_S_Steps, *mCurPort, 1, 0, 0 );
940 
941 
942 	// If we're not currently drawing track text, check to see if we start new text
943 	if ( mTrackTextDur == 0 && mTrackTextPosMode ) {
944 		if ( mTrackTextStartFcn.Evaluate() > 0 )
945 			StartTrackText();
946 	}
947 
948 	// If we already have a t.t. draw in progress, draw the text in the (full) foreground color
949 	if ( mTrackTextDur > 0 ) {
950 
951 		// From 0 to 1, how far are we into the text display interval?
952 		t = ( mT - mTrackTextStartTime ) / mTrackTextDur;
953 
954 		// Decrease the text intensity thru time
955 		intensity = 255 * ( 1.2 - .3*t );
956 		if ( intensity > 255 )
957 			intensity = 255;
958 
959 		mCurPort -> SetTextColor( mPalette[ intensity ] );
960 		mCurPort -> SetTrackTextFont();
961 		mCurPort -> DrawText( mTrackTextPos.h, mTrackTextPos.v, mTrackText );
962 	}
963 
964 	// Draw the console text to the offscreen image.  Then copy the image to the OS out port
965 	if ( mT_MS < mConsoleExpireTime ) {
966 
967 		// To ensure the console text is readable, we erase it when we're done
968 		mCurPort -> SetTextMode( SRC_XOR );
969 		mCurPort -> SetTextColor( mPalette[ 255 ] );
970 		mCurPort -> SetConsoleFont();
971 		DrawConsole();
972 		DrawFrame();
973 		mCurPort -> SetTextColor( mPalette[ 0 ] );
974 		DrawConsole();
975 		mCurPort -> SetTextMode( SRC_OR );  }
976 	else
977 		DrawFrame();
978 
979 	// We need to avoid text all bluring together so we overwrite the foreground text we just drew
980 	//  with text of a lower intensity...
981 	if ( mTrackTextDur > 0 ) {
982 
983 		// Is the text is about to expire? if not, continue drawing.
984 		if ( t <= 1 ) {
985 
986 			intensity = 255.5 * pow( t, 1.5 );
987 			mCurPort -> SetTextColor( mPalette[ intensity ] );
988 			mCurPort -> SetTrackTextFont();
989 			mCurPort -> DrawText( mTrackTextPos.h, mTrackTextPos.v, mTrackText ); }
990 		else {
991 
992 			// The text's duration is up so turn the draw flag off
993 			mTrackTextDur = 0;
994 		}
995 	}
996 
997 
998 	// Maintain the frame rate
999 	mFrameCount++;
1000 	if ( mT_MS - mFrameCountStart >= 1500 ) {
1001 		mCurFrameRate = 10000 * mFrameCount / ( mT_MS - mFrameCountStart );
1002 		mFrameCountStart = mT_MS;
1003 		mFrameCount = 0;
1004 	}
1005 
1006 	if ( mT_MS - mLastCursorUpdate > 3000 ) {
1007 		mLastCursorUpdate = mT_MS;
1008 		if ( IsFullscreen() )
1009 			EgOSUtils::HideCursor();
1010 	}
1011 }
1012 
1013 
StartTrackText()1014 void GForce::StartTrackText() {
1015 
1016 	if ( mTrackTextPosMode ) {
1017 		CalcTrackTextPos();
1018 		mTrackTextDur = mTrackTextDurFcn.Evaluate();
1019 		mTrackTextStartTime = mT;
1020 	}
1021 }
1022 
DrawFrame()1023 void GForce::DrawFrame() {
1024 
1025 
1026 	__setupPort
1027 
1028 	// If we're fullscreen, follow the API (the screen may need to do something to finish)
1029 	if ( mScreen.IsFullscreen() )
1030 		mOutPort = mScreen.BeginFrame();
1031 
1032 	// Someone may have asked to clear the GF window/pane
1033 	if ( mNeedsPaneErased ) {
1034 		ErasePane();
1035 		mNeedsPaneErased = false;
1036 	}
1037 
1038 	// Blt our offscreen world to the output device
1039 	Rect r;
1040 	r.left = r.top = 0;
1041 	r.right = mDispRect.right - mDispRect.left;
1042 	r.bottom = mDispRect.bottom - mDispRect.top;
1043 //	mCurPort -> CopyBits( mOutPort, &r, &mDispRect );
1044 	mCurPort -> CopyBits( mVideoBuffer, &r, &mDispRect );
1045 
1046 	// If we're fullscreen, follow the API (the screen may need to do something to finish)
1047 	if ( mScreen.IsFullscreen() )
1048 		mScreen.EndFrame();
1049 
1050 	__restorePort
1051 }
1052 
SetOutVideoBuffer(unsigned char * inVideoBuffer)1053 void GForce::SetOutVideoBuffer( unsigned char *inVideoBuffer ) {
1054 	mVideoBuffer = inVideoBuffer;
1055 }
1056 
1057 #define __loadFolder( folderName, specList, playList )						\
1058 	startOver = true;														\
1059 	folder.AssignFolder( folderName );										\
1060 	while ( EgOSUtils::GetNextFile( folder, spec, startOver, false ) ) {	\
1061 		specList.AddCopy( spec );											\
1062 		startOver = false;													\
1063 	}																		\
1064 	/* Build a 'play' list */												\
1065 	playList.RemoveAll();													\
1066 	for ( i = 1; i <= specList.Count(); i++ ) {								\
1067 		playList.Add( i );													\
1068 	}																		\
1069 	playList.Randomize();
1070 
1071 
BuildConfigLists()1072 void GForce::BuildConfigLists() {
1073 	CEgFileSpec folder, spec;
1074 	int i;
1075 	bool startOver;
1076 
1077 	__loadFolder( DATADIR "/GForceDeltaFields", mDeltaFields, mFieldPlayList )
1078 
1079 	__loadFolder( DATADIR "/GForceWaveShapes", mWaveShapes, mShapePlayList )
1080 
1081 	__loadFolder( DATADIR "/GForceColorMaps", mColorMaps, mColorPlayList )
1082 
1083 	__loadFolder( DATADIR "/GForceParticles", mParticles, mParticlePlayList )
1084 }
1085 
1086 
1087 // A linear spread is the default field
1088 #define __FIELD_FACTORY		"\
1089 Aspc=0,\
1090 srcX=\"x * .9\",\
1091 srcY=\"y * .9\",\
1092 Vers=100\
1093 "
1094 
1095 // A centered circle is the default shape
1096 #define __SHAPE_FACTORY		"\
1097 Stps=-1,\
1098 B0=\"t * 0.0003\",\
1099 Aspc=1,\
1100 C0=\"abs( mag( s ) ) * 0.15 + .3\",\
1101 C1=\"s * 6.28318530 + b0\",\
1102 X0=\"c0 * cos( c1 )\",\
1103 Y0=\"c0 * sin( c1 )\",\
1104 Vers=100\
1105 "
1106 
1107 // A single color is the defaut color
1108 #define __COLOR_FACTORY		"\
1109 H=\".9\",\
1110 S=\".8\",\
1111 V=\"i\",\
1112 Vers=100\
1113 "
1114 
1115 
1116 
1117 
loadColorMap(long inColorMapNum,bool inAllowMorph)1118 void GForce::loadColorMap( long inColorMapNum, bool inAllowMorph ) {
1119 	const CEgFileSpec* spec;
1120 	int ok = false, vers;
1121 	ArgList args;
1122 
1123 	// Fetch the spec for our config file or folder
1124 	spec = mColorMaps.FetchSpec( inColorMapNum );
1125 
1126 	if ( spec ) {
1127  		mCurColorMapNum = inColorMapNum;
1128 
1129 		ok = ConfigFile::Load( spec, args );
1130 		if ( ok ) {
1131 			vers = args.GetArg( 'Vers' );
1132 			ok = vers >= 100 && vers < 110;
1133 			spec -> GetFileName( mColorMapName );
1134 		}
1135 	}
1136 
1137 
1138 	if ( ! ok ) {
1139 		args.SetArgs( __COLOR_FACTORY );
1140 		mColorMapName.Assign( "<Factory Default>" );
1141 	}
1142 
1143 	// If the pref says so, display that we're loading a new config
1144 	if ( mNewConfigNotify ) {
1145 		Print( "Loaded ColorMap: " );
1146 		Println( &mColorMapName );
1147 	}
1148 
1149 
1150 	// If first time load, don't do any transition/morph, otherwise set up the morph
1151 	if ( mGF_Palette == 0 || ! inAllowMorph ) {
1152 		mGF_Palette = &mPal1;
1153 		mNextPal	= &mPal2;
1154 		mGF_Palette -> Assign( args );
1155 		mColorTransTime = -1;
1156 		mNextColorChange = mT + mColorInterval.Evaluate(); }
1157 	else {
1158 		mNextPal -> Assign( args );
1159 		mGF_Palette -> SetupTransition( mNextPal, &mColorTrans );
1160 
1161 		// Calculate how long this transition/morph will be
1162 		mColorTransTime	= EgOSUtils::Rnd( mTransitionLo * 1000, mTransitionHi * 1000 );
1163 		mColorTransEnd	= mT_MS + mColorTransTime;
1164 	}
1165 }
1166 
1167 
1168 
1169 
1170 
1171 #define DEC_SIZE 6
1172 
loadDeltaField(long inFieldNum)1173 void GForce::loadDeltaField( long inFieldNum ) {
1174 	const CEgFileSpec* spec;
1175 	int ok = false, vers;
1176 	ArgList args;
1177 	UtilStr	name;
1178 
1179 	// Fetch the spec for our config file or folder
1180 	spec = mDeltaFields.FetchSpec( inFieldNum );
1181 
1182 	if ( spec ) {
1183 
1184 		// Know what to put a check mark next to in the popup menu
1185 		mCurFieldNum = inFieldNum;
1186 
1187 		ok = ConfigFile::Load( spec, args );
1188 		if ( ok ) {
1189 			vers = args.GetArg( 'Vers' );
1190 			ok = vers >= 100 && vers < 110;
1191 			spec -> GetFileName( name );
1192 		}
1193 	}
1194 
1195 	if ( ! ok ) {
1196 		args.SetArgs( __FIELD_FACTORY );
1197 		name.Assign( "<Factory Default>" );
1198 	}
1199 
1200 	// Initiate recomputation of mField
1201 	mField -> Assign( args, name );
1202 	mNextFieldChange = mT + mFieldInterval.Evaluate();
1203 }
1204 
1205 
1206 
1207 
1208 
loadWaveShape(long inShapeNum,bool inAllowMorph)1209 void GForce::loadWaveShape( long inShapeNum, bool inAllowMorph ) {
1210 	const CEgFileSpec* spec;
1211 	int ok = false, vers;
1212 	ArgList	args;
1213 
1214 	// Fetch the spec for our config file or folder
1215 	spec = mWaveShapes.FetchSpec( inShapeNum );
1216 
1217 	if ( spec ) {
1218 		// Know what to put a check mark next to in the popup menu
1219 		mCurShapeNum = inShapeNum;
1220 
1221 		ok = ConfigFile::Load( spec, args );
1222 
1223 		if ( ok ) {
1224 			vers = args.GetArg( 'Vers' );
1225 			ok = vers >= 100 && vers < 110;
1226 			spec -> GetFileName( mWaveShapeName );
1227 		}
1228 	}
1229 
1230 
1231 	if ( ! ok ) {
1232 		args.SetArgs( __SHAPE_FACTORY );
1233 		mWaveShapeName.Assign( "<Factory Default>" );
1234 	}
1235 
1236 	// If the pref says so, display that we're loading a new config
1237 	if ( mNewConfigNotify ) {
1238 		Print( "Loaded WaveShape: " );
1239 		Println( &mWaveShapeName );
1240 	}
1241 
1242 
1243 	// If first time load, don't do any transition/morph, otherwise set up the morph
1244 	if ( mWave == 0 || ! inAllowMorph ) {
1245 		mWave		= &mWave1;
1246 		mNextWave	= &mWave2;
1247 		mWave -> Load( args, mNum_S_Steps );
1248 		mNextShapeChange = mT + mShapeInterval.Evaluate();
1249 		mShapeTransTime = -1; }
1250 	else {
1251 		mNextWave -> Load( args, mNum_S_Steps );
1252 		mWave -> SetupTransition( mNextWave );
1253 
1254 		// Calculate how long this transition/morph will take
1255 		mShapeTransTime	= EgOSUtils::Rnd( mTransitionLo * 1000, mTransitionHi * 1000 );
1256 		mShapeTransEnd	= mT_MS + mShapeTransTime;
1257 	}
1258 
1259 }
1260 
1261 
1262 
1263 
1264 
1265 
loadParticle(long inParticleNum)1266 void GForce::loadParticle( long inParticleNum ) {
1267 	const CEgFileSpec* spec;
1268 	int ok = false, vers;
1269 	ArgList args;
1270 	ParticleGroup* newParticle;
1271 	UtilStr name;
1272 
1273 	// Fetch the spec for our config file or folder
1274 	spec = mParticles.FetchSpec( inParticleNum );
1275 
1276 	if ( spec ) {
1277 
1278 		mCurParticleNum = inParticleNum;
1279 		ok = ConfigFile::Load( spec, args );
1280 		if ( ok ) {
1281 			vers = args.GetArg( 'Vers' );
1282 			ok = vers >= 100 && vers < 110;
1283 
1284 			spec -> GetFileName( name );
1285 
1286 			// If the pref says so, display that we're loading a new config
1287 			if ( mNewConfigNotify ) {
1288 				Print( "Loaded Particle: " );
1289 				Println( &name );
1290 			}
1291 		}
1292 	}
1293 
1294 
1295 	if ( ok ) {
1296 
1297 		// Avoid having to reallocate mem...
1298 		newParticle = (ParticleGroup*) mStoppedParticlePool.GetHead();
1299 
1300 		// If there weren'y any particles already expired, make a new instance
1301 		if ( ! newParticle )
1302 			newParticle = new ParticleGroup( &mT, (ExprUserFcn**) &mSampleFcn );
1303 
1304 		// Add the new particle to the group that gets executed each frame
1305 		newParticle -> mTitle.Assign( name );
1306 		mRunningParticlePool.addToHead( newParticle );
1307 
1308 		// The GF particle probability fcn has access to these variables
1309 		mNumRunningParticles = mRunningParticlePool.shallowCount();
1310 		mLastParticleStart = mT;
1311 
1312 		// Determine how long this particle will be around
1313 		newParticle -> SetDuration( mParticleDurationFcn.Evaluate() );
1314 
1315 		// Tell the particle to compile it's config text
1316 		newParticle -> Load( args );
1317 	}
1318 }
1319 
1320 
NewSong()1321 void GForce::NewSong() {
1322 
1323 	mTrackText.Assign( mTrackMetaText );
1324 	mTrackText.Replace( "\\r", "\r" );
1325 	mTrackText.Replace( "#ARTIST#", mArtist.getCStr(), false );
1326 	mTrackText.Replace( "#ALBUM#", mAlbum.getCStr(), false );
1327 	mTrackText.Replace( "#TITLE#", mSongTitle.getCStr(), false );
1328 
1329 	CalcTrackTextPos();
1330 
1331 	mLastSongStart = mT;
1332 }
1333 
1334 
1335 
CalcTrackTextPos()1336 void GForce::CalcTrackTextPos() {
1337 	long height, width;
1338 	long x = mCurPort -> GetX();
1339 	long y = mCurPort -> GetY();
1340 
1341 	mCurPort -> TextRect( mTrackText.getCStr(), width, height );
1342 
1343 	switch ( mTrackTextPosMode ) {
1344 
1345 		case 1:		// Upper-left corner
1346 			mTrackTextPos.h = 5;
1347 			mTrackTextPos.v = mTrackTextSize + 5;
1348 			break;
1349 
1350 		case 2:		// Bottom-left corner
1351 			mTrackTextPos.h = 5;
1352 			mTrackTextPos.v = y - height - 3;
1353 			break;
1354 
1355 		case 3:		// Centered
1356 			mTrackTextPos.h = ( x - width )  / 2;
1357 			mTrackTextPos.v = ( y - height ) / 2;
1358 			break;
1359 
1360 		default:	// Random Position
1361 			mTrackTextPos.h = EgOSUtils::Rnd( 5, x - width );
1362 			mTrackTextPos.v = EgOSUtils::Rnd( mTrackTextSize + 5, y - height );
1363 			break;
1364 	}
1365 }
1366 
1367 
1368 
SpawnNewParticle()1369 void GForce::SpawnNewParticle() {
1370 	int i;
1371 
1372 	// Load the next particle in the (randomized) play list...
1373 	i = mParticlePlayList.FindIndexOf( mCurParticleNum );
1374 
1375 	// Make a new play list if we've reached the end of the list...
1376 	if ( i >= mParticlePlayList.Count() ) {
1377 		mParticlePlayList.Randomize();
1378 		i = 0;
1379 	}
1380 
1381 	// loadGradField() will initiate computation on mField with a new grad field...
1382 	loadParticle( mParticlePlayList.Fetch( i + 1 ) );
1383 }
1384 
1385 
1386 
1387 
1388 
Print(char * inStr)1389 void GForce::Print( char* inStr ) {
1390 	long num = mConsoleLines.Count();
1391 	UtilStr* lastLine = mConsoleLines.Fetch( num );
1392 
1393 	// Append the text to the console text..
1394 	if ( lastLine )
1395 		lastLine -> Append( inStr );
1396 	else {
1397 		mConsoleLines.Add( inStr );
1398 		num = 1;
1399 	}
1400 
1401 	// Setup when this line will be deleted
1402 	mLineExpireTimes[ num - 1 ] = mT_MS + mConsoleLineDur * 1000;
1403 
1404 	// Make the console visible for the next few seconds
1405 	mConsoleExpireTime = mT_MS + mConsoleDelay * 1000;
1406 }
1407 
1408 
Println(char * inStr)1409 void GForce::Println( char* inStr ) {
1410 	Print( inStr );
1411 
1412 	mConsoleLines.Add( "" );
1413 }
1414 
1415 // This is stupid!
1416 #define PIX_PER_LINE 10
1417 
DrawConsole()1418 void GForce::DrawConsole() {
1419 	long i, start, num = mConsoleLines.Count();
1420 	long x = mDispRect.left + 5;
1421 	long top = PIX_PER_LINE + 3;
1422 	UtilStr* theLine;
1423 
1424 	if ( mConsoleLines.Count() == 0 )
1425 		return;
1426 
1427 	// Delete console lines that are too old...
1428 	while ( mLineExpireTimes.Fetch( 1 ) < mT_MS && num > 0 ) {
1429 		mConsoleLines.Remove( 1 );
1430 		mLineExpireTimes.RemoveElement( 1 );
1431 		num--;
1432 	}
1433 
1434 	// Check if console runs off the display rect...
1435 	if ( num * PIX_PER_LINE > mDispRect.bottom - mDispRect.top - top)
1436 		start = num - ( mDispRect.bottom - mDispRect.top - top) / PIX_PER_LINE;
1437 	else
1438 		start = 1;
1439 
1440 	// Draw each line of the console...
1441 	for ( i = start; i <= num; i++ ) {
1442 		theLine = mConsoleLines.Fetch( i );
1443 		mCurPort -> DrawText( x, top + (i-start) * PIX_PER_LINE, *theLine );
1444 	}
1445 }
1446 
1447 
1448 
1449 
1450 
1451 
ErasePane()1452 void GForce::ErasePane() {
1453 
1454 }
1455 
1456 
1457 
SetWinPort(WindowPtr inWin,const Rect * inRect)1458 void GForce::SetWinPort( WindowPtr inWin, const Rect* inRect ) {
1459 	Rect r;
1460 
1461 	// mDoingSetPortWin == true is a signal that another thread is in SetWinPort()
1462 	if ( mDoingSetPortWin )
1463 		return;
1464 	mDoingSetPortWin = true;
1465 	mWind = inWin;
1466 
1467 	if ( inRect )
1468 		r = *inRect;
1469 
1470 	long x = r.right - r.left;
1471 	long y = r.bottom - r.top;
1472 
1473 	SetPort( 0, r, false );
1474 
1475 	// Signal that this thread is done with SetPortWin()
1476 	mDoingSetPortWin = false;
1477 }
1478 
1479 
1480 
1481 
SetPort(GrafPtr inPort,const Rect & inRect,bool inFullScreen)1482 void GForce::SetPort( GrafPtr inPort, const Rect& inRect, bool inFullScreen ) {
1483 	long x = inRect.right - inRect.left;
1484 	long y = inRect.bottom - inRect.top;
1485 
1486 	mOutPort = inPort;
1487 	mAtFullScreen = inFullScreen;
1488 
1489 
1490 	// The pane rect is the rect within inPort th plugin frame occupies
1491 	mPaneRect = inRect;
1492 
1493 	// mDispRect is the rect within inPort G-Force is drawing in (ex, the letterbox)
1494 	// Change the disp rect if the desired size exceeds the pixel ceiling
1495 	mDispRect = inRect;
1496 
1497 	// Setup the offscreen port
1498 	mPortA.Init( x, y, 8 );
1499 	mPortB.Init( x, y, 8 );
1500 	mCurPort = &mPortA;
1501 
1502 	// Erase/init our output window
1503 	mNeedsPaneErased = true;
1504 
1505 	// If setting port for the first time...
1506 	if ( mWave == 0 ) {
1507 		loadWaveShape( mShapePlayList.Fetch( 1 ), false );
1508 		loadColorMap( mColorPlayList.Fetch( 1 ), false );
1509 
1510 		// loadGradField() will initiate computation on mField with a new grad field...
1511 		loadDeltaField( mFieldPlayList.Fetch( 1 ) );
1512 		DeltaField* temp = mField;
1513 		mField = mNextField;
1514 		mNextField = temp;
1515 		loadDeltaField( mFieldPlayList.Fetch( 2 ) );
1516 	}
1517 
1518 	// The grad fields have to know the pixel dimentions
1519 	mField1.SetSize( x, y, mPortA.GetRowSize() );
1520 	mField2.SetSize( x, y, mPortA.GetRowSize() );
1521 
1522 	// The track text may depend on the port size
1523 	CalcTrackTextPos();
1524 
1525 	// Changing the port (and the resolution) may change the mouse cords
1526 	EgOSUtils::GetMouse( mLastMousePt );
1527 
1528 }
1529 
GetWinRect(Rect & outRect)1530 void GForce::GetWinRect( Rect& outRect ) {
1531 	SetRect( &outRect, 0, 0, 0, 0 );
1532 }
1533 
1534